本地开发换成真订阅;闪电挪到卡片上;忙碌状态改成按卡片

占位线路删了。它们指向 127.0.0.1:1080,从来没测通过,而 URLTest 只在你选的那条
「对测试地址是活的」时才认账(urltest.go:106-111)——所以点了立刻被切回去,
一个正常行为看起来像 bug。现在本地内核直接用真订阅,通过隧道拉。

订阅地址带访问令牌,不能进 git:dev/config.yaml 变成模板,dev:reset 从 .env 取值生成
dev/home/config.yaml。顺带确认内核认 base64 分享链接格式(provider.go:384-390 回退到
ConvertsV2Ray),所以 Sub-Store 的输出直接能用。

闪电从组标题挪到每张卡的左下角,也就是延迟显示的位置:测过显示毫秒数,没测过显示闪电,
两种都能点,点了测这一条。忙碌状态从「禁用整个界面」改成只有正在提交的那张卡显示虚线边框。
This commit is contained in:
im0o
2026-08-10 13:21:27 +08:00
parent 5cdc183190
commit e8b8181ef6
7 changed files with 429 additions and 361 deletions
+23 -19
View File
@@ -35,7 +35,10 @@ const done = ref('')
const fields = ref<ListenerFields | null>(null)
const formOpen = ref(false)
const formEditing = ref<Inbound | null>(null)
const testing = ref(false)
/** 正在提交的那个出站的名字。只有它显示忙碌,其余卡片照常能点 */
const pendingOutbound = ref<string | null>(null)
/** 正在测延迟的线路 */
const testing = ref<string[]>([])
onMounted(async () => {
try {
@@ -101,7 +104,8 @@ async function commit(next: ConfigObject, success: string) {
async function pickOutbound(mode: OutboundMode) {
const target = selected.value
if (!target || target.listenerIndex === null || saving.value) return
if (!target || target.listenerIndex === null || pendingOutbound.value) return
pendingOutbound.value = outboundLabel(mode).text
const next = clone()
const entry = (next.listeners as ConfigObject[])[target.listenerIndex]!
@@ -110,7 +114,11 @@ async function pickOutbound(mode: OutboundMode) {
if (proxy === undefined) delete entry.proxy
else entry.proxy = proxy
await commit(next, `${target.name} 的出站改成了 ${outboundLabel(mode).text}`)
try {
await commit(next, `${target.name} 的出站改成了 ${outboundLabel(mode).text}`)
} finally {
pendingOutbound.value = null
}
}
function openAdd() {
@@ -155,24 +163,20 @@ async function selectMember(group: string, member: string) {
}
}
/** 让内核并发测一个策略组里所有成员的延迟,测完刷新一次拿到新数字。 */
async function testGroup(group: string) {
if (testing.value) return
testing.value = true
failure.value = null
done.value = ''
/** 测一条线路的延迟。纯读操作,不影响任何配置,所以谁都不用禁用。 */
async function testLine(name: string) {
if (testing.value.includes(name)) return
testing.value = [...testing.value, name]
try {
const result = await api.testGroup(group)
const ok = Object.values(result).filter((ms) => ms > 0).length
done.value = `${group} 测完了,${ok} / ${Object.keys(result).length} 条通`
await api.testLine(name)
emit('changed')
} catch (error) {
// 测不通是关于线路的结论,不是 Patchbay 出了问题,所以用中性提示而不是错误框。
// 内核在全部超时时回 504,单条测失败回 503,两种都属于「测出来了,结果不好」。
const raw = error instanceof ApiError ? String(error.payload.message ?? error.message) : String(error)
done.value = raw.includes('timeout') ? `${group} 里的线路全部超时` : `测试没能完成:${raw}`
// 测不通是关于线路的结论,不是 Patchbay 出了问题,不弹错误框
const raw =
error instanceof ApiError ? String(error.payload.message ?? error.message) : String(error)
done.value = raw.includes('timeout') ? `${name} 超时` : `${name} 测不通`
} finally {
testing.value = false
testing.value = testing.value.filter((n) => n !== name)
}
}
@@ -252,13 +256,13 @@ async function removeSelected() {
:destination="resolveDestination(data, selected)"
:runtime="runtime"
:kernel-online="kernelOnline"
:saving="saving"
@pick="pickOutbound"
@edit="openEdit"
@remove="removeSelected"
@select="selectMember"
@test="testGroup"
@test="testLine"
:testing="testing"
:pending-outbound="pendingOutbound"
/>
</div>