feat(product-edit):商品编辑页初版

This commit is contained in:
R524809
2026-09-09 15:11:58 +08:00
parent 319887726e
commit 130a005231
19 changed files with 1541 additions and 21 deletions
+1
View File
@@ -3,3 +3,4 @@ export * from './auth';
export * from './stores';
export * from './watermarks';
export * from './media';
export * from './product-editor';
+127
View File
@@ -0,0 +1,127 @@
import { z } from 'zod';
/**
* M3.0 editor entry and read-model contracts. Draft mutation, pricing, and
* publishing DTOs intentionally stay out of this first editor slice.
*/
export type ProductEditorContext = {
collection_item_id: string;
product_id: string;
profile_id: string;
target_store: { id: string; name: string; currency_code: string };
editor_path: string;
};
export type ProductEditorSnapshot = {
product: {
id: string;
revision: number;
common: {
source_title: string | null;
brand: string | null;
model: string | null;
barcode: string | null;
purchase_url: string | null;
supply_note: string | null;
};
};
profile: {
id: string;
revision: number;
platform_id: string;
market_code: string | null;
fulfillment: string;
target_store: { id: string; name: string; currency_code: string };
title: string | null;
description: string | null;
tags: string[];
category: {
id: string | null;
platform_type_id: string | null;
source: string | null;
};
};
variants: Array<{
id: string;
source_variant_key: string | null;
source_sku: string | null;
display_name: string | null;
sort_order: number;
purchase_cost_cny: string | null;
listing: {
id: string;
offer_id: string | null;
status: string;
selling_price: string | null;
list_price: string | null;
currency_code: string | null;
};
}>;
attributes: Array<{
id: string;
scope: 'common' | 'variant';
variant_id: string | null;
platform_attribute_id: string;
complex_id: string;
values: unknown;
language_code: string | null;
source: string;
confirmation_status: string;
}>;
unmapped_attributes: Array<{
id: string;
variant_id: string | null;
original_platform_category_id: string | null;
original_platform_attribute_id: string;
original_complex_id: string;
original_name: string;
values: unknown;
language_code: string | null;
reason: string;
status: string;
}>;
warnings: Array<{ code: string; message: string }>;
};
const uuid = z.uuid();
const nullableText = (max: number) => z.string().trim().max(max).nullable();
/** M3.1 intentionally covers editable product/profile/SKU fundamentals. */
export const saveProductEditorDraftRequestSchema = z.object({
common: z.object({
source_title: nullableText(1_000),
brand: nullableText(300),
model: nullableText(300),
barcode: nullableText(300),
purchase_url: z.url().nullable(),
supply_note: nullableText(4_000),
}).strict(),
profile: z.object({
target_store_id: uuid,
fulfillment: z.enum(['rfbs', 'fbs', 'fbo', 'fbp', 'unknown']),
title: nullableText(1_000),
description: nullableText(100_000),
tags: z.array(z.string().trim().min(1).max(100)).max(30),
}).strict(),
variants: z.array(z.object({
id: uuid.nullable(),
client_key: z.string().trim().min(1).max(100).nullable(),
copy_from_variant_id: uuid.nullable().optional(),
display_name: nullableText(1_000),
source_sku: nullableText(500),
purchase_cost_cny: z.string().regex(/^\d+(\.\d{1,4})?$/).nullable(),
offer_id: nullableText(500),
}).strict().superRefine((variant, context) => {
if (!variant.id && !variant.client_key) {
context.addIssue({ code: 'custom', path: ['client_key'], message: '新 SKU 必须提供 client_key' });
}
})).min(1).max(100),
}).strict();
export type SaveProductEditorDraftRequest = z.infer<typeof saveProductEditorDraftRequestSchema>;
export type SaveProductEditorDraftResponse = {
product_revision: number;
profile_revision: number;
created_variants: Array<{ client_key: string; id: string }>;
warnings: Array<{ code: string; message: string }>;
};