docs+feat: record backlog tip and move custom protocols to More
Document delivered IA and backlog four items; put custom plugins under the wizard More group per design.
This commit is contained in:
1 parent
9984e21930
commit
76bfda8627
8 files changed
+50
-10
No files matched your search
@@ -365,7 +365,8 @@
|
||||
"installFailed": "Failed to install {protocol}",
|
||||
"groupEmployee": "Employee access",
|
||||
"groupSite": "Site interconnect",
|
||||
"groupPenetrate": "Intranet penetration"
|
||||
"groupPenetrate": "Intranet penetration",
|
||||
"groupMore": "More"
|
||||
},
|
||||
"users": {
|
||||
"title": "Admins",
|
||||
|
||||
@@ -365,7 +365,8 @@
|
||||
"installFailed": "{protocol} 安装失败",
|
||||
"groupEmployee": "员工接入",
|
||||
"groupSite": "站点对接",
|
||||
"groupPenetrate": "内网穿透"
|
||||
"groupPenetrate": "内网穿透",
|
||||
"groupMore": "更多"
|
||||
},
|
||||
"users": {
|
||||
"title": "管理员",
|
||||
|
||||
@@ -123,7 +123,7 @@ watch(() => route.query.tab, (name) => {
|
||||
router.replace({ path: '/sessions', query: { tab: 'audit' } })
|
||||
return
|
||||
}
|
||||
const next = TAB_NAMES.includes(name) ? name : 'audit'
|
||||
const next = normalizeLogTab(name)
|
||||
if (activeTab.value !== next) activeTab.value = next
|
||||
}, { immediate: true })
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
<div class="row-between">
|
||||
<div class="filter-bar">
|
||||
<el-input v-model="auditFilters.username" :placeholder="$t('common.username')" clearable size="small" style="width:120px" @clear="searchAudit" @keyup.enter="searchAudit" />
|
||||
<el-input v-model="auditFilters.keyword" :placeholder="$t('logs.keywordPlaceholder')" clearable size="small" style="width:160px" @clear="searchAudit" @keyup.enter="searchAudit" />
|
||||
<el-select v-model="auditFilters.event" :placeholder="$t('logs.eventType')" clearable multiple size="small" style="width:220px" @change="searchAudit">
|
||||
<el-option :label="$t('sessions.eventConnect')" value="connect" />
|
||||
<el-option :label="$t('sessions.eventDisconnect')" value="disconnect" />
|
||||
@@ -146,7 +147,7 @@ const auditExporting = ref(false)
|
||||
const auditPage = ref(1)
|
||||
const auditPageSize = ref(20)
|
||||
const auditTotal = ref(0)
|
||||
const auditFilters = reactive({ username: '', event: [] })
|
||||
const auditFilters = reactive({ username: '', keyword: '', event: [] })
|
||||
const auditDateRange = ref(null)
|
||||
|
||||
watch(() => route.query.tab, (name) => {
|
||||
@@ -209,7 +210,8 @@ async function loadAudit() {
|
||||
const params = {
|
||||
page: auditPage.value,
|
||||
page_size: auditPageSize.value,
|
||||
username: auditFilters.username,
|
||||
username: auditFilters.username || undefined,
|
||||
keyword: auditFilters.keyword || undefined,
|
||||
}
|
||||
if (auditFilters.event?.length) params.event = auditFilters.event.join(',')
|
||||
if (auditDateRange.value) {
|
||||
@@ -227,7 +229,10 @@ async function loadAudit() {
|
||||
|
||||
async function exportAudit() {
|
||||
auditExporting.value = true
|
||||
const params = { username: auditFilters.username }
|
||||
const params = {
|
||||
username: auditFilters.username || undefined,
|
||||
keyword: auditFilters.keyword || undefined,
|
||||
}
|
||||
if (auditFilters.event?.length) params.event = auditFilters.event.join(',')
|
||||
if (auditDateRange.value) {
|
||||
params.start = auditDateRange.value[0]
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<!-- 第一步:协议选择 -->
|
||||
<el-alert v-if="step === 0" type="info" :closable="false" :title="$t('tunnelWizard.onDemandHint')" style="margin-bottom:16px" />
|
||||
<div v-if="step === 0" class="proto-groups">
|
||||
<div v-for="group in protocolGroups" :key="group.id" class="proto-group">
|
||||
<div v-for="group in protocolGroups.filter(g => g.keys.some(k => builtin[k]) || (g.id === 'more' && customProtocols.length))" :key="group.id" class="proto-group">
|
||||
<div class="proto-group-title">{{ $t(group.titleKey) }}</div>
|
||||
<div class="proto-grid">
|
||||
<div v-for="key in group.keys.filter(k => builtin[k])" :key="key"
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="group.id === 'penetrate'">
|
||||
<template v-if="group.id === 'more'">
|
||||
<div v-for="p in customProtocols" :key="p.name" class="proto-card" :class="{ active: form.protocol === p.name }"
|
||||
@click="selectProtocol(p.name)">
|
||||
<div class="proto-icon"><ProtocolIcon protocol="custom" :size="36" /></div>
|
||||
@@ -177,6 +177,7 @@ const protocolGroups = [
|
||||
{ id: 'employee', titleKey: 'tunnelWizard.groupEmployee', keys: ['wireguard', 'ikev2', 'l2tp', 'openvpn'] },
|
||||
{ id: 'site', titleKey: 'tunnelWizard.groupSite', keys: ['ipsec', 'gre'] },
|
||||
{ id: 'penetrate', titleKey: 'tunnelWizard.groupPenetrate', keys: ['frp'] },
|
||||
{ id: 'more', titleKey: 'tunnelWizard.groupMore', keys: [] },
|
||||
]
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Plus, InfoFilled, ArrowDown, Refresh } from '@element-plus/icons-vue'
|
||||
@@ -141,7 +141,14 @@ const changingIds = ref(new Set())
|
||||
let loadRevision = 0
|
||||
const keyword = ref('')
|
||||
const protocolFilter = ref('')
|
||||
const stateFilter = ref(typeof route.query.state === 'string' ? route.query.state : '')
|
||||
function initialStateFilter() {
|
||||
const f = route.query.filter
|
||||
const s = route.query.state
|
||||
if (f === 'unhealthy') return 'error'
|
||||
if (typeof s === 'string' && s) return s
|
||||
return ''
|
||||
}
|
||||
const stateFilter = ref(initialStateFilter())
|
||||
const protocolOptions = ref([])
|
||||
// 批量操作
|
||||
const selectedRows = ref([])
|
||||
@@ -268,6 +275,14 @@ function download(row) {
|
||||
})
|
||||
.catch(e => ElMessage.error(e.message || t('tunnels.downloadFailed')))
|
||||
}
|
||||
|
||||
watch(() => [route.query.filter, route.query.state], () => {
|
||||
const next = initialStateFilter()
|
||||
if (stateFilter.value !== next) {
|
||||
stateFilter.value = next
|
||||
load()
|
||||
}
|
||||
})
|
||||
let timer
|
||||
onMounted(async () => {
|
||||
load()
|
||||
|
||||
Reference in new issue
Block a user