From da80b28ba499380b953fd1279ff508eda86c5274 Mon Sep 17 00:00:00 2001 From: jacy-dukang Date: Mon, 6 Jul 2026 16:34:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=95=86=E5=93=81=E5=88=A0=E9=99=A4=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=88=E9=9C=80=E8=A6=81=E4=BA=8C=E6=AC=A1=E7=A1=AE?= =?UTF-8?q?=E8=AE=A4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/admin-web/src/pages/ProductsPage.tsx | 44 +++++++++++++++---- .../modules/ops/admin-products.controller.ts | 7 ++- .../src/modules/ops/admin-products.service.ts | 17 +++++++ 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/apps/admin-web/src/pages/ProductsPage.tsx b/apps/admin-web/src/pages/ProductsPage.tsx index 2db87d6..00b1c43 100644 --- a/apps/admin-web/src/pages/ProductsPage.tsx +++ b/apps/admin-web/src/pages/ProductsPage.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { - Button, Descriptions, Divider, Drawer, Form, Input, InputNumber, Modal, Select, Space, + Button, Descriptions, Divider, Drawer, Form, Input, InputNumber, Modal, Popconfirm, Select, Space, Table, Tabs, Tag, Typography, message, } from 'antd'; import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; @@ -244,6 +244,20 @@ export default function ProductsPage() { const [drawerOpen, setDrawerOpen] = useState(false); const [createOpen, setCreateOpen] = useState(false); + async function handleDelete(row: Row) { + try { + await request(`/admin/products/${row.id}`, { method: 'DELETE' }); + message.success('已删除'); + if (detail?.id === row.id) { + setDrawerOpen(false); + setDetail(null); + } + void reload(); + } catch (e) { + message.error(e instanceof Error ? e.message : '删除失败'); + } + } + const columns: ColumnsType = [ { title: 'SKU', dataIndex: 'skuCode', width: 90 }, { title: '商品名', dataIndex: 'name', width: 180, ellipsis: true }, @@ -255,14 +269,26 @@ export default function ProductsPage() { { title: '排序', dataIndex: 'sortOrder', width: 60 }, { title: '创建', dataIndex: 'createdAt', width: 160, render: fmtTime }, { - title: '操作', width: 80, + title: '操作', width: 120, render: (_, row) => ( - + + + handleDelete(row)} + > + + + ), }, ]; @@ -283,7 +309,7 @@ export default function ProductsPage() { - { setPage(p); setPageSize(ps); } }} /> setDrawerOpen(false)} extra={detail && ( diff --git a/server/dukang-api/src/modules/ops/admin-products.controller.ts b/server/dukang-api/src/modules/ops/admin-products.controller.ts index efbea21..9bf3dd6 100644 --- a/server/dukang-api/src/modules/ops/admin-products.controller.ts +++ b/server/dukang-api/src/modules/ops/admin-products.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Get, Param, Post, Put, Query, UseGuards } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Post, Put, Query, UseGuards } from '@nestjs/common'; import { HqAuthGuard } from '../../common/guards/hq-auth.guard'; import { AdminProductsService } from './admin-products.service'; import { AdminProductsQueryDto } from './dto/admin-query.dto'; @@ -28,4 +28,9 @@ export class AdminProductsController { update(@Param('id') id: string, @Body() dto: UpdateProductDto) { return this.service.update(BigInt(id), dto); } + + @Delete(':id') + remove(@Param('id') id: string) { + return this.service.remove(BigInt(id)); + } } diff --git a/server/dukang-api/src/modules/ops/admin-products.service.ts b/server/dukang-api/src/modules/ops/admin-products.service.ts index ce93dbe..7f0502c 100644 --- a/server/dukang-api/src/modules/ops/admin-products.service.ts +++ b/server/dukang-api/src/modules/ops/admin-products.service.ts @@ -135,6 +135,23 @@ export class AdminProductsService { return this.detail(id); } + async remove(id: bigint) { + const product = await this.prisma.commonProductItem.findUnique({ where: { id } }); + if (!product) throw new NotFoundException('商品不存在'); + + const orderCount = await this.prisma.order.count({ where: { productId: id } }); + if (orderCount > 0) { + throw new BadRequestException(`该商品已有 ${orderCount} 笔关联订单,无法删除`); + } + + await this.prisma.commonResource.deleteMany({ + where: { ownerType: 'PRODUCT', ownerId: id }, + }); + await this.prisma.commonProductItem.delete({ where: { id } }); + + return { ok: true }; + } + private formatProduct( product: Prisma.CommonProductItemGetPayload<{ include: { coverResource: true } }>, extraResources: Prisma.CommonResourceGetPayload[],