feat(edit): 编辑页类目树逻辑开发

This commit is contained in:
2026-09-10 22:26:52 +08:00
parent 946af649dd
commit 05a00d46b9
13 changed files with 628 additions and 323 deletions
+27 -11
View File
@@ -67,7 +67,7 @@ function formValues(snapshot: ProductEditorSnapshot): EditorFormValues {
title: snapshot.profile.title,
description: snapshot.profile.description,
tags: snapshot.profile.tags,
platform_product_category_id: snapshot.profile.category.id,
store_product_category_id: snapshot.profile.category.id,
category_source: snapshot.profile.category.source as
'source_capture' | 'ai_suggestion' | 'manual' | null,
},
@@ -430,7 +430,8 @@ const ProductEditPage = () => {
const navigate = useNavigate();
const [form] = Form.useForm<EditorFormValues>();
const supplyUrl = Form.useWatch(['common', 'purchase_url'], form);
const selectedCategoryId = Form.useWatch(['profile', 'platform_product_category_id'], form);
const selectedCategoryId = Form.useWatch(['profile', 'store_product_category_id'], form);
const selectedStoreId = Form.useWatch(['profile', 'target_store_id'], form);
const draftAttributes = Form.useWatch('attributes', form) ?? [];
const [snapshot, setSnapshot] = useState<ProductEditorSnapshot | null>(null);
const [collectionRecord, setCollectionRecord] = useState<CollectionItemInspection | null>(null);
@@ -480,7 +481,7 @@ const ProductEditPage = () => {
if (!snapshot || !currentPlatform) return;
let active = true;
void productEditorApi
.categoryOptions(snapshot.profile.id, currentPlatform.id)
.categoryOptions(snapshot.profile.id, currentPlatform.id, undefined, selectedStoreId)
.then((options) => {
if (active) setCategoryOptions(options);
})
@@ -490,7 +491,7 @@ const ProductEditPage = () => {
return () => {
active = false;
};
}, [catalogRefresh, currentPlatform, snapshot]);
}, [catalogRefresh, currentPlatform, selectedStoreId, snapshot]);
useEffect(() => {
if (!snapshot || !currentPlatform || !selectedCategoryId) {
setAttributeSchema([]);
@@ -499,7 +500,7 @@ const ProductEditPage = () => {
}
let active = true;
void productEditorApi
.attributeSchema(snapshot.profile.id, currentPlatform.id, selectedCategoryId)
.attributeSchema(snapshot.profile.id, currentPlatform.id, selectedCategoryId, selectedStoreId)
.then((schema) => {
if (active) setAttributeSchema(schema);
})
@@ -509,7 +510,7 @@ const ProductEditPage = () => {
return () => {
active = false;
};
}, [catalogRefresh, currentPlatform, selectedCategoryId, snapshot]);
}, [catalogRefresh, currentPlatform, selectedCategoryId, selectedStoreId, snapshot]);
useEffect(() => {
if (!snapshot || !currentPlatform || !selectedCategoryId) return;
const dictionaryIds = [
@@ -534,7 +535,8 @@ const ProductEditPage = () => {
currentPlatform.id,
dictionaryId,
undefined,
selectedCategoryId
selectedCategoryId,
selectedStoreId,
),
] as const
)
@@ -551,7 +553,7 @@ const ProductEditPage = () => {
return () => {
active = false;
};
}, [attributeSchema, catalogRefresh, currentPlatform, selectedCategoryId, snapshot]);
}, [attributeSchema, catalogRefresh, currentPlatform, selectedCategoryId, selectedStoreId, snapshot]);
useEffect(() => {
if (!snapshot || !currentPlatform) return;
let active = true;
@@ -784,7 +786,8 @@ const ProductEditPage = () => {
currentPlatform.id,
dictionaryId,
query,
selectedCategoryId
selectedCategoryId,
selectedStoreId,
);
setDictionaryValues((current) => ({ ...current, [dictionaryId]: page.items }));
};
@@ -888,18 +891,31 @@ const ProductEditPage = () => {
label: `${store.name}(${store.currency_code})`,
})
)}
onChange={() => {
form.setFieldValue(['profile', 'store_product_category_id'], null);
form.setFieldValue(['profile', 'category_source'], null);
form.setFieldValue('attributes', []);
}}
/>
</Form.Item>
<Form.Item
name={['profile', 'platform_product_category_id']}
name={['profile', 'store_product_category_id']}
label="产品类目"
className="span-full"
rules={[{ required: true, message: '请选择产品类目' }]}
getValueProps={(categoryId: string | null) => ({
value: categoryId
? (categories.paths.get(categoryId) ?? [categoryId])
: [],
})}
getValueFromEvent={(value: string[]) =>
value.at(-1) ?? null
}
>
<Cascader
className="editor-standard-cascader"
showSearch
placeholder="选择当前平台的产品类目"
placeholder="选择当前店铺可发布的产品类目"
options={categories.options}
notFoundContent="类目缓存正在准备中,请稍后重试"
getPopupContainer={(node) => node.parentElement ?? document.body}
+7 -16
View File
@@ -39,30 +39,19 @@ export const productEditorApi = {
'If-Match': `"${revisions.product}:${revisions.profile}"`,
},
}),
categoryOptions: (profileId: string, platformId: string, search?: string) =>
categoryOptions: (profileId: string, platformId: string, search?: string, storeId?: string | null) =>
api.get<ProductEditorCategoryOption[]>(
`/product-platform-profiles/${profileId}/category-options`,
{
params: search ? { q: search } : undefined,
params: { ...(search ? { q: search } : {}), ...(storeId ? { store_id: storeId } : {}) },
headers: { 'X-Atlas-Platform-Id': platformId },
}
),
syncCategoryTree: (profileId: string, platformId: string) =>
api.post<{ operation: Operation }>(
`/product-platform-profiles/${profileId}/category-syncs`,
undefined,
{
headers: {
'X-Atlas-Platform-Id': platformId,
'Idempotency-Key': crypto.randomUUID(),
},
}
),
attributeSchema: (profileId: string, platformId: string, categoryId?: string | null) =>
attributeSchema: (profileId: string, platformId: string, categoryId?: string | null, storeId?: string | null) =>
api.get<ProductAttributeSchema[]>(
`/product-platform-profiles/${profileId}/attribute-schema`,
{
params: categoryId ? { category_id: categoryId } : undefined,
params: { ...(categoryId ? { category_id: categoryId } : {}), ...(storeId ? { store_id: storeId } : {}) },
headers: { 'X-Atlas-Platform-Id': platformId },
}
),
@@ -83,13 +72,15 @@ export const productEditorApi = {
platformId: string,
dictionaryId: string,
search?: string,
categoryId?: string | null
categoryId?: string | null,
storeId?: string | null,
) =>
api.get<{ items: ProductAttributeDictionaryValue[]; next_cursor: string | null }>(
`/product-platform-profiles/${profileId}/attribute-dictionaries/${encodeURIComponent(dictionaryId)}`,
{
params: {
...(categoryId ? { category_id: categoryId } : {}),
...(storeId ? { store_id: storeId } : {}),
...(search ? { q: search } : {}),
},
headers: { 'X-Atlas-Platform-Id': platformId },