Refresh tunnel actions promptly and clarify Feishu OIDC setup

This commit is contained in:
opsx committed 2026-10-22 00:01:12 +08:00
1 parent 41fb9d5631
commit 6a1bace3e9
10 files changed
+136 -32

No files matched your search

+31 -22
View File
@@ -75,24 +75,24 @@
<el-table-column :label="$t('tunnels.node')" width="110">
<template #default="{ row }">{{ row.node?.name || $t('tunnels.localNode') }}</template>
</el-table-column>
<el-table-column :label="$t('common.operation')" width="285" fixed="right">
<el-table-column :label="$t('common.operation')" width="205" fixed="right">
<template #default="{ row }">
<el-button v-if="stateOf(row) !== 'running'" type="success" size="small" plain @click="start(row)">{{ $t('tunnels.start') }}</el-button>
<el-button v-else type="warning" size="small" plain @click="stop(row)">{{ $t('tunnels.stop') }}</el-button>
<el-button size="small" link @click="showDetail(row)">{{ $t('tunnels.detail') }}</el-button>
<el-button v-if="stateOf(row) !== 'running'" type="success" size="small" plain :loading="changingIds.has(row.id)" :disabled="changingIds.has(row.id)" @click="changeState(row, 'start')">{{ $t('tunnels.start') }}</el-button>
<el-button v-else type="warning" size="small" plain :loading="changingIds.has(row.id)" :disabled="changingIds.has(row.id)" @click="changeState(row, 'stop')">{{ $t('tunnels.stop') }}</el-button>
<el-button size="small" link @click="$router.push(`/tunnels/${row.id}/edit`)">{{ $t('common.edit') }}</el-button>
<el-dropdown trigger="click" @command="cmd => onMoreCommand(cmd, row)" style="margin-left:8px">
<el-button size="small" link>{{ $t('tunnels.more') }}<el-icon class="el-icon--right"><ArrowDown /></el-icon></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="detail">{{ $t('tunnels.detail') }}</el-dropdown-item>
<el-dropdown-item command="logs">{{ $t('tunnels.viewLogs') }}</el-dropdown-item>
<el-dropdown-item command="download">{{ $t('tunnels.downloadConfig') }}</el-dropdown-item>
<el-dropdown-item v-if="row.protocol === 'wireguard'" command="qrcode">{{ $t('tunnels.showQrCode') }}</el-dropdown-item>
<el-dropdown-item v-if="row.protocol === 'ipsec'" command="ipsec-peer">{{ $t('tunnels.ipsecTest') }}</el-dropdown-item>
<el-dropdown-item command="delete" divided>{{ $t('common.delete') }}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button type="danger" size="small" link @click="remove(row)">{{ $t('common.delete') }}</el-button>
</template>
</el-table-column>
<template #empty>
@@ -251,6 +251,8 @@ const { t } = useI18n()
const router = useRouter()
const list = ref([])
const loading = ref(false)
const changingIds = ref(new Set())
let loadRevision = 0
const keyword = ref('')
const protocolFilter = ref('')
const stateFilter = ref('')
@@ -330,6 +332,7 @@ function peerOnline(row) {
}
async function load() {
const revision = ++loadRevision
loading.value = true
try {
const { data } = await api.get('/tunnels', { params: { keyword: keyword.value, protocol: protocolFilter.value } })
@@ -337,13 +340,15 @@ async function load() {
if (stateFilter.value) {
result = result.filter(r => stateOf(r) === stateFilter.value)
}
list.value = result
} finally { loading.value = false }
if (revision === loadRevision) list.value = result
} finally { if (revision === loadRevision) loading.value = false }
}
// 更多操作下拉命令分发
function onMoreCommand(cmd, row) {
if (cmd === 'download') download(row)
if (cmd === 'detail') showDetail(row)
else if (cmd === 'delete') remove(row)
else if (cmd === 'download') download(row)
else if (cmd === 'qrcode') showQr(row)
else if (cmd === 'ipsec-peer') router.push('/ipsec-peer')
else if (cmd === 'logs') openLogStream(row)
@@ -446,23 +451,27 @@ async function savePolicy(tunnelId) {
} finally { policySaving.value = false }
}
async function start(row) {
async function changeState(row, action) {
if (changingIds.value.has(row.id)) return
changingIds.value = new Set([...changingIds.value, row.id])
++loadRevision // discard a poll started before this operation
try {
await api.post(`/tunnels/${row.id}/start`)
ElMessage.success(t('tunnels.started'))
const { data } = await api.post(`/tunnels/${row.id}/${action}`)
row.state = data.state
row.runtime = null // discard the monitor's older snapshot immediately
if (stateFilter.value && stateFilter.value !== data.state) {
list.value = list.value.filter(item => item.id !== row.id)
}
ElMessage.success(t(action === 'start' ? 'tunnels.started' : 'tunnels.stopped'))
} catch (e) {
ElMessage.error(e.response?.data?.error || t('tunnels.startFailed'))
ElMessage.error(e.response?.data?.error || t(action === 'start' ? 'tunnels.startFailed' : 'tunnels.stopFailed'))
} finally {
try { await load() } finally {
const next = new Set(changingIds.value)
next.delete(row.id)
changingIds.value = next
}
}
load()
}
async function stop(row) {
try {
await api.post(`/tunnels/${row.id}/stop`)
ElMessage.success(t('tunnels.stopped'))
} catch (e) {
ElMessage.error(e.response?.data?.error || t('tunnels.stopFailed'))
}
load()
}
async function remove(row) {
try {