Redesign console navigation and settings tabs
This commit is contained in:
1 parent
ea9c885a15
commit
16955c3126
5 files changed
+205
-74
No files matched your search
@@ -1,6 +1,11 @@
|
||||
<template>
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :lg="10">
|
||||
<section class="settings-page">
|
||||
<div class="settings-intro">
|
||||
<div><span class="settings-eyebrow">OPSX / SETTINGS</span><h2>{{ $t('router.settings') }}</h2><p>{{ $t('settings.pageDescription') }}</p></div>
|
||||
</div>
|
||||
<el-tabs v-model="activeTab" class="settings-tabs" @tab-change="changeTab">
|
||||
<el-tab-pane :label="$t('settings.securityTab')" name="security">
|
||||
<div class="settings-column">
|
||||
<el-alert v-if="auth.mustChange" type="warning" :closable="false" show-icon
|
||||
:title="$t('settings.mustChangePassword')"
|
||||
style="margin-bottom:16px" />
|
||||
@@ -30,31 +35,13 @@
|
||||
<el-button type="success" style="width:100%;margin-top:8px" :loading="mfaLoading" @click="enableMFA">{{ $t('settings.verifyAndEnable') }}</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="page-card" shadow="never" style="margin-top:16px">
|
||||
<template #header><b>{{ $t('settings.deployTitle') }}</b></template>
|
||||
<el-descriptions :column="1" size="small" border>
|
||||
<el-descriptions-item :label="$t('settings.deliveryForm')">{{ $t('settings.deliveryDesc') }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('settings.dataDir')">{{ $t('settings.dataDirDesc') }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('settings.listenAddr')">{{ $t('settings.listenAddrDesc') }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('settings.keyStorage')">{{ $t('settings.keyStorageDesc') }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('settings.systemDeps')">{{ $t('settings.systemDepsDesc') }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('settings.prodSuggestion')">{{ $t('settings.prodSuggestionDesc') }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :lg="14" v-if="auth.isAdmin()">
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="auth.isAdmin()" :label="$t('settings.gatewayTab')" name="gateway">
|
||||
<div class="settings-column settings-column-wide">
|
||||
<el-card class="page-card" shadow="never" style="margin-bottom:16px">
|
||||
<template #header><b>{{ $t('tls.title') }}</b></template>
|
||||
<el-alert type="info" :closable="false" :title="$t('tls.description')" style="margin-bottom:16px" />
|
||||
<el-card shadow="never" style="margin-bottom:16px">
|
||||
<template #header><b>{{ $t('tls.commonSetupTitle') }}</b></template>
|
||||
<el-alert type="info" :closable="false" :title="$t('tls.commonSetupHint')" style="margin-bottom:12px" />
|
||||
<template v-if="hostReadiness">
|
||||
<p class="tls-hint">{{ $t('tls.hostRoot') }}:{{ hostReadiness.root ? '✓' : '—' }} · {{ $t('tls.hostRoute') }}:{{ hostReadiness.default_route_ready ? '✓' : '—' }} · {{ $t('tls.hostForward') }}:{{ hostReadiness.ipv4_forwarding ? '✓' : '—' }}</p>
|
||||
<el-button :loading="hostFixing" :disabled="!hostReadiness.root || hostReadiness.ipv4_forwarding" @click="enableForwarding">{{ $t('tls.enableForwarding') }}</el-button>
|
||||
</template>
|
||||
<el-button :loading="hostLoading" @click="loadHostReadiness">{{ $t('tls.refresh') }}</el-button>
|
||||
</el-card>
|
||||
<el-steps :active="nginxSetup?.site_stage === 'https' ? 3 : nginxSetup?.nginx_installed && nginxSetup?.certbot_installed && nginxSetup?.webroot_ready ? 2 : 1" finish-status="success" style="margin-bottom:16px">
|
||||
<el-step :title="$t('tls.setupStepCheck')" />
|
||||
<el-step :title="$t('tls.setupStepInstall')" />
|
||||
@@ -119,13 +106,33 @@
|
||||
<pre class="nginx-preview">{{ nginxPreview.https_site }}</pre>
|
||||
</template>
|
||||
</el-card>
|
||||
<el-card class="page-card" shadow="never">
|
||||
<template #header><b>{{ $t('settings.auditTitle') }}</b></template>
|
||||
<el-empty :description="$t('settings.auditMigrated')">
|
||||
<el-button type="primary" @click="$router.push('/logs')">{{ $t('router.logs') }}</el-button>
|
||||
</el-empty>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="auth.isAdmin()" :label="$t('settings.hostTab')" name="host">
|
||||
<div class="settings-column settings-column-wide">
|
||||
<el-card class="page-card" shadow="never" style="margin-bottom:16px">
|
||||
<template #header><b>{{ $t('tls.commonSetupTitle') }}</b></template>
|
||||
<el-alert type="info" :closable="false" :title="$t('tls.commonSetupHint')" style="margin-bottom:12px" />
|
||||
<template v-if="hostReadiness">
|
||||
<p class="tls-hint">{{ $t('tls.hostRoot') }}:{{ hostReadiness.root ? '✓' : '—' }} · {{ $t('tls.hostRoute') }}:{{ hostReadiness.default_route_ready ? '✓' : '—' }} · {{ $t('tls.hostForward') }}:{{ hostReadiness.ipv4_forwarding ? '✓' : '—' }}</p>
|
||||
<el-button :loading="hostFixing" :disabled="!hostReadiness.root || hostReadiness.ipv4_forwarding" @click="enableForwarding">{{ $t('tls.enableForwarding') }}</el-button>
|
||||
</template>
|
||||
<el-button :loading="hostLoading" @click="loadHostReadiness">{{ $t('tls.refresh') }}</el-button>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-card class="page-card" shadow="never">
|
||||
<template #header><b>{{ $t('settings.deployTitle') }}</b></template>
|
||||
<el-descriptions :column="1" size="small" border>
|
||||
<el-descriptions-item :label="$t('settings.deliveryForm')">{{ $t('settings.deliveryDesc') }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('settings.dataDir')">{{ $t('settings.dataDirDesc') }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('settings.listenAddr')">{{ $t('settings.listenAddrDesc') }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('settings.keyStorage')">{{ $t('settings.keyStorageDesc') }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('settings.systemDeps')">{{ $t('settings.systemDepsDesc') }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('settings.prodSuggestion')">{{ $t('settings.prodSuggestionDesc') }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- MFA 备用码持久化弹窗 -->
|
||||
<el-dialog v-model="backupVisible" :title="$t('settings.mfaEnabledTitle')" width="480px" align-center
|
||||
@@ -140,17 +147,26 @@
|
||||
<el-button type="primary" @click="backupVisible = false">{{ $t('settings.savedConfirm') }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-row>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { ref, onMounted, nextTick, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import QRCode from 'qrcode'
|
||||
import api, { auth } from '../api'
|
||||
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const activeTab = ref('security')
|
||||
const visibleTab = (name) => auth.isAdmin() && ['gateway', 'host'].includes(name) ? name : 'security'
|
||||
watch(() => route.query.tab, name => { activeTab.value = visibleTab(name) }, { immediate: true })
|
||||
function changeTab(name) {
|
||||
if (route.query.tab !== name) router.replace({ path: '/settings', query: name === 'security' ? {} : { tab: name } })
|
||||
}
|
||||
const pwd = ref({ old_password: '', new_password: '', confirm: '' })
|
||||
const pwdLoading = ref(false)
|
||||
const mfa = ref({ enabled: false, uri: '', code: '' })
|
||||
@@ -391,6 +407,19 @@ function copyBackup() {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.settings-page { max-width: 1160px; margin: 0 auto; padding-bottom: 32px; }
|
||||
.settings-intro { padding: 8px 4px 22px; }
|
||||
.settings-eyebrow { color: #287cc6; font-size: 11px; font-weight: 700; letter-spacing: 2px; }
|
||||
.settings-intro h2 { margin: 9px 0 6px; color: #1b2b43; font-size: 25px; letter-spacing: -.4px; }
|
||||
.settings-intro p { margin: 0; color: var(--text-regular); line-height: 1.6; font-size: 13px; }
|
||||
.settings-tabs { background: white; border: 1px solid var(--border-base); border-radius: 14px; box-shadow: var(--shadow-sm); padding: 0 22px 22px; min-height: 380px; }
|
||||
.settings-tabs :deep(.el-tabs__header) { margin-bottom: 22px; }
|
||||
.settings-tabs :deep(.el-tabs__item) { height: 55px; font-weight: 600; }
|
||||
.settings-column { max-width: 620px; }
|
||||
.settings-column-wide { max-width: 850px; }
|
||||
.settings-column :deep(.page-card) { border-color: #e6ecf3; box-shadow: none; }
|
||||
.settings-column :deep(.el-card__header) { background: #f9fbfe; color: #21344e; }
|
||||
@media (max-width: 640px) { .settings-tabs { padding: 0 14px 16px; } .settings-tabs :deep(.el-tabs__item) { font-size: 12px; padding: 0 10px; } }
|
||||
.backup-codes {
|
||||
display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; padding: 12px;
|
||||
background: var(--bg-inset); border-radius: var(--radius-sm); border: 1px solid var(--border-base);
|
||||
|
||||
Reference in new issue
Block a user