浏览代码

refactor(inbounds): remove column sorter from inbound list

Drop the table header sorter on the inbounds page: the sortKey/sortOrder
state, the sortedInbounds memo and onChange handler, the per-column
sorterFor spreads, the SORT_FNS comparator map, and the now-unused
SortKey/SortOrder types. The list renders in DB order.
MHSanaei 16 小时之前
父节点
当前提交
61e8bed3e0

+ 9 - 28
frontend/src/pages/inbounds/list/InboundList.tsx

@@ -25,11 +25,10 @@ import {
 
 import { HttpUtil } from '@/utils';
 
-import { SORT_FNS } from './helpers';
 import { buildRowActionsMenu } from './RowActions';
 import { useInboundColumns } from './useInboundColumns';
 import InboundStatsModal from './InboundStatsModal';
-import type { DBInboundRecord, GeneralAction, InboundListProps, RowAction, SortKey, SortOrder } from './types';
+import type { DBInboundRecord, GeneralAction, InboundListProps, RowAction } from './types';
 import './InboundList.css';
 
 export default function InboundList({
@@ -49,8 +48,6 @@ export default function InboundList({
   onBulkDelete,
 }: InboundListProps) {
   const { t } = useTranslation();
-  const [sortKey, setSortKey] = useState<SortKey | null>(null);
-  const [sortOrder, setSortOrder] = useState<SortOrder>(null);
   const [statsRecord, setStatsRecord] = useState<DBInboundRecord | null>(null);
   const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]);
 
@@ -67,14 +64,6 @@ export default function InboundList({
     }
   }, []);
 
-  const sortedInbounds = useMemo(() => {
-    if (!sortKey || !sortOrder) return dbInbounds;
-    const fn = SORT_FNS[sortKey];
-    if (!fn) return dbInbounds;
-    const sorted = [...dbInbounds].sort((a, b) => fn(a, b, { nodesById, clientCount }));
-    return sortOrder === 'descend' ? sorted.reverse() : sorted;
-  }, [dbInbounds, sortKey, sortOrder, nodesById, clientCount]);
-
   const hasAnyRemark = useMemo(
     () => dbInbounds.some((i) => typeof i.remark === 'string' && i.remark.trim() !== ''),
     [dbInbounds],
@@ -89,11 +78,11 @@ export default function InboundList({
   }, []);
 
   const selectAll = useCallback((checked: boolean) => {
-    setSelectedRowKeys(checked ? sortedInbounds.map((i) => i.id) : []);
-  }, [sortedInbounds]);
+    setSelectedRowKeys(checked ? dbInbounds.map((i) => i.id) : []);
+  }, [dbInbounds]);
 
