路由页改成主从布局,出站可以真的改了

右侧详情面板取代原来的手风琴展开:空间用起来了,选中状态更清楚,行也不会被推来推去。
出站选择做成分组的按钮网格而不是下拉框——一眼看得到全部可选项和当前状态。

点一下就走完整的落盘链路(校验、备份、写入、重载),失败时按停在哪一步显示不同的话。
订阅线路那一档暂时禁用:选中它需要先自动建一个筛选组,那部分还没做。

顺带两处从截图里看出来的问题:连接线原本是固定 40px 的短横,读起来像装饰不像连接,
改成占据弹性列真的把两头连起来;默认入站的名字原本是「HTTP 和 SOCKS5 共用」,
和类型列重复,改成它来自的配置键 mixed-port。
This commit is contained in:
im0o
2026-08-10 11:55:53 +08:00
parent 1fc052028e
commit 206e14c7e7
6 changed files with 618 additions and 321 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ describe('合并两套写法的入站', () => {
'mixed-port': 7777,
listeners: [{ name: '容器A', type: 'mixed', port: 7778, proxy: 'AUTO' }],
})
expect(list.map((i) => i.name)).toEqual(['HTTP 和 SOCKS5 共用', '容器A'])
expect(list.map((i) => i.name)).toEqual(['mixed-port', '容器A'])
expect(list[0]!.isDefault).toBe(true)
expect(list[1]!.isDefault).toBe(false)
expect(list[1]!.listenerIndex).toBe(0)
+10 -8
View File
@@ -23,12 +23,12 @@ export const BUILTIN_OUTBOUNDS = [
] as const
/** 旧写法的顶层端口字段,以及它们各自等价于哪种入站类型。 */
const LEGACY_PORTS: Array<{ key: string; type: string; label: string }> = [
{ key: 'mixed-port', type: 'mixed', label: 'HTTP 和 SOCKS5 共用' },
{ key: 'port', type: 'http', label: 'HTTP' },
{ key: 'socks-port', type: 'socks', label: 'SOCKS5' },
{ key: 'redir-port', type: 'redir', label: '透明代理(REDIRECT)' },
{ key: 'tproxy-port', type: 'tproxy', label: '透明代理(TPROXY)' },
const LEGACY_PORTS: Array<{ key: string; type: string }> = [
{ key: 'mixed-port', type: 'mixed' },
{ key: 'port', type: 'http' },
{ key: 'socks-port', type: 'socks' },
{ key: 'redir-port', type: 'redir' },
{ key: 'tproxy-port', type: 'tproxy' },
]
/** 一路入站的出站去向。四选一,对应界面上那个下拉框。 */
@@ -77,14 +77,16 @@ export function outboundToProxyField(mode: OutboundMode): string | undefined {
export function collectInbounds(data: ConfigObject): Inbound[] {
const result: Inbound[] = []
for (const { key, type, label } of LEGACY_PORTS) {
for (const { key, type } of LEGACY_PORTS) {
const raw = data[key]
const port = typeof raw === 'number' ? raw : Number(raw)
// 0 和缺省都表示没启用这个端口
if (!Number.isFinite(port) || port <= 0) continue
result.push({
id: `legacy:${key}`,
name: label,
// 名字直接用它来自的那个配置键。比「HTTP 和 SOCKS5 共用」这种描述好:
// 类型那一列已经写着协议了,名字再说一遍是重复,而配置键能直接对上文件里的行。
name: key,
type,
port: String(port),
listen: null,