feat: 产品/客户/订单批量删除;接入 GeoLite2 城市库(地理分布/激活记录/审计日志显示归属地);仪表盘趋势图美化

This commit is contained in:
Logan.Li committed 2026-09-14 22:48:14 +08:00
1 parent 252ae14aef
commit 473dcd0aab
24 files changed
+431 -34

No files matched your search

+31 -3
View File
@@ -3,10 +3,16 @@
<el-card shadow="never">
<div class="toolbar">
<el-input v-model="keyword" placeholder="搜索产品名称/编码" clearable style="width: 240px" @keyup.enter="loadData" @clear="loadData" />
<el-button type="primary" @click="dialogVisible = true">新增产品</el-button>
<div>
<el-button type="danger" plain :disabled="!selectedRows.length" @click="batchDelete">
批量删除{{ selectedRows.length ? `(${selectedRows.length})` : '' }}
</el-button>
<el-button type="primary" @click="dialogVisible = true">新增产品</el-button>
</div>
</div>
<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="name" label="产品名称" min-width="140" />
<el-table-column prop="code" label="编码" width="120" />
<el-table-column prop="version" label="版本" width="90" />
@@ -67,7 +73,29 @@
<script setup>
import { ref, reactive } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getProducts, createProduct, updateProduct, deleteProduct, updateProductStatus } from '@/api/product'
const selectedRows = ref([])
function onSelectionChange(rows) {
selectedRows.value = rows || []
}
// 批量删除:被 License 引用的产品由后端自动跳过 // Logan.Li
async function batchDelete() {
const ids = selectedRows.value.map((r) => r.id).filter(Boolean)
if (!ids.length) return
await ElMessageBox.confirm(
`已选 ${ids.length} 个产品;被 License 引用的将自动跳过,未引用的连同其定价计划一并删除。确认继续?`,
'批量删除',
{ type: 'warning' },
)
const res = await batchDeleteProducts(ids)
const data = res.data || {}
ElMessage.success(`删除 ${data.deleted || 0} 条,跳过 ${data.skipped || 0} 条${data.failed ? `,失败 ${data.failed}` : ''}`)
selectedRows.value = []
loadData()
}
import { getProducts, createProduct, updateProduct, deleteProduct, updateProductStatus, batchDeleteProducts } from '@/api/product'
const list = ref([])
const loading = ref(false)