feat(sub): per-inbound sort order for subscription links

Add a subSortIndex field to inbounds that controls the order of links
in subscription output only: the raw sub body, the HTML sub page, and
the JSON/Clash formats (all served from the same query). Lower values
come first; ties keep id order. The panel inbound list is unaffected.

The value is editable in the inbound form next to the share-address
fields, propagates to nodes via wireInbound, and follows the usual
node-sync rules (copied on import, mirrored while not dirty, never a
structural change).

Rescoped from #5214 by @Ponywka.
This commit is contained in:
MHSanaei
2026-06-12 12:03:22 +02:00
parent 7ae3ea66d1
commit f1a4286e2f
36 changed files with 367 additions and 4 deletions
@@ -575,6 +575,14 @@ export default function InboundFormModal({
</Form.Item>
)}
<Form.Item
name="subSortIndex"
label={t('pages.inbounds.form.subSortIndex')}
extra={t('pages.inbounds.form.subSortIndexHelp')}
>
<InputNumber min={1} />
</Form.Item>
<Form.Item
name="port"
label={t('pages.inbounds.port')}
@@ -93,6 +93,11 @@ export default function InboundList({
[dbInbounds],
);
const hasAnySubSortIndex = useMemo(
() => dbInbounds.some((i) => (i.subSortIndex ?? 1) > 1),
[dbInbounds],
);
const toggleSelect = useCallback((id: number, checked: boolean) => {
setSelectedRowKeys((prev) => {
const next = new Set(prev);
@@ -115,6 +120,7 @@ export default function InboundList({
const columns = useInboundColumns({
hasAnyRemark,
hasAnySubSortIndex,
hasActiveNode,
nodesById,
clientCount,
@@ -22,6 +22,7 @@ export interface DBInboundRecord extends ProtocolFlags {
id: number;
enable: boolean;
remark: string;
subSortIndex: number;
port: number;
protocol: string;
up: number;
@@ -1,6 +1,6 @@
import { useMemo, type ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
import { Popover, Switch, Tag, type TableColumnType } from 'antd';
import { Popover, Switch, Tag, Tooltip, type TableColumnType } from 'antd';
import { TeamOutlined } from '@ant-design/icons';
import { SizeFormatter, IntlUtil, ColorUtils } from '@/utils';
@@ -21,6 +21,7 @@ import type { ClientCountEntry, DBInboundRecord, RowAction } from './types';
interface UseInboundColumnsParams {
hasAnyRemark: boolean;
hasAnySubSortIndex: boolean;
hasActiveNode: boolean;
nodesById: Map<number, NodeRecord>;
clientCount: Record<number, ClientCountEntry>;
@@ -33,6 +34,7 @@ interface UseInboundColumnsParams {
export function useInboundColumns({
hasAnyRemark,
hasAnySubSortIndex,
hasActiveNode,
nodesById,
clientCount,
@@ -113,6 +115,20 @@ export function useInboundColumns({
});
}
if (hasAnySubSortIndex) {
cols.push({
title: (
<Tooltip title={t('pages.inbounds.form.subSortIndex')}>
{t('pages.inbounds.subSortIndex')}
</Tooltip>
),
dataIndex: 'subSortIndex',
key: 'subSortIndex',
align: 'right',
width: 70,
});
}
cols.push(
{
title: t('pages.inbounds.port'),
@@ -267,5 +283,5 @@ export function useInboundColumns({
);
return cols;
}, [t, hasAnyRemark, hasActiveNode, nodesById, clientCount, subEnable, expireDiff, trafficDiff, datepicker, onRowAction, onSwitchEnable]);
}, [t, hasAnyRemark, hasAnySubSortIndex, hasActiveNode, nodesById, clientCount, subEnable, expireDiff, trafficDiff, datepicker, onRowAction, onSwitchEnable]);
}