feat: 离线License批量删除;同步循环加固(启动即跑/pending自动握手/防重入/panic恢复,移至main启动);同步时间显示到秒

This commit is contained in:
Logan.Li committed 2026-09-14 22:08:43 +08:00
1 parent 30b87dec62
commit 252ae14aef
5 files changed
+79 -11

No files matched your search

+32 -2
View File
@@ -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('')
+12 -1
View File
@@ -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>