Files
opsx-vpn/frontend/src/views/ClientDevices.vue
T

78 lines
3.0 KiB
Vue

<template>
<el-card class="page-card" shadow="never">
<template #header>
<div class="vpn-header">
<div>
<b>{{ $t('clientDevices.title') }}</b>
<span style="margin-left:8px;color:#909399;font-size:12px">
{{ $t('clientDevices.hint') }}
</span>
</div>
<el-button :icon="Refresh" circle :loading="loading" @click="load" />
</div>
</template>
<el-table :data="devices" size="small" v-loading="loading">
<el-table-column prop="username" :label="$t('common.username')" width="140" />
<el-table-column prop="name" :label="$t('clientDevices.deviceName')" min-width="150" />
<el-table-column prop="platform" :label="$t('clientDevices.platform')" width="100" />
<el-table-column prop="tunnel_name" :label="$t('clientDevices.tunnel')" min-width="140" />
<el-table-column prop="client_address" :label="$t('clientDevices.address')" width="130" />
<el-table-column :label="$t('common.traffic')" width="170">
<template #default="{ row }">{{ fmtBytes(row.rx_bytes) }} / {{ fmtBytes(row.tx_bytes) }}</template>
</el-table-column>
<el-table-column :label="$t('clientDevices.lastHandshake')" width="170">
<template #default="{ row }">{{ row.last_seen_at ? new Date(row.last_seen_at).toLocaleString() : '-' }}</template>
</el-table-column>
<el-table-column :label="$t('common.status')" width="90">
<template #default="{ row }">
<el-tag :type="row.revoked ? 'info' : 'success'" size="small">
{{ row.revoked ? $t('clientDevices.revoked') : $t('common.enabled') }}
</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('common.operation')" width="110" fixed="right">
<template #default="{ row }">
<el-popconfirm v-if="!row.revoked" :title="$t('clientDevices.revokeConfirm')" @confirm="revoke(row)">
<template #reference>
<el-button type="danger" size="small" plain>{{ $t('clientDevices.revoke') }}</el-button>
</template>
</el-popconfirm>
<span v-else style="color:#c0c4cc">—</span>
</template>
</el-table-column>
<template #empty>{{ $t('clientDevices.empty') }}</template>
</el-table>
</el-card>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { Refresh } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
import api from '../api'
import { fmtBytes } from '../utils/format'
const { t } = useI18n()
const devices = ref([])
const loading = ref(false)
async function load() {
loading.value = true
try {
const { data } = await api.get('/client-devices')
devices.value = data.devices || []
} finally {
loading.value = false
}
}
async function revoke(row) {
await api.post(`/client-devices/${row.id}/revoke`)
ElMessage.success(t('clientDevices.revokeDone'))
load()
}
onMounted(load)
</script>