Files
im0o b1b251b897
release / release (push) Successful in 26s
legacy 入站可升级为命名入站
顶层端口字段没有 proxy 可写(listener/inbound/base.go:97),界面上
切不了订阅。升级删掉顶层键、建同端口的 listener:listen 按 allow-lan
推导后写死,全局规则里 MATCH 之前的部分复制成附加规则,来源照抄
MATCH 目标组的 use:,让流量去向尽量不变。udp 不写,命名入站解析时
默认就是开的(listener/parse.go:52)。
「默认」tag 随术语表改成 legacy,旧叫法进 avoid 列表。
2026-08-12 17:30:47 +08:00

144 lines
3.6 KiB
Vue

<script setup lang="ts">
/**
* 入站列表。每行一路入站:端口、类型、名字、它落在哪个出站上。
*
* 只管显示和选中,不碰任何数据。
*/
import { inboundTypeLabel, isUnsetOutbound, outboundLabel } from '@shared/inbounds.ts'
import { displayName, isGenerated } from '@shared/inbound-groups.ts'
import { resolveChain } from '@shared/outbound-chain.ts'
import type { Inbound } from '@shared/inbounds.ts'
import { isChainedLine, runtime } from '../runtime'
import IconLink from '~icons/ri/links-line'
defineProps<{ inbounds: Inbound[] }>()
const selected = defineModel<string | null>('selected', { required: true })
/**
* 这一行右侧显示什么。
*
* 指向 Patchbay 自动创建的代理组时,顺着「当前选中」解析到最终出口——组名和左边的
* 名字列完全重复,而组选中 AUTO 时「AUTO」也不是答案,用户要的是流量走哪条线。
* 出口经过链式代理入口时带一个标记,和详情里的流量去向对得上。
*/
function outboundOf(inbound: Inbound): { text: string; chained: boolean } {
const mode = inbound.outbound
if (mode.kind !== 'named' || !isGenerated(mode.name))
return { text: outboundLabel(mode), chained: false }
const exit = resolveChain(runtime.value, runtime.value[mode.name]?.now ?? null).at(-1)
return {
text: displayName(exit ?? '') || '未选择',
chained: exit !== undefined && isChainedLine(exit),
}
}
</script>
<template>
<ol class="rows">
<li v-for="inbound in inbounds" :key="inbound.id">
<button
type="button"
:class="['line', selected === inbound.id && 'is-on']"
@click="selected = inbound.id"
>
<span class="port mono">{{ inbound.port }}</span>
<span class="type mono">{{ inboundTypeLabel(inbound.type) }}</span>
<span class="name">
{{ inbound.name }}
<span v-if="inbound.isDefault" class="tag">legacy</span>
</span>
<span :class="['out mono', isUnsetOutbound(inbound.outbound) && 'is-muted']">
<span v-if="outboundOf(inbound).chained" class="chain" title="经过链式代理">
<IconLink />
</span>
{{ outboundOf(inbound).text }}
</span>
</button>
</li>
</ol>
</template>
<style scoped>
.rows {
list-style: none;
margin: 0;
padding: 0;
border: 1px solid var(--c-border);
border-radius: var(--r-md);
background: var(--c-surface);
overflow: hidden;
}
.rows li + li {
border-top: 1px solid var(--c-border);
}
.line {
width: 100%;
display: grid;
grid-template-columns: 4.5rem 5rem 1fr minmax(5rem, auto);
align-items: center;
gap: var(--s-3);
padding: var(--s-3) var(--s-4);
border: none;
background: none;
cursor: pointer;
text-align: left;
}
.line:hover {
background: var(--c-surface-sunken);
}
.line.is-on {
background: var(--c-accent-soft);
}
.port {
font-size: var(--t-lg);
font-variant-numeric: tabular-nums;
}
.type {
font-size: var(--t-xs);
color: var(--c-text-faint);
}
.name {
display: flex;
align-items: center;
gap: var(--s-2);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tag {
flex: none;
padding: 0 var(--s-2);
border: 1px solid var(--c-border-strong);
border-radius: 999px;
font-size: var(--t-xs);
color: var(--c-text-soft);
line-height: 1.6;
}
.out {
display: flex;
align-items: center;
justify-content: flex-end;
gap: var(--s-1);
font-size: var(--t-sm);
}
.chain {
display: flex;
color: var(--c-chain-text);
}
.out.is-muted {
color: var(--c-text-faint);
}
</style>