Files
Nexus/frontend/src/pages/CommandsPage.vue
T

169 lines
5.2 KiB
Vue
Raw Normal View History

<template>
<v-container fluid class="pa-6">
<v-card elevation="0" border rounded="lg">
<v-card-title class="d-flex align-center">
命令日志
<v-spacer />
<v-select
v-model="viewMode"
:items="[{ title: '命令视图', value: 'commands' }, { title: '会话视图', value: 'sessions' }]"
item-title="title"
item-value="value"
variant="outlined"
density="compact"
hide-details
style="max-width: 160px"
@update:model-value="loadData"
/>
<v-select
v-model="serverFilter"
:items="serverList"
item-title="name"
item-value="id"
label="服务器"
variant="outlined"
density="compact"
hide-details
style="max-width: 180px"
class="ml-3"
clearable
@update:model-value="loadData"
/>
</v-card-title>
<v-divider />
<!-- Command View -->
<v-data-table-server
v-if="viewMode === 'commands'"
:items="commands"
:headers="commandHeaders"
:items-length="total"
:loading="loading"
:page="page"
:items-per-page="30"
hover
density="compact"
@update:page="page = $event; loadData()"
>
<template #item.command="{ item }">
<code class="text-body-2">{{ item.command }}</code>
</template>
<template #item.server_name="{ item }">
<span class="text-medium-emphasis">{{ item.server_name || '—' }}</span>
</template>
<template #item.admin_username="{ item }">
<span class="text-medium-emphasis">{{ item.admin_username || '—' }}</span>
</template>
<template #item.created_at="{ item }">
<span class="text-caption text-medium-emphasis">{{ item.created_at }}</span>
</template>
<template #no-data>
<div class="text-center text-medium-emphasis py-6">暂无数据</div>
</template>
</v-data-table-server>
<!-- Session View -->
<v-data-table-server
v-else
:items="sessions"
:headers="sessionHeaders"
:items-length="total"
:loading="loading"
:page="page"
:items-per-page="30"
hover
density="compact"
@update:page="page = $event; loadData()"
>
<template #item.server_name="{ item }">
<span class="font-weight-medium">{{ item.server_name || '—' }}</span>
</template>
<template #item.status="{ item }">
<v-chip :color="item.status === 'closed' ? 'grey' : 'success'" size="small" variant="tonal" label>
{{ item.status }}
</v-chip>
</template>
<template #item.started_at="{ item }">
<span class="text-caption text-medium-emphasis">{{ item.started_at }}</span>
</template>
<template #no-data>
<div class="text-center text-medium-emphasis py-6">暂无数据</div>
</template>
</v-data-table-server>
</v-card>
</v-container>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { http } from '@/api'
import { useServerList } from '@/composables/useServerList'
import { useSnackbar } from '@/composables/useSnackbar'
import { formatApiError } from '@/utils/apiError'
import type { CommandLogItem, SshSessionItem } from '@/types/api'
const snackbar = useSnackbar()
const { servers: serverList, loadServers } = useServerList()
// ── State ──
const loading = ref(false)
const viewMode = ref<'commands' | 'sessions'>('commands')
const page = ref(1)
const total = ref(0)
const itemsPerPage = ref(30)
const serverFilter = ref<number | null>(null)
const commands = ref<CommandLogItem[]>([])
const sessions = ref<SshSessionItem[]>([])
const commandHeaders = [
{ title: '命令', key: 'command' },
{ title: '服务器', key: 'server_name' },
{ title: '用户', key: 'admin_username', width: 100 },
{ title: '时间', key: 'created_at', width: 160 },
]
const sessionHeaders = [
{ title: '服务器', key: 'server_name' },
{ title: '状态', key: 'status', width: 80 },
{ title: '来源', key: 'remote_addr', width: 120 },
{ title: '开始时间', key: 'started_at', width: 160 },
]
// ── Data loading ──
async function loadData() {
loading.value = true
try {
if (viewMode.value === 'commands') {
const res = await http.getList<CommandLogItem>('/assets/command-logs', {
server_id: serverFilter.value || undefined,
page: page.value,
per_page: itemsPerPage.value,
})
commands.value = res.items
total.value = res.total
} else {
const res = await http.getList<SshSessionItem>('/assets/ssh-sessions', {
server_id: serverFilter.value || undefined,
page: page.value,
per_page: itemsPerPage.value,
})
sessions.value = res.items
total.value = res.total
}
} catch (e: unknown) {
commands.value = []
sessions.value = []
snackbar(formatApiError(e, '加载命令日志失败'), 'error')
} finally {
loading.value = false
}
}
onMounted(() => {
loadServers()
loadData()
})
</script>