feat: 全协议一键安装 + 国内GitHub镜像加速 + 中英文i18n国际化
后端: - binmgr.go: 新增 detectChina() 国内/海外自动判断(ipinfo.io) - binmgr.go: 国内自动走 gh-proxy.com 镜像加速 GitHub 下载 - binmgr.go: 新增 InstallViaPackageManager() 包管理器一键安装 - binmgr.go: 新增 GetDeploySpec()/ProtocolAlias() 导出函数 - api.go: installBinary 支持全部7个协议(FRP/WireGuard/OpenVPN/IPSec/IKEv2/L2TP/StrongSwan) - api.go: 策略: GitHub下载失败 → 自动降级包管理器安装 前端: - 安装 vue-i18n@9, 支持中英文切换 - 新增 locales/zh-CN.json + en-US.json (548个翻译key, 17个模块) - 新增 i18n.js 配置, localStorage 持久化语言偏好 - Layout.vue 右上角语言切换下拉菜单 - 所有17个Vue文件中文文本转为 $t() 调用 - Element Plus 组件语言跟随切换 - TunnelWizard: 所有协议显示「一键安装」按钮(带loading) - 前端构建零错误
This commit is contained in:
1 parent
1e2f8ef24f
commit
021d012478
25 files changed
+2313
-746
No files matched your search
@@ -2,19 +2,19 @@
|
||||
<el-card class="page-card" shadow="never" v-loading="loading">
|
||||
<template #header>
|
||||
<div style="display:flex;justify-content:space-between;align-items:center">
|
||||
<b>编辑专线</b>
|
||||
<el-button @click="router.push('/tunnels')">返回列表</el-button>
|
||||
<b>{{ $t('tunnelEditor.title') }}</b>
|
||||
<el-button @click="router.push('/tunnels')">{{ $t('tunnelEditor.backToList') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-alert type="info" :closable="false" style="margin-bottom:18px"
|
||||
title="密码、PSK、Token 等敏感字段不会回显;留空表示继续使用原值。" />
|
||||
:title="$t('tunnelEditor.sensitiveFieldHint')" />
|
||||
|
||||
<el-form v-if="form" label-width="170px" label-position="left" style="max-width:720px">
|
||||
<el-form-item label="专线名称" required>
|
||||
<el-form-item :label="$t('tunnelEditor.tunnelName')" required>
|
||||
<el-input v-model="form.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="协议类型">
|
||||
<el-form-item :label="$t('tunnelEditor.protocolType')">
|
||||
<el-tag :color="protocolColor(protocol)" effect="dark" style="border:none">{{ protocolDisplay(protocol) }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item v-for="field in visibleFields" :key="field.key" :label="field.label" :required="field.required">
|
||||
@@ -24,18 +24,18 @@
|
||||
<el-option v-for="option in field.options" :key="optionValue(option)" :label="optionLabel(option)" :value="optionValue(option)" />
|
||||
</el-select>
|
||||
<el-input v-else-if="field.type === 'password'" v-model="form.config[field.key]" type="password"
|
||||
show-password :placeholder="field.placeholder || '留空保持原值'" />
|
||||
show-password :placeholder="field.placeholder || $t('tunnelEditor.keepOriginal')" />
|
||||
<el-input v-else v-model="form.config[field.key]" :placeholder="field.placeholder || field.default" />
|
||||
<div v-if="field.tip" class="field-tip">{{ field.tip }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="到期时间">
|
||||
<el-date-picker v-model="form.expire_at" type="datetime" clearable placeholder="留空为永久" style="width:100%" />
|
||||
<el-form-item :label="$t('tunnelEditor.expireTime')">
|
||||
<el-date-picker v-model="form.expire_at" type="datetime" clearable :placeholder="$t('tunnelEditor.expirePlaceholder')" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="自动启动"><el-switch v-model="form.auto_start" /></el-form-item>
|
||||
<el-form-item label="备注"><el-input v-model="form.remark" type="textarea" :rows="3" /></el-form-item>
|
||||
<el-form-item :label="$t('tunnelEditor.autoStart')"><el-switch v-model="form.auto_start" /></el-form-item>
|
||||
<el-form-item :label="$t('common.remark')"><el-input v-model="form.remark" type="textarea" :rows="3" /></el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="save">保存配置</el-button>
|
||||
<el-button @click="router.push('/tunnels')">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="save">{{ $t('tunnelEditor.saveConfig') }}</el-button>
|
||||
<el-button @click="router.push('/tunnels')">{{ $t('common.cancel') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
@@ -45,9 +45,11 @@
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import api from '../api'
|
||||
import { CUSTOM_FIELDS, PROTOCOL_META, protocolColor, protocolDisplay, optionValue, optionLabel } from '../protocols'
|
||||
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const loading = ref(true)
|
||||
@@ -84,7 +86,7 @@ async function load() {
|
||||
config,
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error.response?.data?.error || '加载专线失败')
|
||||
ElMessage.error(error.response?.data?.error || t('tunnelEditor.loadFailed'))
|
||||
router.push('/tunnels')
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -92,14 +94,14 @@ async function load() {
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (!form.value.name) return ElMessage.warning('请填写专线名称')
|
||||
if (!form.value.name) return ElMessage.warning(t('tunnelEditor.fillName'))
|
||||
saving.value = true
|
||||
try {
|
||||
await api.put(`/tunnels/${route.params.id}`, form.value)
|
||||
ElMessage.success('配置已保存')
|
||||
ElMessage.success(t('tunnelEditor.configSaved'))
|
||||
router.push('/tunnels')
|
||||
} catch (error) {
|
||||
ElMessage.error(error.response?.data?.error || '保存失败')
|
||||
ElMessage.error(error.response?.data?.error || t('common.saveFailed'))
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user