feat(edit): 编辑页样式修改

This commit is contained in:
R524809
2026-09-20 17:44:40 +08:00
parent e0e07be4ea
commit 75fa2b60f3
7 changed files with 279 additions and 86 deletions
+93 -14
View File
@@ -35,6 +35,25 @@ import './ProductEditPage.css';
type EditorFormValues = SaveProductEditorDraftRequest; type EditorFormValues = SaveProductEditorDraftRequest;
type AttributeDraft = NonNullable<EditorFormValues['attributes']>[number]; type AttributeDraft = NonNullable<EditorFormValues['attributes']>[number];
/** 规格字段有值时才可据此恢复到对应的 SKU 编辑模式。 */
function hasPackageWeight(spec: {
length_mm: string | null;
width_mm: string | null;
height_mm: string | null;
shipping_weight_g: string | null;
}) {
return Boolean(spec.shipping_weight_g);
}
function hasPackageDimensions(spec: {
length_mm: string | null;
width_mm: string | null;
height_mm: string | null;
shipping_weight_g: string | null;
}) {
return Boolean(spec.length_mm && spec.width_mm && spec.height_mm);
}
/** /**
* 按 schema 补齐表单中缺失的属性草稿:aspect(变体维度)属性逐个 SKU 一条, * 按 schema 补齐表单中缺失的属性草稿:aspect(变体维度)属性逐个 SKU 一条,
* 其余类目级一条,确保 Form.List 字段与 schema 一一对应、可按定义渲染。 * 其余类目级一条,确保 Form.List 字段与 schema 一一对应、可按定义渲染。
@@ -167,11 +186,11 @@ function categoryTree(options: ProductEditorCategoryOption[]) {
for (const category of options) { for (const category of options) {
const node = nodes.get(category.id)!; const node = nodes.get(category.id)!;
node.isLeaf = !node.children?.length; node.isLeaf = !node.children?.length;
node.disabled = node.isLeaf && ( node.disabled =
!category.is_active || node.isLeaf &&
!category.external_type_id || (!category.is_active ||
category.external_type_id === '0' !category.external_type_id ||
); category.external_type_id === '0');
} }
for (const category of options) { for (const category of options) {
const path: string[] = []; const path: string[] = [];
@@ -404,6 +423,8 @@ const ProductEditPage = () => {
const [materializingTransferId, setMaterializingTransferId] = useState<string | null>(null); const [materializingTransferId, setMaterializingTransferId] = useState<string | null>(null);
const [dirty, setDirty] = useState(false); const [dirty, setDirty] = useState(false);
const [moreFeaturesExpanded, setMoreFeaturesExpanded] = useState(false); const [moreFeaturesExpanded, setMoreFeaturesExpanded] = useState(false);
const [usesVariantPackageWeight, setUsesVariantPackageWeight] = useState(false);
const [usesVariantPackageDimensions, setUsesVariantPackageDimensions] = useState(false);
const blocker = useBlocker(dirty); const blocker = useBlocker(dirty);
const sourceSpecsApplied = useRef(false); const sourceSpecsApplied = useRef(false);
const categories = useMemo(() => categoryTree(categoryOptions), [categoryOptions]); const categories = useMemo(() => categoryTree(categoryOptions), [categoryOptions]);
@@ -440,6 +461,14 @@ const ProductEditPage = () => {
setSourceOverriddenFields(next.source_material_state.overridden_fields); setSourceOverriddenFields(next.source_material_state.overridden_fields);
sourceSpecsApplied.current = false; sourceSpecsApplied.current = false;
form.setFieldsValue(formValues(next)); form.setFieldsValue(formValues(next));
setUsesVariantPackageWeight(
next.variants.length > 0 &&
next.variants.every((variant) => hasPackageWeight(variant.package_spec))
);
setUsesVariantPackageDimensions(
next.variants.length > 0 &&
next.variants.every((variant) => hasPackageDimensions(variant.package_spec))
);
setDirty(false); setDirty(false);
} catch (error) { } catch (error) {
message.error(apiErrorMessage(error)); message.error(apiErrorMessage(error));
@@ -601,7 +630,9 @@ const ProductEditPage = () => {
Object.fromEntries( Object.fromEntries(
results.map(([id, page]) => [ results.map(([id, page]) => [
id, id,
dictionaryQueries.current[id] ? (current[id] ?? null) : page.next_cursor, dictionaryQueries.current[id]
? (current[id] ?? null)
: page.next_cursor,
]) ])
) )
); );
@@ -615,13 +646,7 @@ const ProductEditPage = () => {
return () => { return () => {
active = false; active = false;
}; };
}, [ }, [catalogRefresh, currentPlatform, editableAttributeSchema, selectedCategoryId, snapshot]);
catalogRefresh,
currentPlatform,
editableAttributeSchema,
selectedCategoryId,
snapshot,
]);
useEffect(() => { useEffect(() => {
if (!snapshot || !currentPlatform) return; if (!snapshot || !currentPlatform) return;
let active = true; let active = true;
@@ -703,6 +728,53 @@ const ProductEditPage = () => {
}, [blocker, modal]); }, [blocker, modal]);
const leave = () => navigate('/collection'); const leave = () => navigate('/collection');
/** 将 SPU 共用包装重量复制到每个 SKU,保留默认值作为未填写 SKU 的安全回退。 */
const useVariantPackageWeight = () => {
const defaultPackageSpec = form.getFieldValue([
'common',
'default_package_spec',
]) as EditorFormValues['common']['default_package_spec'];
const variants = form.getFieldValue('variants') as EditorFormValues['variants'];
form.setFieldsValue({
variants: variants.map((variant) => {
const packageSpec = variant.package_spec;
return {
...variant,
package_spec: {
...packageSpec,
shipping_weight_g:
packageSpec.shipping_weight_g ?? defaultPackageSpec.shipping_weight_g,
},
};
}),
});
setUsesVariantPackageWeight(true);
setDirty(true);
};
/** 将 SPU 共用包装尺寸复制到每个 SKU,保留默认值作为未填写 SKU 的安全回退。 */
const useVariantPackageDimensions = () => {
const defaultPackageSpec = form.getFieldValue([
'common',
'default_package_spec',
]) as EditorFormValues['common']['default_package_spec'];
const variants = form.getFieldValue('variants') as EditorFormValues['variants'];
form.setFieldsValue({
variants: variants.map((variant) => {
const packageSpec = variant.package_spec;
return {
...variant,
package_spec: {
...packageSpec,
length_mm: packageSpec.length_mm ?? defaultPackageSpec.length_mm,
width_mm: packageSpec.width_mm ?? defaultPackageSpec.width_mm,
height_mm: packageSpec.height_mm ?? defaultPackageSpec.height_mm,
},
};
}),
});
setUsesVariantPackageDimensions(true);
setDirty(true);
};
/** 保存草稿:携带快照版本号做乐观锁;来源更新/版本冲突时重新加载合并结果。 */ /** 保存草稿:携带快照版本号做乐观锁;来源更新/版本冲突时重新加载合并结果。 */
const save = async (values: EditorFormValues) => { const save = async (values: EditorFormValues) => {
if (!productId || !currentPlatform || !snapshot) return; if (!productId || !currentPlatform || !snapshot) return;
@@ -836,7 +908,8 @@ const ProductEditPage = () => {
if ( if (
dictionaryContextRef.current !== requestContext || dictionaryContextRef.current !== requestContext ||
dictionaryQueries.current[attributeId] !== query dictionaryQueries.current[attributeId] !== query
) return; )
return;
setDictionaryValues((current) => ({ setDictionaryValues((current) => ({
...current, ...current,
[attributeId]: cursor [attributeId]: cursor
@@ -976,6 +1049,10 @@ const ProductEditPage = () => {
categoryPaths={categories.paths} categoryPaths={categories.paths}
categoryNotFoundContent={categoryNotFoundContent} categoryNotFoundContent={categoryNotFoundContent}
onCategorySearch={setCategorySearch} onCategorySearch={setCategorySearch}
usesVariantPackageWeight={usesVariantPackageWeight}
usesVariantPackageDimensions={usesVariantPackageDimensions}
onUseVariantPackageWeight={useVariantPackageWeight}
onUseVariantPackageDimensions={useVariantPackageDimensions}
/> />
<ProductAttributeSection <ProductAttributeSection
@@ -1014,6 +1091,8 @@ const ProductEditPage = () => {
onOpenDictionaryValues={openDictionaryValues} onOpenDictionaryValues={openDictionaryValues}
onLoadMoreDictionaryValues={loadMoreDictionaryValues} onLoadMoreDictionaryValues={loadMoreDictionaryValues}
onDirty={() => setDirty(true)} onDirty={() => setDirty(true)}
usesVariantPackageWeight={usesVariantPackageWeight}
usesVariantPackageDimensions={usesVariantPackageDimensions}
/> />
<ProductOtherSection <ProductOtherSection
@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { Button, Form, Input, Select, Typography } from 'antd'; import { Button, Form, Input, InputNumber, Select, Typography } from 'antd';
import type { FormInstance } from 'antd'; import type { FormInstance } from 'antd';
import type { import type {
ProductAttributeDictionaryValue, ProductAttributeDictionaryValue,
@@ -159,18 +159,18 @@ function DynamicAttributeField({
placeholder={`输入后回车添加,最多 ${definition.max_value_count} 项`} placeholder={`输入后回车添加,最多 ${definition.max_value_count} 项`}
/> />
</Form.Item> </Form.Item>
) : ( ) : definition.value_type === 'integer' || definition.value_type === 'decimal' ? (
<Form.Item name={scalarValueName} noStyle> <Form.Item name={scalarValueName} noStyle>
<Input <InputNumber<string>
type={ step={definition.value_type === 'integer' ? '1' : '0.001'}
definition.value_type === 'integer' || definition.value_type === 'decimal' stringMode
? 'number'
: undefined
}
step={definition.value_type === 'integer' ? '1' : 'any'}
placeholder={`填写${definition.name}`} placeholder={`填写${definition.name}`}
/> />
</Form.Item> </Form.Item>
) : (
<Form.Item name={scalarValueName} noStyle>
<Input placeholder={`填写${definition.name}`} />
</Form.Item>
); );
return ( return (
@@ -56,6 +56,7 @@
} }
.dynamic-attribute-control, .dynamic-attribute-control,
.dynamic-attribute-control .ant-select, .dynamic-attribute-control .ant-select,
.dynamic-attribute-control .ant-input { .dynamic-attribute-control .ant-input,
.dynamic-attribute-control .ant-input-number {
width: 100%; width: 100%;
} }
@@ -12,10 +12,9 @@
/* 长×宽×高输入组:随容器宽度收缩(压缩到下限后再由窄屏断点改为纵向堆叠) */ /* 长×宽×高输入组:随容器宽度收缩(压缩到下限后再由窄屏断点改为纵向堆叠) */
.package-dimensions-controls { .package-dimensions-controls {
display: grid; display: grid;
grid-template-columns: minmax(64px, 130px) 22px minmax(64px, 130px) 22px minmax( grid-template-columns:
64px, minmax(64px, 130px) 22px minmax(64px, 130px) 22px minmax(64px, 130px)
130px auto auto;
) auto;
align-items: center; align-items: center;
gap: 7px; gap: 7px;
max-width: 100%; max-width: 100%;
@@ -30,6 +29,14 @@
color: #667085; color: #667085;
line-height: 32px; line-height: 32px;
} }
.package-dimensions-controls .ant-input-number {
width: 100%;
}
.package-weight-control {
display: flex;
align-items: center;
gap: 4px;
}
/* 类目下拉弹层(Cascader popup,经 classNames.root 挂载) */ /* 类目下拉弹层(Cascader popup,经 classNames.root 挂载) */
.editor-category-popup .ant-cascader-menu { .editor-category-popup .ant-cascader-menu {
@@ -1,5 +1,6 @@
import { Card, Cascader, Form, Input, Select } from 'antd'; import { Button, Card, Cascader, Form, Input, InputNumber, Select, Tooltip } from 'antd';
import type { FormInstance } from 'antd'; import type { FormInstance } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import type { ProductEditorSnapshot } from '@atlas/contracts'; import type { ProductEditorSnapshot } from '@atlas/contracts';
import { SectionTitle } from './SectionTitle'; import { SectionTitle } from './SectionTitle';
import './ProductMainInfoSection.css'; import './ProductMainInfoSection.css';
@@ -13,28 +14,68 @@ type CategoryOption = {
children?: CategoryOption[]; children?: CategoryOption[];
}; };
function PackageFields({ name }: { name: NamePath }) { function PackageFields({
name,
usesVariantPackageWeight,
usesVariantPackageDimensions,
onUseVariantPackageWeight,
onUseVariantPackageDimensions,
}: {
name: NamePath;
usesVariantPackageWeight: boolean;
usesVariantPackageDimensions: boolean;
onUseVariantPackageWeight: () => void;
onUseVariantPackageDimensions: () => void;
}) {
return ( return (
<div className="package-fields"> <div className="package-fields">
<Form.Item name={[...name, 'shipping_weight_g']} label="包装重量" required> {!usesVariantPackageWeight && (
<Input type="number" min="0" step="any" addonAfter="g" style={{ width: 150 }} /> <Form.Item name={[...name, 'shipping_weight_g']} label="包装重量" required>
</Form.Item> <div className="package-weight-control">
<Form.Item label="包装尺寸" required className="package-dimensions"> <InputNumber<string>
<div className="package-dimensions-controls"> min="0"
<Form.Item name={[...name, 'length_mm']} noStyle> step="0.001"
<Input type="number" min="0" step="any" placeholder="长" /> stringMode
</Form.Item> addonAfter="g"
<span className="package-dimension-separator">×</span> style={{ width: 150 }}
<Form.Item name={[...name, 'width_mm']} noStyle> />
<Input type="number" min="0" step="any" placeholder="宽" /> <Tooltip title="按 SKU 分别填写包装重量">
</Form.Item> <Button
<span className="package-dimension-separator">×</span> type="text"
<Form.Item name={[...name, 'height_mm']} noStyle> icon={<PlusOutlined />}
<Input type="number" min="0" step="any" placeholder="高" /> aria-label="按 SKU 分别填写包装重量"
</Form.Item> onClick={onUseVariantPackageWeight}
<span className="package-dimension-unit">mm</span> />
</div> </Tooltip>
</Form.Item> </div>
</Form.Item>
)}
{!usesVariantPackageDimensions && (
<Form.Item label="包装尺寸" required className="package-dimensions">
<div className="package-dimensions-controls">
<Form.Item name={[...name, 'length_mm']} noStyle>
<InputNumber<string> min="0" step="0.001" stringMode placeholder="长" />
</Form.Item>
<span className="package-dimension-separator">×</span>
<Form.Item name={[...name, 'width_mm']} noStyle>
<InputNumber<string> min="0" step="0.001" stringMode placeholder="宽" />
</Form.Item>
<span className="package-dimension-separator">×</span>
<Form.Item name={[...name, 'height_mm']} noStyle>
<InputNumber<string> min="0" step="0.001" stringMode placeholder="高" />
</Form.Item>
<span className="package-dimension-unit">mm</span>
<Tooltip title="按 SKU 分别填写包装尺寸">
<Button
type="text"
icon={<PlusOutlined />}
aria-label="按 SKU 分别填写包装尺寸"
onClick={onUseVariantPackageDimensions}
/>
</Tooltip>
</div>
</Form.Item>
)}
</div> </div>
); );
} }
@@ -47,6 +88,10 @@ type Props = {
categoryPaths: Map<string, string[]>; categoryPaths: Map<string, string[]>;
categoryNotFoundContent: string; categoryNotFoundContent: string;
onCategorySearch: (query: string) => void; onCategorySearch: (query: string) => void;
usesVariantPackageWeight: boolean;
usesVariantPackageDimensions: boolean;
onUseVariantPackageWeight: () => void;
onUseVariantPackageDimensions: () => void;
}; };
/** /**
@@ -63,6 +108,10 @@ export function ProductMainInfoSection({
categoryPaths, categoryPaths,
categoryNotFoundContent, categoryNotFoundContent,
onCategorySearch, onCategorySearch,
usesVariantPackageWeight,
usesVariantPackageDimensions,
onUseVariantPackageWeight,
onUseVariantPackageDimensions,
}: Props) { }: Props) {
return ( return (
<Card className="editor-section editor-horizontal-section" bordered={false}> <Card className="editor-section editor-horizontal-section" bordered={false}>
@@ -139,7 +188,15 @@ export function ProductMainInfoSection({
<Input className="editor-short-input" placeholder="无品牌请填写“无品牌”" /> <Input className="editor-short-input" placeholder="无品牌请填写“无品牌”" />
</Form.Item> </Form.Item>
</div> </div>
<PackageFields name={['common', 'default_package_spec']} /> {(!usesVariantPackageWeight || !usesVariantPackageDimensions) && (
<PackageFields
name={['common', 'default_package_spec']}
usesVariantPackageWeight={usesVariantPackageWeight}
usesVariantPackageDimensions={usesVariantPackageDimensions}
onUseVariantPackageWeight={onUseVariantPackageWeight}
onUseVariantPackageDimensions={onUseVariantPackageDimensions}
/>
)}
</Card> </Card>
); );
} }
@@ -4,6 +4,10 @@
.sku-section .ant-input { .sku-section .ant-input {
border-radius: 8px; border-radius: 8px;
} }
.sku-section .ant-input-number {
width: 100%;
border-radius: 8px;
}
.sku-section textarea.ant-input { .sku-section textarea.ant-input {
resize: none; resize: none;
} }
@@ -13,6 +17,9 @@
.sku-section .ant-input-group .ant-input { .sku-section .ant-input-group .ant-input {
border-radius: 8px 0 0 8px; border-radius: 8px 0 0 8px;
} }
.sku-section .ant-input-number-group .ant-input-number {
border-radius: 8px 0 0 8px;
}
/* 卡片列表与卡片骨架 */ /* 卡片列表与卡片骨架 */
.sku-card-list { .sku-card-list {
@@ -83,7 +90,8 @@
width: 100%; width: 100%;
} }
.sku-card-summary .ant-input, .sku-card-summary .ant-input,
.sku-card-summary textarea.ant-input { .sku-card-summary textarea.ant-input,
.sku-card-summary .ant-input-number {
width: 100%; width: 100%;
} }
.sku-card-field, .sku-card-field,
@@ -310,7 +318,9 @@
min-width: 0; min-width: 0;
} }
/* 独立的重量输入(非"长×宽×高"行内组)限制宽度 */ /* 独立的重量输入(非"长×宽×高"行内组)限制宽度 */
.sku-spec-panel > .ant-form-item:not(:has(.sku-inline-dimensions)) .ant-input-group-wrapper { .sku-spec-panel
> .ant-form-item:not(:has(.sku-inline-dimensions))
:is(.ant-input-group-wrapper, .ant-input-number-group-wrapper) {
max-width: 220px; max-width: 220px;
} }
.sku-inline-dimensions { .sku-inline-dimensions {
@@ -1,5 +1,16 @@
import { useState } from 'react'; import { useState } from 'react';
import { Button, Card, Form, Image, Input, Popconfirm, Space, Tag, Typography } from 'antd'; import {
Button,
Card,
Form,
Image,
Input,
InputNumber,
Popconfirm,
Space,
Tag,
Typography,
} from 'antd';
import type { FormInstance } from 'antd'; import type { FormInstance } from 'antd';
import { CopyOutlined, DeleteOutlined, DownOutlined, PlusOutlined } from '@ant-design/icons'; import { CopyOutlined, DeleteOutlined, DownOutlined, PlusOutlined } from '@ant-design/icons';
import type { import type {
@@ -65,15 +76,15 @@ function InlineDimensions({ name }: { name: NamePath }) {
return ( return (
<div className="sku-inline-dimensions"> <div className="sku-inline-dimensions">
<Form.Item name={[...name, 'length_mm']} noStyle> <Form.Item name={[...name, 'length_mm']} noStyle>
<Input type="number" min="0" step="any" placeholder="长" /> <InputNumber<string> min="0" step="0.001" stringMode placeholder="长" />
</Form.Item> </Form.Item>
<span className="sku-dimension-separator">×</span> <span className="sku-dimension-separator">×</span>
<Form.Item name={[...name, 'width_mm']} noStyle> <Form.Item name={[...name, 'width_mm']} noStyle>
<Input type="number" min="0" step="any" placeholder="宽" /> <InputNumber<string> min="0" step="0.001" stringMode placeholder="宽" />
</Form.Item> </Form.Item>
<span className="sku-dimension-separator">×</span> <span className="sku-dimension-separator">×</span>
<Form.Item name={[...name, 'height_mm']} noStyle> <Form.Item name={[...name, 'height_mm']} noStyle>
<Input type="number" min="0" step="any" placeholder="高" /> <InputNumber<string> min="0" step="0.001" stringMode placeholder="高" />
</Form.Item> </Form.Item>
<span className="sku-dimension-unit">mm</span> <span className="sku-dimension-unit">mm</span>
</div> </div>
@@ -94,6 +105,8 @@ type Props = {
onOpenDictionaryValues: (dictionaryId: string) => void; onOpenDictionaryValues: (dictionaryId: string) => void;
onLoadMoreDictionaryValues: (dictionaryId: string, query: string) => void; onLoadMoreDictionaryValues: (dictionaryId: string, query: string) => void;
onDirty: () => void; onDirty: () => void;
usesVariantPackageWeight: boolean;
usesVariantPackageDimensions: boolean;
}; };
/** /**
@@ -117,6 +130,8 @@ export function ProductVariantSection({
onOpenDictionaryValues, onOpenDictionaryValues,
onLoadMoreDictionaryValues, onLoadMoreDictionaryValues,
onDirty, onDirty,
usesVariantPackageWeight,
usesVariantPackageDimensions,
}: Props) { }: Props) {
// 实时监听变体草稿:驱动主图回显与候选图片的选中态。 // 实时监听变体草稿:驱动主图回显与候选图片的选中态。
const draftVariants = (Form.useWatch('variants', form) ?? []) as VariantValues[]; const draftVariants = (Form.useWatch('variants', form) ?? []) as VariantValues[];
@@ -140,7 +155,11 @@ export function ProductVariantSection({
{(fields, { add, remove }) => { {(fields, { add, remove }) => {
// 新建变体默认值:id 为 null 表示服务端尚不存在, // 新建变体默认值:id 为 null 表示服务端尚不存在,
// client_key 作为保存前的临时标识。 // client_key 作为保存前的临时标识。
const addVariant = () => const addVariant = () => {
const defaultPackageSpec = form.getFieldValue([
'common',
'default_package_spec',
]) as EditorFormValues['common']['default_package_spec'];
add({ add({
id: null, id: null,
client_key: crypto.randomUUID(), client_key: crypto.randomUUID(),
@@ -157,15 +176,24 @@ export function ProductVariantSection({
status: null, status: null,
}, },
package_spec: { package_spec: {
length_mm: null, length_mm: usesVariantPackageDimensions
width_mm: null, ? defaultPackageSpec.length_mm
height_mm: null, : null,
shipping_weight_g: null, width_mm: usesVariantPackageDimensions
? defaultPackageSpec.width_mm
: null,
height_mm: usesVariantPackageDimensions
? defaultPackageSpec.height_mm
: null,
shipping_weight_g: usesVariantPackageWeight
? defaultPackageSpec.shipping_weight_g
: null,
status: null, status: null,
}, },
offer_id: null, offer_id: null,
media_asset_ids: [], media_asset_ids: [],
} satisfies VariantValues); } satisfies VariantValues);
};
return ( return (
<div className="sku-card-list"> <div className="sku-card-list">
{fields.map((field, index) => { {fields.map((field, index) => {
@@ -331,7 +359,12 @@ export function ProductVariantSection({
}, },
]} ]}
> >
<Input placeholder="—" /> <InputNumber<string>
min="0"
step="0.0001"
stringMode
placeholder="—"
/>
</Form.Item> </Form.Item>
</div> </div>
<div className="sku-card-field sku-price-field"> <div className="sku-card-field sku-price-field">
@@ -340,13 +373,15 @@ export function ProductVariantSection({
{original?.listing.currency_code ?? '₽'} {original?.listing.currency_code ?? '₽'}
) )
</span> </span>
<Input <InputNumber<string>
value={ value={
original?.listing.selling_price ?? original?.listing.selling_price ??
'' ''
} }
placeholder="—" placeholder="—"
readOnly readOnly
controls={false}
stringMode
/> />
</div> </div>
<div className="sku-card-value"> <div className="sku-card-value">
@@ -542,26 +577,30 @@ export function ProductVariantSection({
<Typography.Title level={5}> <Typography.Title level={5}>
SKU 规格 SKU 规格
</Typography.Title> </Typography.Title>
<Form.Item {usesVariantPackageWeight && (
name={[ <Form.Item
field.name, name={[
'package_spec', field.name,
'shipping_weight_g', 'package_spec',
]} 'shipping_weight_g',
label="包装重量" ]}
> label="包装重量"
<Input >
type="number" <InputNumber<string>
min="0" min="0"
step="any" step="0.001"
addonAfter="g" stringMode
/> addonAfter="g"
</Form.Item> />
<Form.Item label="包装尺寸"> </Form.Item>
<InlineDimensions )}
name={[field.name, 'package_spec']} {usesVariantPackageDimensions && (
/> <Form.Item label="包装尺寸">
</Form.Item> <InlineDimensions
name={[field.name, 'package_spec']}
/>
</Form.Item>
)}
<Form.Item <Form.Item
name={[ name={[
field.name, field.name,
@@ -570,10 +609,10 @@ export function ProductVariantSection({
]} ]}
label="商品重量" label="商品重量"
> >
<Input <InputNumber<string>
type="number"
min="0" min="0"
step="any" step="0.001"
stringMode
addonAfter="g" addonAfter="g"
/> />
</Form.Item> </Form.Item>