API功能性修复
This commit is contained in:
404
frontend/admin/src-reverse/pages/user/UserManagePage.js
Normal file
404
frontend/admin/src-reverse/pages/user/UserManagePage.js
Normal file
@@ -0,0 +1,404 @@
|
||||
import { makeRecoveredPage } from "../../runtime/makeRecoveredPage.js";
|
||||
import UserManagePageView from "./UserManagePage.jsx";
|
||||
|
||||
export default makeRecoveredPage({
|
||||
title: "User Manage",
|
||||
routePath: "/user/manage",
|
||||
moduleId: "u8t",
|
||||
featureKey: "user.manage",
|
||||
component: UserManagePageView,
|
||||
translationNamespaces: ["user", "common", "order", "traffic"],
|
||||
summary:
|
||||
"The user management page is one of the heaviest admin pages in the bundle: it combines a server-side data table, advanced filter sheet, batch actions, email sending, user generation, traffic history dialog, inline order assignment, reset-secret/reset-traffic flows, and a full edit side sheet.",
|
||||
api: [
|
||||
{
|
||||
name: "user.fetch",
|
||||
method: "POST",
|
||||
path: "{secure}/user/fetch",
|
||||
queryKey: ["userList", "{pagination}", "{filters}", "{sorting}"],
|
||||
purpose: "Loads paginated user table data from server-side filters and sorting.",
|
||||
requestShape: {
|
||||
pageSize: "number",
|
||||
current: "1-based page number",
|
||||
filter: [{ id: "column id", value: "raw value or operator:value string" }],
|
||||
sort: [{ id: "column id", desc: "boolean" }],
|
||||
},
|
||||
responseShape: {
|
||||
data: [
|
||||
{
|
||||
id: "number",
|
||||
email: "string",
|
||||
is_admin: "boolean",
|
||||
is_staff: "boolean",
|
||||
banned: "boolean",
|
||||
balance: "number",
|
||||
commission_balance: "number",
|
||||
online_count: "number",
|
||||
device_limit: "number | null",
|
||||
transfer_enable: "number",
|
||||
total_used: "number",
|
||||
u: "number",
|
||||
d: "number",
|
||||
expired_at: "unix timestamp | null",
|
||||
next_reset_at: "unix timestamp | null",
|
||||
created_at: "unix timestamp",
|
||||
subscribe_url: "string",
|
||||
uuid: "string",
|
||||
token: "string",
|
||||
remarks: "string | null",
|
||||
plan: {
|
||||
id: "number",
|
||||
name: "string",
|
||||
},
|
||||
group: {
|
||||
id: "number",
|
||||
name: "string",
|
||||
},
|
||||
invite_user: {
|
||||
id: "number",
|
||||
email: "string",
|
||||
},
|
||||
invite_user_id: "number | null",
|
||||
commission_type: "number",
|
||||
commission_rate: "number | null",
|
||||
},
|
||||
],
|
||||
total: "number",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user.update",
|
||||
method: "POST",
|
||||
path: "{secure}/user/update",
|
||||
purpose: "Persists edited user fields from the edit side sheet.",
|
||||
notes: [
|
||||
"Only dirty fields are submitted beyond the required id.",
|
||||
"Upload/download fields are edited in GB in the form and converted back to bytes before submit.",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "user.resetSecret",
|
||||
method: "POST",
|
||||
path: "{secure}/user/resetSecret",
|
||||
purpose: "Resets a user's secret/token from the actions menu.",
|
||||
},
|
||||
{
|
||||
name: "user.generate",
|
||||
method: "POST",
|
||||
path: "{secure}/user/generate",
|
||||
purpose: "Bulk generates users from the toolbar dialog, optionally downloading CSV output.",
|
||||
},
|
||||
{
|
||||
name: "user.getStatUser",
|
||||
method: "POST",
|
||||
path: "{secure}/stat/getStatUser",
|
||||
purpose: "Loads per-user traffic history rows for the traffic records dialog.",
|
||||
},
|
||||
{
|
||||
name: "user.destroy",
|
||||
method: "POST",
|
||||
path: "{secure}/user/destroy",
|
||||
purpose: "Deletes a user after confirmation from the row action menu.",
|
||||
},
|
||||
{
|
||||
name: "user.sendMail",
|
||||
method: "POST",
|
||||
path: "{secure}/user/sendMail",
|
||||
purpose: "Sends email to selected users, filtered users, or all users from the send-mail dialog.",
|
||||
},
|
||||
{
|
||||
name: "user.dumpCsv",
|
||||
method: "POST",
|
||||
path: "{secure}/user/dumpCSV",
|
||||
purpose: "Exports current selection/filter scope as CSV from the toolbar menu.",
|
||||
},
|
||||
{
|
||||
name: "user.ban",
|
||||
method: "POST",
|
||||
path: "{secure}/user/ban",
|
||||
purpose: "Batch-bans users by selected rows, current filtered scope, or all users.",
|
||||
payloadExamples: [
|
||||
{ scope: "selected", user_ids: [1, 2, 3] },
|
||||
{ scope: "filtered", filter: [{ id: "email", value: "foo" }], sort: "id", sort_type: "DESC" },
|
||||
{ scope: "all" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "trafficReset.resetUser",
|
||||
method: "POST",
|
||||
path: "{secure}/traffic-reset/reset-user",
|
||||
purpose: "Resets user traffic through the row action dialog.",
|
||||
},
|
||||
{
|
||||
name: "order.assign",
|
||||
method: "POST",
|
||||
path: "{secure}/order/assign",
|
||||
purpose: "Embedded assign-order flow reused from the order page.",
|
||||
},
|
||||
{
|
||||
name: "plan.fetch",
|
||||
method: "GET",
|
||||
path: "{secure}/plan/fetch",
|
||||
purpose: "Provides subscription plan options for filters, editing, generation, and assign-order.",
|
||||
},
|
||||
{
|
||||
name: "server.group.fetch",
|
||||
method: "GET",
|
||||
path: "{secure}/server/group/fetch",
|
||||
purpose: "Provides permission group options for toolbar and edit form.",
|
||||
},
|
||||
],
|
||||
stateModel: {
|
||||
list: {
|
||||
rowSelection: {},
|
||||
columnVisibility: {
|
||||
is_admin: false,
|
||||
is_staff: false,
|
||||
},
|
||||
columnFilters: [],
|
||||
sorting: [],
|
||||
pagination: {
|
||||
pageIndex: 0,
|
||||
pageSize: 20,
|
||||
},
|
||||
initialHiddenColumns: [
|
||||
"commission_balance",
|
||||
"created_at",
|
||||
"is_admin",
|
||||
"is_staff",
|
||||
"permission_group",
|
||||
"plan_id",
|
||||
],
|
||||
initialColumnPinning: {
|
||||
right: ["actions"],
|
||||
},
|
||||
},
|
||||
routeBootstrap: {
|
||||
searchParamsToFilters: [{ key: "email", parser: "string" }],
|
||||
},
|
||||
toolbarDialogs: {
|
||||
generateUsers: false,
|
||||
sendMail: false,
|
||||
batchBanConfirm: false,
|
||||
advancedFilterSheet: false,
|
||||
},
|
||||
editSheet: {
|
||||
open: false,
|
||||
editingUser: null,
|
||||
planOptions: [],
|
||||
},
|
||||
},
|
||||
toolbarModel: {
|
||||
primaryActions: [
|
||||
"Generate users dialog",
|
||||
"Email search filter on the email column",
|
||||
"Advanced filter sheet",
|
||||
"Column visibility menu",
|
||||
"Bulk action dropdown",
|
||||
],
|
||||
advancedFilters: [
|
||||
"email",
|
||||
"id",
|
||||
"plan_id",
|
||||
"transfer_enable",
|
||||
"total_used",
|
||||
"online_count",
|
||||
"expired_at",
|
||||
"uuid",
|
||||
"token",
|
||||
"banned",
|
||||
"remarks",
|
||||
"invite_user.email",
|
||||
"invite_user_id",
|
||||
"is_admin",
|
||||
"is_staff",
|
||||
],
|
||||
bulkMenu: [
|
||||
"Send mail",
|
||||
"Export CSV",
|
||||
"Batch ban",
|
||||
],
|
||||
scopeResolution:
|
||||
"Selection scope is selected rows when any row is checked, otherwise filtered scope when filters exist, otherwise all users.",
|
||||
},
|
||||
tableModel: {
|
||||
columns: [
|
||||
{
|
||||
key: "select",
|
||||
titleKey: "columns.select_row",
|
||||
renderer: "Checkbox column with select-all checkbox in header.",
|
||||
},
|
||||
{
|
||||
key: "is_admin",
|
||||
titleKey: "columns.is_admin",
|
||||
hiddenByDefault: true,
|
||||
filterBehavior: "Facet filter based on boolean values.",
|
||||
},
|
||||
{
|
||||
key: "is_staff",
|
||||
titleKey: "columns.is_staff",
|
||||
hiddenByDefault: true,
|
||||
filterBehavior: "Facet filter based on boolean values.",
|
||||
},
|
||||
{
|
||||
key: "id",
|
||||
titleKey: "columns.id",
|
||||
renderer: "Outline badge",
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
key: "email",
|
||||
titleKey: "columns.email",
|
||||
renderer:
|
||||
"Composite identity cell rendered by a dedicated user summary component, including email, UUID/token-related context, and online/offline indicators.",
|
||||
},
|
||||
{
|
||||
key: "online_count",
|
||||
titleKey: "columns.online_count",
|
||||
renderer:
|
||||
"Online devices badge showing current online_count against device_limit, with tooltip for unlimited vs limited semantics.",
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
key: "banned",
|
||||
titleKey: "columns.status",
|
||||
renderer: "Status badge for banned vs normal.",
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
key: "plan_id",
|
||||
titleKey: "columns.subscription",
|
||||
renderer: "Nested plan.name text",
|
||||
},
|
||||
{
|
||||
key: "group_id",
|
||||
titleKey: "columns.group",
|
||||
renderer: "Nested group.name badge",
|
||||
},
|
||||
{
|
||||
key: "total_used",
|
||||
titleKey: "columns.used_traffic",
|
||||
renderer:
|
||||
"Usage progress bar with percentage and tooltip containing total traffic.",
|
||||
},
|
||||
{
|
||||
key: "transfer_enable",
|
||||
titleKey: "columns.total_traffic",
|
||||
renderer: "Formatted total traffic text",
|
||||
},
|
||||
{
|
||||
key: "expired_at",
|
||||
titleKey: "columns.expire_time",
|
||||
renderer:
|
||||
"Status badge for permanent/remaining/expired with tooltip exposing full time, day delta, and next_reset_at when present.",
|
||||
},
|
||||
{
|
||||
key: "balance",
|
||||
titleKey: "columns.balance",
|
||||
renderer: "Currency value",
|
||||
},
|
||||
{
|
||||
key: "commission_balance",
|
||||
titleKey: "columns.commission",
|
||||
renderer: "Currency value",
|
||||
},
|
||||
{
|
||||
key: "created_at",
|
||||
titleKey: "columns.register_time",
|
||||
renderer: "Registration time text",
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
titleKey: "columns.actions",
|
||||
renderer:
|
||||
"Dropdown menu containing edit, assign order, copy subscribe URL, reset secret, orders link, invites filter shortcut, traffic records dialog, reset traffic dialog, and delete confirmation.",
|
||||
},
|
||||
],
|
||||
mobileLayout: {
|
||||
primaryField: "email",
|
||||
gridFields: ["online_count", "transfer_enable", "used_traffic", "expired_at"],
|
||||
},
|
||||
},
|
||||
dialogs: {
|
||||
generateUsers: {
|
||||
formFields: [
|
||||
"email_prefix",
|
||||
"email_suffix",
|
||||
"password",
|
||||
"expired_at",
|
||||
"plan_id",
|
||||
"generate_count",
|
||||
"download_csv",
|
||||
],
|
||||
behavior: [
|
||||
"If generate_count is empty, email_prefix becomes required.",
|
||||
"If download_csv is enabled and generate_count is used, the response is handled as a Blob download.",
|
||||
],
|
||||
},
|
||||
sendMail: {
|
||||
scopes: ["selected", "filtered", "all"],
|
||||
fields: ["subject", "content"],
|
||||
templateSupport:
|
||||
"Supports placeholder variables such as {{app.name}}, {{user.email}}, {{user.plan_name}}, etc.",
|
||||
specialAction: "Apply system notice template button fills subject and content from built-in defaults.",
|
||||
},
|
||||
editUser: {
|
||||
fields: [
|
||||
"email",
|
||||
"invite_user_email",
|
||||
"password",
|
||||
"balance",
|
||||
"commission_balance",
|
||||
"u",
|
||||
"d",
|
||||
"transfer_enable",
|
||||
"expired_at",
|
||||
"plan_id",
|
||||
"banned",
|
||||
"commission_type",
|
||||
"commission_rate",
|
||||
"discount",
|
||||
"speed_limit",
|
||||
"device_limit",
|
||||
"is_admin",
|
||||
"is_staff",
|
||||
"remarks",
|
||||
],
|
||||
conversions: [
|
||||
"u and d are displayed in GB with three decimals and converted back to bytes on submit.",
|
||||
"transfer_enable is edited in GB and converted back to bytes on change.",
|
||||
],
|
||||
expireTimePresets: [
|
||||
"permanent",
|
||||
"1 month",
|
||||
"3 months",
|
||||
"specific datetime",
|
||||
],
|
||||
},
|
||||
trafficRecords: {
|
||||
columns: ["record_at", "u", "d", "server_rate", "total"],
|
||||
purpose: "Shows paginated traffic usage history for a single user.",
|
||||
},
|
||||
},
|
||||
interactions: [
|
||||
"If the route opens with ?email=... the page seeds that value into the email column filter automatically.",
|
||||
"Export CSV and batch ban both resolve their target scope from selected rows first, then filtered data, then all users.",
|
||||
"Invites action does not navigate away; it rewrites current columnFilters to invite_user_id=eq:<current user id>.",
|
||||
"Orders action deep-links into /finance/order?user_id=eq:<current user id>.",
|
||||
"Copy URL action copies subscribe_url directly from the current row.",
|
||||
"Reset secret, reset traffic, and delete all refetch the list after success.",
|
||||
],
|
||||
notes: [
|
||||
"The page uses a provider to share edit-sheet state and refreshData across the table and side sheet.",
|
||||
"Advanced filters are composed into table column filters, using operator prefixes such as eq:, gt:, lt:, and raw contains values.",
|
||||
"Column visibility is user-controlled through a generic shared table view-options dropdown.",
|
||||
],
|
||||
sourceRefs: [
|
||||
"frontend/admin/reverse/output/index-CO3BwsT2.pretty.js:27389",
|
||||
"frontend/admin/reverse/output/index-CO3BwsT2.pretty.js:300842",
|
||||
"frontend/admin/reverse/output/index-CO3BwsT2.pretty.js:301426",
|
||||
"frontend/admin/reverse/output/index-CO3BwsT2.pretty.js:303321",
|
||||
"frontend/admin/reverse/output/index-CO3BwsT2.pretty.js:303835",
|
||||
"frontend/admin/reverse/output/index-CO3BwsT2.pretty.js:304646",
|
||||
],
|
||||
});
|
||||
Reference in New Issue
Block a user