界面文案改成中性措辞;延迟测试报出真实原因
原来那句「X 测不通」把两种要查不同地方的情况混在了一起。内核的语义是精确的 (hub/route/proxies.go:122-145):504 表示在给定超时内没有完成,503 表示连接建立失败 或测得延迟为 0。现在分开报,超时会写明是几秒,503 且内核没给具体错误时说明可能是 线路失效或本机到该服务器网络不通。 其余文案统一过一遍:「本机之外也能连」「这个名字已经被占用了」「还没做」这类口语改成 「允许来自其它主机的连接」「名称已存在」「尚未实现」。
This commit is contained in:
@@ -83,13 +83,13 @@ function isCurrentOutbound(mode: OutboundMode): boolean {
|
||||
}
|
||||
|
||||
const BUILTINS: Array<{ mode: OutboundMode; label: string; sub: string }> = [
|
||||
{ mode: { kind: 'rules' }, label: 'RULE', sub: '走分流规则' },
|
||||
{ mode: { kind: 'direct' }, label: 'DIRECT', sub: '不经过代理' },
|
||||
{ mode: { kind: 'global' }, label: 'GLOBAL', sub: '跟随全局选择' },
|
||||
{ mode: { kind: 'rules' }, label: 'RULE', sub: '由规则决定' },
|
||||
{ mode: { kind: 'direct' }, label: 'DIRECT', sub: '直接连接' },
|
||||
{ mode: { kind: 'global' }, label: 'GLOBAL', sub: '跟随 GLOBAL 组' },
|
||||
]
|
||||
|
||||
const LINE_LOCKED = '订阅线路不能被入站直接引用,选中它需要自动建一个筛选组——这部分还没做'
|
||||
const NO_PROXY_FIELD = '这路入站没有 proxy 字段'
|
||||
const LINE_LOCKED = '订阅线路无法被入站直接引用,需要通过筛选组间接引入。该功能尚未实现。'
|
||||
const NO_PROXY_FIELD = '该入站没有 proxy 字段'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -126,7 +126,7 @@ const NO_PROXY_FIELD = '这路入站没有 proxy 字段'
|
||||
</p>
|
||||
|
||||
<template v-if="switchable">
|
||||
<h4>{{ switchable.group }} 当前选中</h4>
|
||||
<h4>{{ switchable.group }} 的当前选中</h4>
|
||||
<div class="grid">
|
||||
<OutboundCard
|
||||
v-for="m in switchable.members"
|
||||
@@ -148,7 +148,7 @@ const NO_PROXY_FIELD = '这路入站没有 proxy 字段'
|
||||
<section class="block">
|
||||
<h3>出站</h3>
|
||||
<p v-if="locked" class="hint">
|
||||
由 <code>{{ inbound.legacyKey }}</code> 声明,这种写法没有 proxy 字段,固定走 RULE
|
||||
由 <code>{{ inbound.legacyKey }}</code> 声明。该写法没有 proxy 字段,出站固定为 RULE。
|
||||
</p>
|
||||
|
||||
<div v-if="!locked" class="grid">
|
||||
@@ -207,9 +207,9 @@ const NO_PROXY_FIELD = '这路入站没有 proxy 字段'
|
||||
<h4 :class="locked && 'is-dim'">订阅线路</h4>
|
||||
<p v-if="!kernelOnline" class="hint">
|
||||
<IconOffline />
|
||||
内核未连接,看不到订阅里有哪些线路
|
||||
内核未连接,无法获取订阅线路
|
||||
</p>
|
||||
<p v-else-if="providerLines.length === 0" class="hint">订阅里没有线路</p>
|
||||
<p v-else-if="providerLines.length === 0" class="hint">订阅中没有线路</p>
|
||||
<div v-else class="grid">
|
||||
<OutboundCard
|
||||
v-for="line in providerLines"
|
||||
|
||||
@@ -129,10 +129,10 @@ function submit() {
|
||||
|
||||
<div class="body">
|
||||
<label class="row">
|
||||
<span class="k">名字</span>
|
||||
<span class="k">名称</span>
|
||||
<span class="v">
|
||||
<input v-model="name" type="text" placeholder="容器A" autocomplete="off" />
|
||||
<span v-if="nameTaken" class="bad">这个名字已经被占用了</span>
|
||||
<span v-if="nameTaken" class="bad">名称已存在</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
@@ -155,7 +155,7 @@ function submit() {
|
||||
<span class="k">端口</span>
|
||||
<span class="v">
|
||||
<input v-model="port" class="mono" type="text" placeholder="7778" autocomplete="off" />
|
||||
<span v-if="portInvalid" class="bad">支持 7778、200,302、401-429 这几种写法</span>
|
||||
<span v-if="portInvalid" class="bad">支持单个端口、逗号分隔的多个端口、连字符表示的区间</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
@@ -163,7 +163,7 @@ function submit() {
|
||||
<span class="k">监听</span>
|
||||
<span class="v">
|
||||
<input v-model="listen" class="mono" type="text" placeholder="0.0.0.0" autocomplete="off" />
|
||||
<span class="note">留空等于 0.0.0.0,本机之外也能连</span>
|
||||
<span class="note">留空为 0.0.0.0,允许来自其它主机的连接</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
@@ -171,7 +171,7 @@ function submit() {
|
||||
<span class="k">{{ f.key }}</span>
|
||||
<span class="v">
|
||||
<select v-if="f.enum" v-model="extras[f.key]">
|
||||
<option value="" disabled>选一个</option>
|
||||
<option value="" disabled>请选择</option>
|
||||
<option v-for="opt in f.enum" :key="String(opt)" :value="String(opt)">{{ opt }}</option>
|
||||
</select>
|
||||
<input v-else v-model="extras[f.key]" class="mono" type="text" autocomplete="off" />
|
||||
|
||||
+37
-12
@@ -115,7 +115,7 @@ async function pickOutbound(mode: OutboundMode) {
|
||||
else entry.proxy = proxy
|
||||
|
||||
try {
|
||||
await commit(next, `${target.name} 的出站改成了 ${outboundLabel(mode).text}`)
|
||||
await commit(next, `${target.name} 的出站已设为 ${outboundLabel(mode).text}`)
|
||||
} finally {
|
||||
pendingOutbound.value = null
|
||||
}
|
||||
@@ -143,7 +143,7 @@ async function submitForm(entry: ConfigObject) {
|
||||
next.listeners = listeners
|
||||
|
||||
formOpen.value = false
|
||||
await commit(next, editingIndex === null ? `新增了 ${String(entry.name)}` : '改动已保存')
|
||||
await commit(next, editingIndex === null ? `已新增 ${String(entry.name)}` : '已保存')
|
||||
if (editingIndex === null) selectedId.value = `listener:${String(listeners.length - 1)}`
|
||||
}
|
||||
|
||||
@@ -159,22 +159,47 @@ async function selectMember(group: string, member: string) {
|
||||
emit('changed')
|
||||
} catch (error) {
|
||||
const message = error instanceof ApiError ? String(error.payload.error ?? error.message) : String(error)
|
||||
failure.value = { message, hint: '这一步只改内核的运行状态,配置文件没有被动过。' }
|
||||
failure.value = { message, hint: '该操作仅修改内核运行状态,配置文件未被修改。' }
|
||||
}
|
||||
}
|
||||
|
||||
/** 测一条线路的延迟。纯读操作,不影响任何配置,所以谁都不用禁用。 */
|
||||
/** 延迟测试用的超时,毫秒。也用在失败提示里,让「超时」这个词有具体含义。 */
|
||||
const TEST_TIMEOUT_MS = 5000
|
||||
|
||||
/**
|
||||
* 把内核的延迟测试结果翻译成一句说明原因的话。
|
||||
*
|
||||
* 内核的语义是精确的(hub/route/proxies.go:122-145):
|
||||
* 504 在给定超时内没有完成
|
||||
* 503 连接建立失败,或者测得的延迟为 0
|
||||
* 笼统说一句「测不通」会把这两种情况混在一起,而它们要查的地方不一样。
|
||||
*/
|
||||
function describeTestFailure(name: string, error: unknown): string {
|
||||
if (!(error instanceof ApiError)) return `${name} 延迟测试失败:${String(error)}`
|
||||
|
||||
const seconds = String(TEST_TIMEOUT_MS / 1000)
|
||||
if (error.status === 504) return `${name} 延迟测试超时,${seconds} 秒内没有取到测试地址`
|
||||
|
||||
const detail = String(error.payload.message ?? '')
|
||||
const generic = detail === '' || detail === 'An error occurred in the delay test'
|
||||
if (error.status === 503) {
|
||||
return generic
|
||||
? `${name} 无法建立连接,线路可能已失效,或本机到该服务器的网络不通`
|
||||
: `${name} 无法建立连接:${detail}`
|
||||
}
|
||||
return `${name} 延迟测试失败(HTTP ${String(error.status)})${generic ? '' : `:${detail}`}`
|
||||
}
|
||||
|
||||
/** 测一条线路的延迟。纯读操作,不影响任何配置,所以不禁用任何东西。 */
|
||||
async function testLine(name: string) {
|
||||
if (testing.value.includes(name)) return
|
||||
testing.value = [...testing.value, name]
|
||||
try {
|
||||
await api.testLine(name)
|
||||
await api.testLine(name, undefined, TEST_TIMEOUT_MS)
|
||||
emit('changed')
|
||||
} catch (error) {
|
||||
// 测不通是关于线路的结论,不是 Patchbay 出了问题,不弹错误框
|
||||
const raw =
|
||||
error instanceof ApiError ? String(error.payload.message ?? error.message) : String(error)
|
||||
done.value = raw.includes('timeout') ? `${name} 超时` : `${name} 测不通`
|
||||
// 测不通是关于线路的结论,不是 Patchbay 出了故障,所以不用错误框
|
||||
done.value = describeTestFailure(name, error)
|
||||
} finally {
|
||||
testing.value = testing.value.filter((n) => n !== name)
|
||||
}
|
||||
@@ -183,7 +208,7 @@ async function testLine(name: string) {
|
||||
async function removeSelected() {
|
||||
const target = selected.value
|
||||
if (!target || target.listenerIndex === null || saving.value) return
|
||||
if (!confirm(`删除入站「${target.name}」?端口 ${target.port} 会立刻停止监听。`)) return
|
||||
if (!confirm(`删除入站「${target.name}」?端口 ${target.port} 将停止监听。`)) return
|
||||
|
||||
const next = clone()
|
||||
const listeners = (next.listeners as ConfigObject[]).filter((_, i) => i !== target.listenerIndex)
|
||||
@@ -192,7 +217,7 @@ async function removeSelected() {
|
||||
else next.listeners = listeners
|
||||
|
||||
selectedId.value = null
|
||||
await commit(next, `删除了 ${target.name}`)
|
||||
await commit(next, `已删除 ${target.name}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -266,7 +291,7 @@ async function removeSelected() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p v-if="inbounds.length === 0" class="empty">没有入站。mihomo 不会监听任何端口。</p>
|
||||
<p v-if="inbounds.length === 0" class="empty">没有入站,内核不会监听任何端口。</p>
|
||||
|
||||
<InboundForm
|
||||
:open="formOpen"
|
||||
|
||||
@@ -9,7 +9,7 @@ const value = ref('')
|
||||
<div class="gate">
|
||||
<form class="box" @submit.prevent="emit('submit', value)">
|
||||
<h1>Patchbay</h1>
|
||||
<p class="lead">输入访问令牌。</p>
|
||||
<p class="lead">需要访问令牌。</p>
|
||||
<input
|
||||
v-model="value"
|
||||
class="field mono"
|
||||
@@ -21,7 +21,7 @@ const value = ref('')
|
||||
/>
|
||||
<button class="go" type="submit" :disabled="!value.trim()">进入</button>
|
||||
<p class="where">
|
||||
令牌在服务端配置文件旁边的 <code>.patchbay-token</code> 里,启动时也会打印在控制台上。
|
||||
令牌位于服务端配置文件的同级目录 <code>.patchbay-token</code>,启动时也会输出到控制台。
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user