-  const allSelected = sortedInbounds.length > 0 && selectedRowKeys.length === sortedInbounds.length;
-  const someSelected = selectedRowKeys.length > 0 && selectedRowKeys.length < sortedInbounds.length;
+  const allSelected = dbInbounds.length > 0 && selectedRowKeys.length === dbInbounds.length;
+  const someSelected = selectedRowKeys.length > 0 && selectedRowKeys.length < dbInbounds.length;
 
   const handleBulkDelete = useCallback(async () => {
     const ok = await onBulkDelete(selectedRowKeys);
@@ -108,8 +97,6 @@ export default function InboundList({
     subEnable,
     expireDiff,
     trafficDiff,
-    sortKey,
-    sortOrder,
     onRowAction,
     onSwitchEnable,
   });
@@ -160,7 +147,7 @@ export default function InboundList({
       <Space orientation="vertical" style={{ width: '100%' }}>
         {isMobile ? (
           <div className="inbound-cards">
-            {sortedInbounds.length === 0 ? (
+            {dbInbounds.length === 0 ? (
               <div className="card-empty">
                 <ImportOutlined style={{ fontSize: 28, opacity: 0.5 }} />
                 <div>{t('noData')}</div>
@@ -179,7 +166,7 @@ export default function InboundList({
                   <span className="bulk-count">{selectedRowKeys.length}</span>
                 )}
               </div>
-              {sortedInbounds.map((record) => (
+              {dbInbounds.map((record) => (
                 <div key={record.id} className={`inbound-card${selectedRowKeys.includes(record.id) ? ' is-selected' : ''}`}>
                   <div className="card-head">
                     <Checkbox
@@ -217,13 +204,13 @@ export default function InboundList({
         ) : (
           <Table
             columns={columns}
-            dataSource={sortedInbounds}
+            dataSource={dbInbounds}
             rowKey={(r) => r.id}
             rowSelection={{
               selectedRowKeys,
               onChange: (keys: Key[]) => setSelectedRowKeys(keys as number[]),
             }}
-            pagination={paginationFor(sortedInbounds)}
+            pagination={paginationFor(dbInbounds)}
             scroll={{ x: 1000 }}
             style={{ marginTop: 10 }}
             size="small"
@@ -235,12 +222,6 @@ export default function InboundList({
                 </div>
               ),
             }}
-            onChange={(_p, _f, sorter) => {
-              const single = Array.isArray(sorter) ? sorter[0] : sorter;
-              const colKey = (single?.columnKey || single?.field) as SortKey | undefined;
-              setSortKey(colKey || null);
-              setSortOrder((single?.order as SortOrder) || null);
-            }}
           />
         )}
       </Space>

+ 1 - 18
frontend/src/pages/inbounds/list/helpers.ts

@@ -1,8 +1,7 @@
-import type { NodeRecord } from '@/api/queries/useNodesQuery';
 import { isSSMultiUser } from '@/lib/xray/protocol-capabilities';
 import { coerceInboundJsonField } from '@/models/dbinbound';
 
-import type { ClientCountEntry, DBInboundRecord, SortKey, StreamHints } from './types';
+import type { DBInboundRecord, StreamHints } from './types';
 
 export function readStreamHints(streamSettings: unknown): StreamHints {
   const stream = coerceInboundJsonField(streamSettings) as { network?: string; security?: string };
@@ -88,19 +87,3 @@ export function showQrCodeMenu(dbInbound: DBInboundRecord): boolean {
   }
   return false;
 }
-
-export const SORT_FNS: Record<SortKey, (a: DBInboundRecord, b: DBInboundRecord, ctx: { nodesById: Map<number, NodeRecord>; clientCount: Record<number, ClientCountEntry> }) => number> = {
-  id: (a, b) => a.id - b.id,
-  enable: (a, b) => Number(a.enable) - Number(b.enable),
-  remark: (a, b) => (a.remark || '').localeCompare(b.remark || ''),
-  port: (a, b) => a.port - b.port,
-  protocol: (a, b) => a.protocol.localeCompare(b.protocol),
-  traffic: (a, b) => (a.up + a.down) - (b.up + b.down),
-  expiryTime: (a, b) => (a.expiryTime || Infinity) - (b.expiryTime || Infinity),
-  node: (a, b, ctx) => {
-    const nameA = ctx.nodesById.get(a.nodeId ?? -1)?.name ?? (a.nodeId == null ? '￿' : `node #${a.nodeId}`);
-    const nameB = ctx.nodesById.get(b.nodeId ?? -1)?.name ?? (b.nodeId == null ? '￿' : `node #${b.nodeId}`);
-    return nameA.localeCompare(nameB);
-  },
-  clients: (a, b, ctx) => (ctx.clientCount[a.id]?.clients || 0) - (ctx.clientCount[b.id]?.clients || 0),
-};

+ 0 - 13
frontend/src/pages/inbounds/list/types.ts

@@ -74,16 +74,3 @@ export interface InboundListProps {
   onRowAction: (action: { key: RowAction; dbInbound: DBInboundRecord }) => void;
   onBulkDelete: (ids: number[]) => Promise<boolean>;
 }
-
-export type SortKey =
-  | 'id'
-  | 'enable'
-  | 'remark'
-  | 'port'
-  | 'protocol'
-  | 'traffic'
-  | 'expiryTime'
-  | 'node'
-  | 'clients';
-
-export type SortOrder = 'ascend' | 'descend' | null;

+ 3 - 23
frontend/src/pages/inbounds/list/useInboundColumns.tsx

@@ -1,4 +1,4 @@
-import { useCallback, useMemo, type ReactElement } from 'react';
+import { useMemo, type ReactElement } from 'react';
 import { useTranslation } from 'react-i18next';
 import { Popover, Switch, Tag, type TableColumnType } from 'antd';
 
@@ -16,7 +16,7 @@ import {
   tunnelNetworkLabel,
   mixedNetworkLabel,
 } from './helpers';
-import type { ClientCountEntry, DBInboundRecord, RowAction, SortKey, SortOrder } from './types';
+import type { ClientCountEntry, DBInboundRecord, RowAction } from './types';
 
 interface UseInboundColumnsParams {
   hasAnyRemark: boolean;
@@ -26,8 +26,6 @@ interface UseInboundColumnsParams {
   subEnable: boolean;
   expireDiff: number;
   trafficDiff: number;
-  sortKey: SortKey | null;
-  sortOrder: SortOrder;
   onRowAction: (action: { key: RowAction; dbInbound: DBInboundRecord }) => void;
   onSwitchEnable: (dbInbound: DBInboundRecord, next: boolean) => void;
 }
@@ -40,21 +38,12 @@ export function useInboundColumns({
   subEnable,
   expireDiff,
   trafficDiff,
-  sortKey,
-  sortOrder,
   onRowAction,
   onSwitchEnable,
 }: UseInboundColumnsParams): TableColumnType<DBInboundRecord>[] {
   const { t } = useTranslation();
   const { datepicker } = useDatepicker();
 
-  const sorterFor = useCallback((key: SortKey) => ({
-    sorter: true as const,
-    showSorterTooltip: false,
-    sortOrder: sortKey === key ? sortOrder : null,
-    sortDirections: ['ascend' as const, 'descend' as const],
-  }), [sortKey, sortOrder]);
-
   return useMemo(() => {
     const cols: TableColumnType<DBInboundRecord>[] = [
       {
@@ -63,7 +52,6 @@ export function useInboundColumns({
         key: 'id',
         align: 'right',
         width: 30,
-        ...sorterFor('id'),
       },
       {
         title: t('pages.inbounds.operate'),
@@ -84,7 +72,6 @@ export function useInboundColumns({
         key: 'enable',
         align: 'center',
         width: 35,
-        ...sorterFor('enable'),
         render: (_, record) => (
           <Switch
             checked={record.enable}
@@ -101,7 +88,6 @@ export function useInboundColumns({
         key: 'remark',
         align: 'center',
         width: 60,
-        ...sorterFor('remark'),
       });
     }
 
@@ -111,7 +97,6 @@ export function useInboundColumns({
         key: 'node',
         align: 'center',
         width: 60,
-        ...sorterFor('node'),
         render: (_, record) => {
           if (record.nodeId == null) {
             return <Tag color="default">{t('pages.inbounds.localPanel')}</Tag>;
@@ -134,14 +119,12 @@ export function useInboundColumns({
         key: 'port',
         align: 'center',
         width: 40,
-        ...sorterFor('port'),
       },
       {
         title: t('pages.inbounds.protocol'),
         key: 'protocol',
         align: 'left',
         width: 130,
-        ...sorterFor('protocol'),
         render: (_, record) => {
           const tags: ReactElement[] = [<Tag key="p" color="purple">{record.protocol}</Tag>];
           if (record.isWireguard || record.isHysteria) {
@@ -170,7 +153,6 @@ export function useInboundColumns({
         key: 'clients',
         align: 'left',
         width: 50,
-        ...sorterFor('clients'),
         render: (_, record) => {
           const cc = clientCount[record.id];
           if (!cc) return null;
@@ -236,7 +218,6 @@ export function useInboundColumns({
         key: 'traffic',
         align: 'center',
         width: 90,
-        ...sorterFor('traffic'),
         render: (_, record) => (
           <Popover
             content={(
@@ -269,7 +250,6 @@ export function useInboundColumns({
         key: 'expiryTime',
         align: 'center',
         width: 40,
-        ...sorterFor('expiryTime'),
         render: (_, record) => {
           if (record.expiryTime > 0) {
             return (
@@ -286,5 +266,5 @@ export function useInboundColumns({
     );
 
     return cols;
-  }, [t, hasAnyRemark, hasActiveNode, nodesById, clientCount, subEnable, expireDiff, trafficDiff, datepicker, onRowAction, onSwitchEnable, sorterFor]);
+  }, [t, hasAnyRemark, hasActiveNode, nodesById, clientCount, subEnable, expireDiff, trafficDiff, datepicker, onRowAction, onSwitchEnable]);
 }