From 62e61a9e63d56e922ea02dcb8b957f450d15674e Mon Sep 17 00:00:00 2001
From: jacy <18049821889@163.com>
Date: Fri, 21 Aug 2026 16:22:53 +0800
Subject: [PATCH] =?UTF-8?q?v3.5.4=E7=89=88=E6=9C=AC=E6=8F=90=E4=BA=A4?=
=?UTF-8?q?=EF=BC=8C=E4=BF=AE=E6=94=B9api?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/components/ProductSpecsEditor.tsx | 28 +++-
apps/admin-web/src/pages/InvoicesPage.tsx | 33 +++-
apps/admin-web/src/pages/ProductsPage.tsx | 13 +-
.../src/pages/invoice-apply/index.tsx | 66 +++++---
.../src/pages/invoice-titles/index.tsx | 47 +++++-
apps/mini-user/src/pages/pay/index.tsx | 18 +--
apps/mini-user/src/styles/invoice.css | 24 +++
apps/mini-user/src/styles/order.css | 6 +
packages/shared-types/src/catalog.ts | 1 +
packages/shared-types/src/invoice.ts | 9 ++
.../prisma/backfill-product-skus.ts | 35 ++++-
.../prisma/migrate-invoice-category.sql | 5 +
.../dukang-api/prisma/rewrite-sku-codes-dk.ts | 147 ++++++++++++++++++
server/dukang-api/prisma/schema.prisma | 8 +
.../src/modules/catalog/catalog.service.ts | 8 +-
.../src/modules/catalog/product-sku.util.ts | 72 ++++++++-
.../src/modules/ops/admin-products.service.ts | 55 +++++--
.../src/modules/ops/dto/admin-mutate.dto.ts | 1 +
.../src/modules/trade/dto/after-sale.dto.ts | 6 +
.../modules/trade/invoice-title.service.ts | 11 +-
.../src/modules/trade/trade.service.ts | 143 +++++++++--------
21 files changed, 594 insertions(+), 142 deletions(-)
create mode 100644 server/dukang-api/prisma/migrate-invoice-category.sql
create mode 100644 server/dukang-api/prisma/rewrite-sku-codes-dk.ts
diff --git a/apps/admin-web/src/components/ProductSpecsEditor.tsx b/apps/admin-web/src/components/ProductSpecsEditor.tsx
index 84fccba..60f7821 100644
--- a/apps/admin-web/src/components/ProductSpecsEditor.tsx
+++ b/apps/admin-web/src/components/ProductSpecsEditor.tsx
@@ -157,8 +157,7 @@ export default function ProductSpecsEditor({ productId, initialAttrs, initialSku
const payloadSkus = skus.map((s, i) => ({
id: s.id,
specValueIds: resolveIds(s.specValueIds ?? []),
- skuCode: s.skuCode,
- barcode69: s.barcode69,
+ barcode69: s.barcode69.trim(),
price: s.price,
benefitAmount: s.benefitAmount,
status: s.status,
@@ -171,8 +170,10 @@ export default function ProductSpecsEditor({ productId, initialAttrs, initialSku
sortOrder: i,
}));
- for (const row of payloadSkus) {
- if (!row.barcode69?.trim()) throw new Error('请填写全部 SKU 的 69 码');
+ const barcodes = payloadSkus.map((row) => row.barcode69);
+ if (barcodes.some((b) => !b)) throw new Error('每个规格须填写独立 69 码');
+ if (new Set(barcodes).size !== barcodes.length) {
+ throw new Error('同一商品内 69 码不可重复,每个规格请填写不同 69 码');
}
await request(`/admin/products/${productId}/skus`, {
@@ -191,7 +192,8 @@ export default function ProductSpecsEditor({ productId, initialAttrs, initialSku
return (
- 先配置销售规格轴(如「包装」),再生成 SKU 矩阵。未配置规格时仅保留默认一行 SKU,与基础信息价/码同步。
+ SKU 码由系统自动生成(DK 开头),无需填写。每个规格必须填写互不相同的 69 码。
+ 先配置销售规格轴(如「包装」),再生成 SKU 矩阵;未配置规格时仅保留默认一行。
规格轴
@@ -272,7 +274,7 @@ export default function ProductSpecsEditor({ productId, initialAttrs, initialSku
size="small"
rowKey={(_, i) => String(i)}
pagination={false}
- scroll={{ x: 1100 }}
+ scroll={{ x: 1240 }}
dataSource={skus}
columns={[
{
@@ -281,10 +283,20 @@ export default function ProductSpecsEditor({ productId, initialAttrs, initialSku
render: (_, row) => row.specText || (row.specValueIds?.length ? row.specValueIds.join(',') : '默认'),
},
{
- title: '69码',
- width: 140,
+ title: 'SKU',
+ width: 110,
+ render: (_, row) => (
+
+ {row.skuCode || '保存后自动生成'}
+
+ ),
+ },
+ {
+ title: 69码 *,
+ width: 180,
render: (_, row, index) => (
{
const next = [...skus];
diff --git a/apps/admin-web/src/pages/InvoicesPage.tsx b/apps/admin-web/src/pages/InvoicesPage.tsx
index 0f6e018..22dbc4a 100644
--- a/apps/admin-web/src/pages/InvoicesPage.tsx
+++ b/apps/admin-web/src/pages/InvoicesPage.tsx
@@ -17,9 +17,11 @@ import {
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import {
+ INVOICE_CATEGORY_LABELS,
INVOICE_KIND_LABELS,
INVOICE_STATUS_LABELS,
INVOICE_TITLE_TYPE_LABELS,
+ type InvoiceCategory,
type InvoiceKind,
type InvoiceStatus,
type InvoiceTitleType,
@@ -35,6 +37,7 @@ type Row = {
orderNo?: string;
titleType: InvoiceTitleType;
invoiceKind: InvoiceKind;
+ invoiceCategory?: InvoiceCategory;
titleName: string;
status: InvoiceStatus;
overdue?: boolean;
@@ -54,6 +57,7 @@ type CreateFormValues = {
orderNo: string;
titleType: InvoiceTitleType;
invoiceKind: InvoiceKind;
+ invoiceCategory?: InvoiceCategory;
titleName: string;
taxNo?: string;
addressPhone?: string;
@@ -156,6 +160,7 @@ export default function InvoicesPage() {
orderNo: values.orderNo.trim(),
titleType: values.titleType,
invoiceKind: values.invoiceKind,
+ invoiceCategory: values.invoiceCategory,
titleName: values.titleName.trim(),
taxNo: values.taxNo?.trim() || undefined,
addressPhone: values.addressPhone?.trim() || undefined,
@@ -186,9 +191,17 @@ export default function InvoicesPage() {
},
{
title: '票种',
- width: 120,
+ width: 140,
render: (_, r) => INVOICE_KIND_LABELS[r.invoiceKind] ?? r.invoiceKind,
},
+ {
+ title: '类型',
+ width: 88,
+ render: (_, r) =>
+ r.invoiceCategory
+ ? INVOICE_CATEGORY_LABELS[r.invoiceCategory] ?? r.invoiceCategory
+ : '—',
+ },
{ title: '名称', dataIndex: 'titleName', ellipsis: true },
{
title: '状态',
@@ -233,6 +246,7 @@ export default function InvoicesPage() {
createForm.setFieldsValue({
titleType: 'PERSONAL',
invoiceKind: 'NORMAL',
+ invoiceCategory: 'LIQUOR',
});
setCreateOpen(true);
}}
@@ -324,6 +338,11 @@ export default function InvoicesPage() {
{INVOICE_KIND_LABELS[detail.invoiceKind]}
+
+ {detail.invoiceCategory
+ ? INVOICE_CATEGORY_LABELS[detail.invoiceCategory]
+ : '—'}
+
{detail.titleName}
{detail.taxNo ?? '—'}
{detail.addressPhone ?? '—'}
@@ -382,6 +401,18 @@ export default function InvoicesPage() {
}}
/>
+
+
-
-
+
+