feat(cost-center): 支持删除第三方卡券商品
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
* 商品编码固定为 `tpv:<sku>`;成本写入 `sys_third_party_voucher_cost` 的
|
||||
* 商品级版本,由成本快照管道消费。
|
||||
*/
|
||||
import { ReloadOutlined, SyncOutlined } from '@ant-design/icons'
|
||||
import { DeleteOutlined, ReloadOutlined, SyncOutlined } from '@ant-design/icons'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Input,
|
||||
InputNumber,
|
||||
Modal,
|
||||
Popconfirm,
|
||||
Space,
|
||||
Tag,
|
||||
Typography,
|
||||
@@ -22,11 +23,13 @@ import { useState } from 'react'
|
||||
|
||||
import PercentTable from '@/components/common/PercentTable'
|
||||
import {
|
||||
deleteAdminVoucherProduct,
|
||||
ensureVoucherChannelRules,
|
||||
fetchAdminProductCatalog,
|
||||
setAdminProductPrice,
|
||||
} from '@/services/admin'
|
||||
import type { ProductCatalogItem } from '@/types/admin'
|
||||
import { formatAdminDateTime } from '@/utils/admin-time'
|
||||
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
|
||||
import { useCostCenter } from './CostCenterContext'
|
||||
|
||||
@@ -65,6 +68,20 @@ export default function VoucherSkuPanel() {
|
||||
onError: (error) => message.error(error instanceof Error ? error.message : '保存失败'),
|
||||
})
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: (productId: number) => deleteAdminVoucherProduct(productId),
|
||||
onSuccess: (response) => {
|
||||
message.success(
|
||||
response.data.action === 'archived'
|
||||
? '该商品已有历史引用,已归档并停用后续卡券规则;历史成本已保留'
|
||||
: '卡券商品及采购价配置已删除',
|
||||
)
|
||||
void queryClient.invalidateQueries({ queryKey: ['cost-center-voucher-products'] })
|
||||
refreshAll()
|
||||
},
|
||||
onError: (error) => message.error(error instanceof Error ? error.message : '删除失败'),
|
||||
})
|
||||
|
||||
async function handleEnsureRules() {
|
||||
setEnsuringRules(true)
|
||||
try {
|
||||
@@ -99,28 +116,50 @@ export default function VoucherSkuPanel() {
|
||||
title: '生效时间',
|
||||
dataIndex: 'activeCostEffectiveFrom',
|
||||
width: 180,
|
||||
render: (value?: string | null) => value || '—',
|
||||
render: (value?: string | null) =>
|
||||
value ? <span style={{ whiteSpace: 'nowrap' }}>{formatAdminDateTime(value)}</span> : '—',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 110,
|
||||
width: 180,
|
||||
fixed: 'right' as const,
|
||||
render: (_: unknown, row: ProductCatalogItem) => (
|
||||
<Button
|
||||
size="small"
|
||||
disabled={!canConfig}
|
||||
onClick={() => {
|
||||
setSelected(row)
|
||||
form.setFieldsValue({
|
||||
amountFen:
|
||||
row.activeCostAmountFen === null || row.activeCostAmountFen === undefined
|
||||
? 0
|
||||
: Number((row.activeCostAmountFen / 100).toFixed(2)),
|
||||
changeReason: '第三方卡券采购价调整',
|
||||
})
|
||||
}}
|
||||
>
|
||||
改价
|
||||
</Button>
|
||||
<Space size={4}>
|
||||
<Button
|
||||
size="small"
|
||||
disabled={!canConfig}
|
||||
onClick={() => {
|
||||
setSelected(row)
|
||||
form.setFieldsValue({
|
||||
amountFen:
|
||||
row.activeCostAmountFen === null || row.activeCostAmountFen === undefined
|
||||
? 0
|
||||
: Number((row.activeCostAmountFen / 100).toFixed(2)),
|
||||
changeReason: '第三方卡券采购价调整',
|
||||
})
|
||||
}}
|
||||
>
|
||||
改价
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="删除该第三方卡券商品?"
|
||||
description="没有历史引用时会彻底删除;已有历史订单或成本快照时会自动归档并停用后续卡券规则,历史成本保留。"
|
||||
okText="确认删除"
|
||||
cancelText="取消"
|
||||
okButtonProps={{ danger: true }}
|
||||
onConfirm={() => deleteMutation.mutate(row.id)}
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
disabled={!canConfig}
|
||||
loading={deleteMutation.isPending && deleteMutation.variables === row.id}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
@@ -555,3 +555,17 @@ export function ensureVoucherChannelRules() {
|
||||
'/api/v1/admin/cost-center/voucher-products/ensure-rules',
|
||||
)
|
||||
}
|
||||
|
||||
export function deleteAdminVoucherProduct(productId: number) {
|
||||
return apiDelete<{
|
||||
action: 'deleted' | 'archived'
|
||||
deleted: boolean
|
||||
product: ProductCatalogItem
|
||||
deletedCostVersionCount?: number
|
||||
preserved: {
|
||||
mappingCount: number
|
||||
snapshotCount: number
|
||||
orderItemCount: number
|
||||
}
|
||||
}>(`/api/v1/admin/cost-center/voucher-products/${productId}`)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import test from 'node:test'
|
||||
|
||||
test('第三方卡券面板提供安全删除入口并格式化生效时间', async () => {
|
||||
const [panel, service] = await Promise.all([
|
||||
readFile(
|
||||
new URL('../src/pages/admin/cost-center/VoucherSkuPanel.tsx', import.meta.url),
|
||||
'utf8',
|
||||
),
|
||||
readFile(new URL('../src/services/admin/cost-center.ts', import.meta.url), 'utf8'),
|
||||
])
|
||||
|
||||
assert.ok(panel.includes('deleteAdminVoucherProduct'))
|
||||
assert.ok(panel.includes('DeleteOutlined'))
|
||||
assert.ok(panel.includes('Popconfirm'))
|
||||
assert.ok(panel.includes('已有历史订单或成本快照时会自动归档'))
|
||||
assert.ok(panel.includes('formatAdminDateTime(value)'))
|
||||
assert.ok(service.includes('/api/v1/admin/cost-center/voucher-products/${productId}'))
|
||||
assert.ok(service.includes("action: 'deleted' | 'archived'"))
|
||||
})
|
||||
Reference in New Issue
Block a user