refactor(servers): 精简行操作并收拢批量诊断
一键登录文案、移除监测/站点行按钮,检测路径与诊断移至批量栏。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
# 服务器列表操作列与批量栏调整
|
||||
|
||||
**日期**:2026-06-21
|
||||
|
||||
## 变更摘要
|
||||
|
||||
- 行内宝塔按钮文案改为「一键登录」
|
||||
- 移除行内「监测」「站点」按钮(站点链接仍在名称/域名列可点)
|
||||
- 「检测路径」「诊断」从每行操作移至顶部批量操作栏(需勾选服务器)
|
||||
- 批量「检测路径」文案统一;「诊断」须单选 1 台
|
||||
|
||||
## 动机
|
||||
|
||||
行操作过多拥挤;路径检测与 Agent 诊断属批量运维动作,与「健康检查」同类。
|
||||
|
||||
## 涉及文件
|
||||
|
||||
- `frontend/src/components/servers/ServerTableRowActions.vue`
|
||||
- `frontend/src/components/servers/ServerBatchActionBar.vue`
|
||||
- `frontend/src/pages/ServersPage.vue`
|
||||
- `frontend/src/components/servers/ServerUnsetPathPanel.vue`
|
||||
- `frontend/src/constants/serverTableHeaders.ts`
|
||||
|
||||
## 迁移 / 重启
|
||||
|
||||
仅前端构建部署。
|
||||
|
||||
## 验证
|
||||
|
||||
1. `#/servers` 行内见「一键登录」,无「检测路径/诊断」
|
||||
2. 勾选多台 → 批量栏可「检测路径」
|
||||
3. 单选 1 台 → 批量栏「诊断」弹出 Agent 诊断对话框
|
||||
@@ -5,7 +5,8 @@
|
||||
<v-spacer />
|
||||
<v-btn size="small" variant="text" @click="$emit('batch-category')">修改分类</v-btn>
|
||||
<v-btn size="small" variant="text" @click="$emit('health-check')">健康检查</v-btn>
|
||||
<v-btn size="small" variant="text" @click="$emit('detect-path')">检测目标路径</v-btn>
|
||||
<v-btn size="small" variant="text" @click="$emit('detect-path')">检测路径</v-btn>
|
||||
<v-btn size="small" variant="text" :loading="agentDiagnoseLoading" @click="$emit('agent-diagnose')">诊断</v-btn>
|
||||
<v-btn size="small" variant="text" @click="$emit('install-agent')">安装Agent</v-btn>
|
||||
<v-btn size="small" variant="text" @click="$emit('upgrade-agent')">升级Agent</v-btn>
|
||||
<v-btn size="small" variant="text" color="error" @click="$emit('uninstall-agent')">卸载Agent</v-btn>
|
||||
@@ -18,12 +19,14 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
count: number
|
||||
agentDiagnoseLoading?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
'batch-category': []
|
||||
'health-check': []
|
||||
'detect-path': []
|
||||
'agent-diagnose': []
|
||||
'install-agent': []
|
||||
'upgrade-agent': []
|
||||
'uninstall-agent': []
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
<template>
|
||||
<div class="server-row-actions d-flex ga-1 align-center justify-end flex-nowrap">
|
||||
<v-btn
|
||||
:icon="pinnedSlot != null ? 'mdi-check' : 'mdi-plus'"
|
||||
size="x-small"
|
||||
variant="text"
|
||||
:color="pinnedSlot != null ? 'success' : 'primary'"
|
||||
density="compact"
|
||||
class="server-row-actions__pin"
|
||||
:title="pinnedSlot != null ? `已在监测 · 槽 ${pinnedSlot + 1}` : '加入实时监测'"
|
||||
:loading="pinLoading"
|
||||
@click.stop="$emit('pin')"
|
||||
/>
|
||||
<v-btn
|
||||
variant="text"
|
||||
size="x-small"
|
||||
@@ -26,23 +15,12 @@
|
||||
size="x-small"
|
||||
color="deep-orange"
|
||||
density="compact"
|
||||
class="server-row-actions__btn"
|
||||
class="server-row-actions__btn server-row-actions__btn--wide"
|
||||
title="一键登录宝塔面板"
|
||||
:loading="btLoginLoading"
|
||||
@click.stop="$emit('bt-login')"
|
||||
>
|
||||
宝塔
|
||||
</v-btn>
|
||||
<v-btn
|
||||
variant="text"
|
||||
size="x-small"
|
||||
density="compact"
|
||||
class="server-row-actions__btn"
|
||||
:disabled="!siteUrl"
|
||||
:title="siteUrl ? `打开 ${siteHost || siteUrl}` : '无站点 URL'"
|
||||
@click.stop="siteUrl && $emit('site')"
|
||||
>
|
||||
站点
|
||||
一键登录
|
||||
</v-btn>
|
||||
<v-btn
|
||||
variant="text"
|
||||
@@ -53,25 +31,6 @@
|
||||
>
|
||||
文件
|
||||
</v-btn>
|
||||
<v-btn
|
||||
variant="text"
|
||||
size="x-small"
|
||||
density="compact"
|
||||
class="server-row-actions__btn"
|
||||
@click.stop="$emit('detect-path')"
|
||||
>
|
||||
检测路径
|
||||
</v-btn>
|
||||
<v-btn
|
||||
variant="text"
|
||||
size="x-small"
|
||||
density="compact"
|
||||
class="server-row-actions__btn"
|
||||
:loading="diagnoseLoading"
|
||||
@click.stop="$emit('diagnose')"
|
||||
>
|
||||
诊断
|
||||
</v-btn>
|
||||
<v-btn
|
||||
variant="text"
|
||||
size="x-small"
|
||||
@@ -96,34 +55,25 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
pinnedSlot?: number | null
|
||||
pinLoading?: boolean
|
||||
btLoginLoading?: boolean
|
||||
siteUrl?: string | null
|
||||
siteHost?: string | null
|
||||
diagnoseLoading?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
pin: []
|
||||
terminal: []
|
||||
'bt-login': []
|
||||
site: []
|
||||
files: []
|
||||
'detect-path': []
|
||||
diagnose: []
|
||||
edit: []
|
||||
delete: []
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.server-row-actions__pin {
|
||||
flex: 0 0 28px;
|
||||
}
|
||||
|
||||
.server-row-actions__btn {
|
||||
min-width: 52px;
|
||||
padding-inline: 6px;
|
||||
}
|
||||
|
||||
.server-row-actions__btn--wide {
|
||||
min-width: 72px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -52,9 +52,11 @@
|
||||
|
||||
<ServerBatchActionBar
|
||||
:count="selectedCount"
|
||||
:agent-diagnose-loading="agentDiagnoseLoading"
|
||||
@batch-category="$emit('batch-category')"
|
||||
@health-check="$emit('health-check')"
|
||||
@detect-path="$emit('detect-path')"
|
||||
@agent-diagnose="$emit('agent-diagnose')"
|
||||
@install-agent="$emit('install-agent')"
|
||||
@upgrade-agent="$emit('upgrade-agent')"
|
||||
@uninstall-agent="$emit('uninstall-agent')"
|
||||
@@ -167,19 +169,10 @@
|
||||
|
||||
<template #item.actions="{ item }">
|
||||
<ServerTableRowActions
|
||||
:pinned-slot="pinnedMap[item.id] ?? null"
|
||||
:pin-loading="pinLoadingId === item.id"
|
||||
:bt-login-loading="btLoginLoadingId === item.id"
|
||||
:site-url="resolveServerSiteUrl(item)"
|
||||
:site-host="resolveServerSiteHost(item)"
|
||||
:diagnose-loading="agentDiagnoseLoadingId === item.id"
|
||||
@pin="$emit('pin', item)"
|
||||
@terminal="$emit('terminal', item)"
|
||||
@bt-login="$emit('bt-login', item)"
|
||||
@site="$emit('site', item)"
|
||||
@files="$emit('files', item)"
|
||||
@detect-path="$emit('detect-path-single', item)"
|
||||
@diagnose="$emit('agent-diagnose', item)"
|
||||
@edit="$emit('edit', item)"
|
||||
@delete="$emit('delete', item)"
|
||||
/>
|
||||
@@ -214,7 +207,6 @@ import ServerInlineDetail from '@/components/servers/ServerInlineDetail.vue'
|
||||
import ServerBatchActionBar from '@/components/servers/ServerBatchActionBar.vue'
|
||||
import ServerTableRowActions from '@/components/servers/ServerTableRowActions.vue'
|
||||
import { SERVER_DATA_TABLE_HEADERS } from '@/constants/serverTableHeaders'
|
||||
import { resolveServerSiteHost, resolveServerSiteUrl } from '@/utils/serverSiteUrl'
|
||||
|
||||
const props = defineProps<{
|
||||
highlight?: boolean
|
||||
@@ -231,10 +223,8 @@ const props = defineProps<{
|
||||
editingPathId: number | null
|
||||
editingPathValue: string
|
||||
savingPathId: number | null
|
||||
pinnedMap: Record<number, number>
|
||||
pinLoadingId: number | null
|
||||
btLoginLoadingId: number | null
|
||||
agentDiagnoseLoadingId: number | null
|
||||
agentDiagnoseLoading: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -250,10 +240,6 @@ const emit = defineEmits<{
|
||||
terminal: [item: ServerApiItem]
|
||||
'bt-login': [item: ServerApiItem]
|
||||
files: [item: ServerApiItem]
|
||||
site: [item: ServerApiItem]
|
||||
pin: [item: ServerApiItem]
|
||||
'detect-path-single': [item: ServerApiItem]
|
||||
'agent-diagnose': [item: ServerApiItem]
|
||||
edit: [item: ServerApiItem]
|
||||
delete: [item: ServerApiItem]
|
||||
'edit-path': [item: ServerApiItem]
|
||||
@@ -262,6 +248,7 @@ const emit = defineEmits<{
|
||||
'batch-category': []
|
||||
'health-check': []
|
||||
'detect-path': []
|
||||
'agent-diagnose': []
|
||||
'install-agent': []
|
||||
'upgrade-agent': []
|
||||
'uninstall-agent': []
|
||||
|
||||
@@ -7,5 +7,5 @@ export const SERVER_DATA_TABLE_HEADERS = [
|
||||
{ title: '目标路径', key: 'target_path', width: 200 },
|
||||
{ title: 'Agent', key: 'agent_version', width: 100 },
|
||||
{ title: '心跳', key: 'last_heartbeat', width: 120 },
|
||||
{ title: '操作', key: 'actions', width: 380, align: 'end' as const, sortable: false },
|
||||
{ title: '操作', key: 'actions', width: 280, align: 'end' as const, sortable: false },
|
||||
]
|
||||
|
||||
@@ -49,10 +49,8 @@
|
||||
:editing-path-id="editingPathId"
|
||||
:editing-path-value="editingPathValue"
|
||||
:saving-path-id="savingPathId"
|
||||
:pinned-map="pinnedMap"
|
||||
:pin-loading-id="pinLoadingId"
|
||||
:bt-login-loading-id="btLoginLoadingId"
|
||||
:agent-diagnose-loading-id="agentDiagnoseLoadingId"
|
||||
:agent-diagnose-loading="agentDiagnoseLoading"
|
||||
@refresh="loadUnsetPathServers()"
|
||||
@update:page="onUnsetPageChange"
|
||||
@update:items-per-page="onUnsetItemsPerPageChange"
|
||||
@@ -63,10 +61,6 @@
|
||||
@terminal="openTerminal"
|
||||
@bt-login="openBtLogin($event.id)"
|
||||
@files="openFiles"
|
||||
@site="openBrowser"
|
||||
@pin="onPinServer"
|
||||
@detect-path-single="detectPathSingle"
|
||||
@agent-diagnose="openAgentDiagnose"
|
||||
@edit="editServer"
|
||||
@delete="confirmDelete"
|
||||
@edit-path="startEditPath"
|
||||
@@ -75,6 +69,7 @@
|
||||
@batch-category="openBatchCategory('unset')"
|
||||
@health-check="batchHealthCheck('unset')"
|
||||
@detect-path="openDetectPathConfirm('unset')"
|
||||
@agent-diagnose="batchAgentDiagnose('unset')"
|
||||
@install-agent="batchInstallAgent('unset')"
|
||||
@upgrade-agent="batchUpgradeAgent('unset')"
|
||||
@uninstall-agent="batchUninstallAgent('unset')"
|
||||
@@ -182,9 +177,11 @@
|
||||
<!-- Batch Actions -->
|
||||
<ServerBatchActionBar
|
||||
:count="selectedIds.size"
|
||||
:agent-diagnose-loading="agentDiagnoseLoading"
|
||||
@batch-category="openBatchCategory('main')"
|
||||
@health-check="batchHealthCheck('main')"
|
||||
@detect-path="openDetectPathConfirm('main')"
|
||||
@agent-diagnose="batchAgentDiagnose('main')"
|
||||
@install-agent="batchInstallAgent('main')"
|
||||
@upgrade-agent="batchUpgradeAgent('main')"
|
||||
@uninstall-agent="batchUninstallAgent('main')"
|
||||
@@ -328,19 +325,10 @@
|
||||
|
||||
<template #item.actions="{ item }">
|
||||
<ServerTableRowActions
|
||||
:pinned-slot="pinnedMap[item.id] ?? null"
|
||||
:pin-loading="pinLoadingId === item.id"
|
||||
:bt-login-loading="btLoginLoadingId === item.id"
|
||||
:site-url="resolveServerSiteUrl(item)"
|
||||
:site-host="resolveServerSiteHost(item)"
|
||||
:diagnose-loading="agentDiagnoseLoadingId === item.id"
|
||||
@pin="onPinServer(item)"
|
||||
@terminal="openTerminal(item)"
|
||||
@bt-login="openBtLogin(item.id)"
|
||||
@site="openBrowser(item)"
|
||||
@files="openFiles(item)"
|
||||
@detect-path="detectPathSingle(item)"
|
||||
@diagnose="openAgentDiagnose(item)"
|
||||
@edit="editServer(item)"
|
||||
@delete="confirmDelete(item)"
|
||||
/>
|
||||
@@ -1086,14 +1074,6 @@ function onUnsetItemsPerPageChange(n: number) {
|
||||
void loadUnsetPathServers()
|
||||
}
|
||||
|
||||
async function detectPathSingle(item: ServerApiItem) {
|
||||
try {
|
||||
await submitServerBatchJob('detect-path', [item.id])
|
||||
} catch (e: unknown) {
|
||||
snackbar(formatApiError(e, '提交检测任务失败'), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
function clearExpandedForPathMigration(serverId: number, newPath: string) {
|
||||
const unset = isUnsetTargetPath(newPath)
|
||||
if (expandedMainIds.value.includes(serverId) && unset) expandedMainIds.value = []
|
||||
@@ -1571,17 +1551,41 @@ function openFiles(item: ServerApiItem) {
|
||||
|
||||
const showAgentDiagnose = ref(false)
|
||||
const agentDiagnoseLoading = ref(false)
|
||||
const agentDiagnoseLoadingId = ref<number | null>(null)
|
||||
const agentActionLoadingId = ref<number | null>(null)
|
||||
const agentDiagnoseResult = ref<AgentDiagnoseResult | null>(null)
|
||||
const agentDiagnoseTarget = ref<ServerApiItem | null>(null)
|
||||
|
||||
function resolveBatchServerItem(scope: BatchScope, serverId: number): ServerApiItem | undefined {
|
||||
const { items } = batchSelection(scope)
|
||||
const fromSelection = items.value.find(
|
||||
(entry): entry is ServerApiItem => typeof entry === 'object' && entry.id === serverId,
|
||||
)
|
||||
if (fromSelection) return fromSelection
|
||||
const list = scope === 'main' ? servers.value : unsetPathServers.value
|
||||
return list.find(s => s.id === serverId)
|
||||
}
|
||||
|
||||
async function batchAgentDiagnose(scope: BatchScope) {
|
||||
batchScope.value = scope
|
||||
const { items } = batchSelection(scope)
|
||||
const ids = normalizeServerIds(items.value)
|
||||
if (ids.length !== 1) {
|
||||
snackbar(ids.length === 0 ? '请先选择 1 台服务器' : 'Agent 诊断每次仅支持单选 1 台', 'warning')
|
||||
return
|
||||
}
|
||||
const item = resolveBatchServerItem(scope, ids[0])
|
||||
if (!item) {
|
||||
snackbar('未找到所选服务器', 'error')
|
||||
return
|
||||
}
|
||||
await openAgentDiagnose(item)
|
||||
}
|
||||
|
||||
async function openAgentDiagnose(item: ServerApiItem) {
|
||||
agentDiagnoseTarget.value = item
|
||||
agentDiagnoseResult.value = null
|
||||
showAgentDiagnose.value = true
|
||||
agentDiagnoseLoading.value = true
|
||||
agentDiagnoseLoadingId.value = item.id
|
||||
try {
|
||||
agentDiagnoseResult.value = await http.post<AgentDiagnoseResult>(`/servers/${item.id}/agent-diagnose`, {})
|
||||
} catch (e: unknown) {
|
||||
@@ -1589,7 +1593,6 @@ async function openAgentDiagnose(item: ServerApiItem) {
|
||||
snackbar(formatApiError(e, 'Agent 诊断失败'), 'error')
|
||||
} finally {
|
||||
agentDiagnoseLoading.value = false
|
||||
agentDiagnoseLoadingId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user