feat(frontend): give IPSec peer setup a responsive workflow
This commit is contained in:
1 parent
94ec3b042b
commit
3e6e481b79
1 file changed
+115
-97
@@ -1,102 +1,102 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="16">
|
||||
<!-- 左:本端配置 -->
|
||||
<el-col :span="12">
|
||||
<el-card class="page-card" shadow="never">
|
||||
<template #header>
|
||||
<b>{{ $t('ipsecPeer.localParams') }}</b>
|
||||
<el-select v-model="selectedTemplate" :placeholder="$t('ipsecPeer.loadTemplate')" size="small" style="width:200px;float:right"
|
||||
@change="applyTemplate">
|
||||
<el-option v-for="t in templates" :key="t.key" :label="t.name" :value="t.key" />
|
||||
</el-select>
|
||||
</template>
|
||||
<el-alert v-if="templateNote" :title="templateNote" type="info" :closable="false" style="margin-bottom:14px" />
|
||||
<el-form label-width="130px" size="default">
|
||||
<el-form-item :label="$t('ipsecPeer.localPublicIp')" required><el-input v-model="local.public_endpoint" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.remoteGateway')" required><el-input v-model="local.remote_gateway" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.localSubnets')" required><el-input v-model="local.local_subnets" placeholder="192.168.1.0/24" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.remoteSubnets')" required><el-input v-model="local.remote_subnets" placeholder="10.0.0.0/24" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.psk')" required><el-input v-model="local.psk" type="password" show-password /></el-form-item>
|
||||
<el-divider content-position="left">{{ $t('ipsecPeer.phase1') }}</el-divider>
|
||||
<el-form-item :label="$t('ipsecPeer.ikeVersion')">
|
||||
<el-radio-group v-model="local.ike_version">
|
||||
<el-radio-button value="ikev2">IKEv2</el-radio-button>
|
||||
<el-radio-button value="ikev1">IKEv1</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.proposal')">
|
||||
<el-input v-model="local.ike_proposal" placeholder="aes256-sha256-modp2048" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.localId')"><el-input v-model="local.local_id" placeholder="@vpn-local" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.remoteId')"><el-input v-model="local.remote_id" placeholder="@vpn-remote" /></el-form-item>
|
||||
<el-divider content-position="left">{{ $t('ipsecPeer.phase2') }}</el-divider>
|
||||
<el-form-item :label="$t('ipsecPeer.espProposal')"><el-input v-model="local.esp_proposal" placeholder="aes256-sha256" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.pfsGroup')"><el-input v-model="local.pfs_group" placeholder="modp2048" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.dpdPolicy')">
|
||||
<el-select v-model="local.dpd_action" style="width:100%">
|
||||
<el-option :label="$t('ipsecPeer.dpdRestart')" value="restart" />
|
||||
<el-option :label="$t('ipsecPeer.dpdClear')" value="clear" />
|
||||
<el-option :label="$t('ipsecPeer.dpdTrap')" value="trap" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button type="primary" style="width:100%" :loading="creating" @click="createTunnel">
|
||||
{{ $t('ipsecPeer.createIpsecTunnel') }}
|
||||
</el-button>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 右:对端参数对比 + 连通性测试 -->
|
||||
<el-col :span="12">
|
||||
<el-card class="page-card" shadow="never">
|
||||
<template #header><b>{{ $t('ipsecPeer.paramConsistency') }}</b></template>
|
||||
<el-form label-width="130px" size="default">
|
||||
<el-form-item :label="$t('ipsecPeer.peerIkeVersion')">
|
||||
<el-radio-group v-model="peer.ike_version">
|
||||
<el-radio-button value="ikev2">IKEv2</el-radio-button>
|
||||
<el-radio-button value="ikev1">IKEv1</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.peerPhase1')"><el-input v-model="peer.ike_proposal" :placeholder="$t('ipsecPeer.peerIkePlaceholder')" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.peerPhase2')"><el-input v-model="peer.esp_proposal" :placeholder="$t('ipsecPeer.peerEspPlaceholder')" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.peerPfsGroup')"><el-input v-model="peer.pfs_group" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.peerLocalSubnets')"><el-input v-model="peer.local_subnets" :placeholder="$t('ipsecPeer.peerSubnetsPlaceholder')" /></el-form-item>
|
||||
</el-form>
|
||||
<el-button style="width:100%" :loading="checking" @click="compare">{{ $t('ipsecPeer.startCompare') }}</el-button>
|
||||
<div v-if="compareResult" style="margin-top:14px">
|
||||
<el-alert v-if="compareResult.consistent" type="success" :title="$t('ipsecPeer.paramsConsistent')" :closable="false" show-icon />
|
||||
<div v-else>
|
||||
<el-alert type="error" :title="$t('ipsecPeer.paramsInconsistent')" :closable="false" />
|
||||
<ul class="diff-list">
|
||||
<li v-for="(d, i) in compareResult.differences" :key="i">{{ d }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="page-card" shadow="never" style="margin-top:16px">
|
||||
<template #header><b>{{ $t('ipsecPeer.connectivityTest') }}</b></template>
|
||||
<el-select v-model="testTunnelId" :placeholder="$t('ipsecPeer.selectIpsecTunnel')" style="width:100%">
|
||||
<el-option v-for="tun in ipsecTunnels" :key="tun.id" :label="`${tun.name}(${tun.state})`" :value="tun.id" />
|
||||
<div class="ipsec-page">
|
||||
<el-card class="page-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-heading">
|
||||
<b>{{ $t('ipsecPeer.localParams') }}</b>
|
||||
<el-select v-model="selectedTemplate" :placeholder="$t('ipsecPeer.loadTemplate')" class="template-select" @change="applyTemplate">
|
||||
<el-option v-for="t in templates" :key="t.key" :label="t.name" :value="t.key" />
|
||||
</el-select>
|
||||
<el-button type="primary" style="width:100%;margin-top:12px" :loading="testing" :disabled="!testTunnelId"
|
||||
@click="testConnectivity">{{ $t('ipsecPeer.testConnectivity') }}</el-button>
|
||||
<div v-if="testResult" style="margin-top:14px">
|
||||
<el-descriptions :column="1" border size="small">
|
||||
<el-descriptions-item :label="$t('common.target')">{{ testResult.target }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('common.status')">
|
||||
<el-tag :type="testResult.reachable ? 'success' : 'danger'">
|
||||
{{ testResult.reachable ? $t('ipsecPeer.reachable') : $t('ipsecPeer.unreachable') }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('ipsecPeer.latency')">{{ testResult.latency_ms }} ms</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('common.description')">{{ testResult.note || testResult.ike_udp500_note || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<el-alert v-if="templateNote" :title="templateNote" type="info" :closable="false" class="template-note" />
|
||||
<el-form label-position="top" class="ipsec-form">
|
||||
<div class="field-grid">
|
||||
<el-form-item :label="$t('ipsecPeer.localPublicIp')" required><el-input v-model="local.public_endpoint" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.remoteGateway')" required><el-input v-model="local.remote_gateway" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.localSubnets')" required><el-input v-model="local.local_subnets" placeholder="192.168.1.0/24" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.remoteSubnets')" required><el-input v-model="local.remote_subnets" placeholder="10.0.0.0/24" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.psk')" required><el-input v-model="local.psk" type="password" show-password /></el-form-item>
|
||||
</div>
|
||||
<el-collapse class="advanced-options">
|
||||
<el-collapse-item :title="$t('ipsecPeer.advancedOptions')" name="advanced">
|
||||
<div class="section-heading">{{ $t('ipsecPeer.phase1') }}</div>
|
||||
<div class="field-grid">
|
||||
<el-form-item :label="$t('ipsecPeer.ikeVersion')">
|
||||
<el-radio-group v-model="local.ike_version">
|
||||
<el-radio-button value="ikev2">IKEv2</el-radio-button>
|
||||
<el-radio-button value="ikev1">IKEv1</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.proposal')"><el-input v-model="local.ike_proposal" placeholder="aes256-sha256-modp2048" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.localId')"><el-input v-model="local.local_id" placeholder="@vpn-local" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.remoteId')"><el-input v-model="local.remote_id" placeholder="@vpn-remote" /></el-form-item>
|
||||
</div>
|
||||
<div class="section-heading">{{ $t('ipsecPeer.phase2') }}</div>
|
||||
<div class="field-grid">
|
||||
<el-form-item :label="$t('ipsecPeer.espProposal')"><el-input v-model="local.esp_proposal" placeholder="aes256-sha256" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.pfsGroup')"><el-input v-model="local.pfs_group" placeholder="modp2048" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.dpdPolicy')">
|
||||
<el-select v-model="local.dpd_action" style="width:100%">
|
||||
<el-option :label="$t('ipsecPeer.dpdRestart')" value="restart" />
|
||||
<el-option :label="$t('ipsecPeer.dpdClear')" value="clear" />
|
||||
<el-option :label="$t('ipsecPeer.dpdTrap')" value="trap" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-form>
|
||||
<el-button type="primary" :loading="creating" @click="createTunnel">{{ $t('ipsecPeer.createIpsecTunnel') }}</el-button>
|
||||
</el-card>
|
||||
|
||||
<el-card class="page-card" shadow="never">
|
||||
<template #header><b>{{ $t('ipsecPeer.paramConsistency') }}</b></template>
|
||||
<el-form label-position="top" class="ipsec-form field-grid">
|
||||
<el-form-item :label="$t('ipsecPeer.peerIkeVersion')">
|
||||
<el-radio-group v-model="peer.ike_version">
|
||||
<el-radio-button value="ikev2">IKEv2</el-radio-button>
|
||||
<el-radio-button value="ikev1">IKEv1</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.peerPhase1')"><el-input v-model="peer.ike_proposal" :placeholder="$t('ipsecPeer.peerIkePlaceholder')" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.peerPhase2')"><el-input v-model="peer.esp_proposal" :placeholder="$t('ipsecPeer.peerEspPlaceholder')" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.peerPfsGroup')"><el-input v-model="peer.pfs_group" /></el-form-item>
|
||||
<el-form-item :label="$t('ipsecPeer.peerLocalSubnets')"><el-input v-model="peer.local_subnets" :placeholder="$t('ipsecPeer.peerSubnetsPlaceholder')" /></el-form-item>
|
||||
</el-form>
|
||||
<el-button :loading="checking" @click="compare">{{ $t('ipsecPeer.startCompare') }}</el-button>
|
||||
<div v-if="compareResult" style="margin-top:14px">
|
||||
<el-alert v-if="compareResult.consistent" type="success" :title="$t('ipsecPeer.paramsConsistent')" :closable="false" show-icon />
|
||||
<div v-else>
|
||||
<el-alert type="error" :title="$t('ipsecPeer.paramsInconsistent')" :closable="false" />
|
||||
<ul class="diff-list">
|
||||
<li v-for="(d, i) in compareResult.differences" :key="i">{{ d }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="page-card" shadow="never">
|
||||
<template #header><b>{{ $t('ipsecPeer.connectivityTest') }}</b></template>
|
||||
<div class="test-controls">
|
||||
<el-select v-model="testTunnelId" :placeholder="$t('ipsecPeer.selectIpsecTunnel')" class="tunnel-select">
|
||||
<el-option v-for="tun in ipsecTunnels" :key="tun.id" :label="`${tun.name}(${tun.state})`" :value="tun.id" />
|
||||
</el-select>
|
||||
<el-button type="primary" :loading="testing" :disabled="!testTunnelId" @click="testConnectivity">{{ $t('ipsecPeer.testConnectivity') }}</el-button>
|
||||
</div>
|
||||
<div v-if="testResult" style="margin-top:14px">
|
||||
<el-descriptions :column="1" border size="small">
|
||||
<el-descriptions-item :label="$t('common.target')">{{ testResult.target }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('common.status')">
|
||||
<el-tag :type="testResult.reachable ? 'success' : 'danger'">
|
||||
{{ testResult.reachable ? $t('ipsecPeer.reachable') : $t('ipsecPeer.unreachable') }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('ipsecPeer.latency')">{{ testResult.latency_ms }} ms</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('common.description')">{{ testResult.note || testResult.ike_udp500_note || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -188,6 +188,24 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ipsec-page { max-width: 1040px; margin: 0 auto; display: grid; gap: 20px; }
|
||||
.ipsec-page .page-card + .page-card { margin-top: 0; }
|
||||
.card-heading, .test-controls { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
|
||||
.template-select { width: min(280px, 100%); }
|
||||
.template-note { margin-bottom: 20px; }
|
||||
.field-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 32px; }
|
||||
.ipsec-form :deep(.el-form-item) { min-width: 0; margin-bottom: 20px; }
|
||||
.ipsec-form :deep(.el-form-item__label) { height: auto; line-height: 1.4; margin-bottom: 8px; }
|
||||
.advanced-options { margin: 0 0 20px; }
|
||||
.advanced-options :deep(.el-collapse-item__content) { padding: 12px 0 4px; }
|
||||
.section-heading { font-weight: 600; margin: 8px 0 16px; }
|
||||
.section-heading:not(:first-child) { border-top: 1px solid var(--el-border-color-lighter); padding-top: 20px; }
|
||||
.tunnel-select { width: min(440px, 100%); }
|
||||
@media (max-width: 680px) {
|
||||
.field-grid { grid-template-columns: minmax(0, 1fr); }
|
||||
.test-controls { align-items: stretch; }
|
||||
.test-controls .el-button, .test-controls .tunnel-select { width: 100%; margin-left: 0; }
|
||||
}
|
||||
.diff-list { margin: 10px 0 0; padding-left: 20px; color: #f56c6c; font-size: 13px; }
|
||||
.diff-list li { margin-bottom: 6px; }
|
||||
</style>
|
||||
Reference in new issue
Block a user