feat: 离线License批量删除;同步循环加固(启动即跑/pending自动握手/防重入/panic恢复,移至main启动);同步时间显示到秒
This commit is contained in:
1 parent
30b87dec62
commit
252ae14aef
5 files changed
+79
-11
No files matched your search
@@ -9,6 +9,9 @@
|
||||
</el-select>
|
||||
</div>
|
||||
<div>
|
||||
<el-button type="danger" plain :disabled="!selectedRows.length" @click="batchDelete">
|
||||
批量删除{{ selectedRows.length ? `(${selectedRows.length})` : '' }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="openIssue">签发离线序列号</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -21,7 +24,8 @@
|
||||
style="margin-bottom: 12px"
|
||||
/>
|
||||
|
||||
<el-table :data="list" v-loading="loading" stripe>
|
||||
<el-table :data="list" v-loading="loading" stripe @selection-change="onSelectionChange">
|
||||
<el-table-column type="selection" width="48" />
|
||||
<el-table-column label="序列号" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<div class="serial-cell">
|
||||
@@ -112,9 +116,35 @@
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getLicenses, issueOfflineLicense, revokeLicense } from '@/api/license'
|
||||
import { getLicenses, issueOfflineLicense, revokeLicense, batchDeleteLicenses } from '@/api/license'
|
||||
|
||||
const list = ref([])
|
||||
const selectedRows = ref([])
|
||||
|
||||
// 仅已吊销 / 已过期可删除(与在线 License 一致) // Logan.Li
|
||||
function canDelete(row) {
|
||||
return row.status === 'revoked' || row.status === 'expired' || row.days_left <= 0
|
||||
}
|
||||
|
||||
function onSelectionChange(rows) {
|
||||
selectedRows.value = rows || []
|
||||
}
|
||||
|
||||
async function batchDelete() {
|
||||
const ids = selectedRows.value.map((r) => r.id).filter(Boolean)
|
||||
if (!ids.length) return
|
||||
const deletable = selectedRows.value.filter(canDelete).length
|
||||
await ElMessageBox.confirm(
|
||||
`已选 ${ids.length} 条,其中约 ${deletable} 条可删(仅已吊销/已过期);其余将跳过。确认继续?`,
|
||||
'批量删除',
|
||||
{ type: 'warning' },
|
||||
)
|
||||
const res = await batchDeleteLicenses(ids)
|
||||
const data = res.data || {}
|
||||
ElMessage.success(`删除 ${data.deleted || 0} 条,跳过 ${data.skipped || 0} 条${data.failed ? `,失败 ${data.failed}` : ''}`)
|
||||
selectedRows.value = []
|
||||
loadData()
|
||||
}
|
||||
const loading = ref(false)
|
||||
const keyword = ref('')
|
||||
const statusFilter = ref('')
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
<el-table-column label="密钥尾号" width="90">
|
||||
<template #default="{ row }"><code>…{{ row.key_tail }}</code></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="last_sync_at" label="上次同步" width="160" />
|
||||
<el-table-column label="上次同步" width="160">
|
||||
<template #default="{ row }">{{ formatTime(row.last_sync_at) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="last_result" label="最近结果" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="240" fixed="right">
|
||||
<template #default="{ row }">
|
||||
@@ -129,6 +131,15 @@ async function doDelete(row) {
|
||||
await load()
|
||||
}
|
||||
|
||||
// formatTime 时间展示精确到秒(后端返回带纳秒的 RFC3339) // Logan.Li
|
||||
function formatTime(v) {
|
||||
if (!v) return '—'
|
||||
const d = new Date(v)
|
||||
if (Number.isNaN(d.getTime())) return v
|
||||
const p = (n) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
|
||||
Reference in new issue
Block a user