feat: License 支持筛选多选批量删除

仅已吊销或已过期可删,有效授权自动跳过;单条删除同步限制。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-12 21:53:34 +08:00
1 parent f26e787cfc
commit d0e1ca3729
6 files changed
+152 -9

No files matched your search

+4
View File
@@ -22,6 +22,10 @@ export function deleteLicense(id) {
return request({ url: `/licenses/${id}`, method: 'delete' })
}
export function batchDeleteLicenses(ids) {
return request({ url: '/licenses/batch-delete', method: 'post', data: { ids } })
}
export function renewLicense(id, duration) {
return request({ url: `/licenses/${id}/renew`, method: 'post', data: { duration } })
}
+54 -4
View File
@@ -12,6 +12,12 @@
</el-select>
</div>
<div>
<el-button
type="danger"
plain
:disabled="selectedRows.length === 0"
@click="batchDelete"
>批量删除</el-button>
<el-button @click="exportData">导出 CSV</el-button>
<el-button type="primary" @click="openCreate">生成 License</el-button>
</div>
@@ -21,11 +27,12 @@
type="info"
:closable="false"
show-icon
title="本页默认仅展示在线 License。离线序列号请前往「授权管理 → 离线 License」。"
title="本页默认仅展示在线 License。离线序列号请前往「授权管理 → 离线 License」。仅「已吊销 / 已过期」可删除;有效授权会自动跳过。"
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 prop="license_key" label="License Key" min-width="220" />
<el-table-column prop="customer_name" label="客户" min-width="100" />
<el-table-column prop="product_name" label="产品" min-width="100" />
@@ -43,7 +50,7 @@
<el-table-column label="激活" width="80">
<template #default="{ row }">{{ row.current_activations }}/{{ row.max_activations }}</template>
</el-table-column>
<el-table-column label="操作" width="280" fixed="right">
<el-table-column label="操作" width="320" fixed="right">
<template #default="{ row }">
<el-button size="small" @click="viewDetail(row)">详情</el-button>
<el-button size="small" @click="renew(row)">续期</el-button>
@@ -51,6 +58,13 @@
{{ row.status === 'suspended' ? '恢复' : '暂停' }}
</el-button>
<el-button size="small" type="danger" @click="revoke(row)">吊销</el-button>
<el-button
v-if="canDelete(row)"
size="small"
type="danger"
plain
@click="removeOne(row)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -126,7 +140,7 @@ import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
getLicenses, createLicense, renewLicense, revokeLicense, suspendLicense,
activateLicense, getLicense, forceOffline, exportLicenses,
activateLicense, getLicense, forceOffline, exportLicenses, deleteLicense, batchDeleteLicenses,
} from '@/api/license'
import { getAllProducts } from '@/api/product'
import { getAllCustomers } from '@/api/customer'
@@ -141,6 +155,7 @@ const productFilter = ref('')
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const selectedRows = ref([])
const statusOptions = [
{ label: '未激活', value: 'inactive' }, { label: '已激活', value: 'active' },
@@ -157,6 +172,15 @@ function statusTag(s) {
return { active: 'success', inactive: 'info', expired: 'warning', revoked: 'danger', suspended: 'warning' }[s] || ''
}
// 仅已吊销 / 已过期(含剩余天数<=0)可删除 // Logan.Li
function canDelete(row) {
return row.status === 'revoked' || row.status === 'expired' || row.days_left <= 0
}
function onSelectionChange(rows) {
selectedRows.value = rows || []
}
const createVisible = ref(false)
const formRef = ref(null)
const form = reactive({ customer_id: null, product_id: null, duration: 365, max_activations: 1, count: 1, notes: '' })
@@ -220,6 +244,32 @@ async function revoke(row) {
loadData()
}
async function removeOne(row) {
if (!canDelete(row)) {
ElMessage.warning('仅已吊销或已过期的 License 可删除')
return
}
await ElMessageBox.confirm(`确认删除 License「${row.license_key}」?此操作不可恢复。`, '删除', { type: 'warning' })
await deleteLicense(row.id)
loadData()
}
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()
}
async function toggle(row, status) {
if (status === 'active') await activateLicense(row.id)
else await suspendLicense(row.id)