710 lines
23 KiB
TypeScript
710 lines
23 KiB
TypeScript
import {
|
||
productcontrollerCreateproduct,
|
||
productcontrollerDeleteproduct,
|
||
productcontrollerGetproductlist,
|
||
productcontrollerUpdateproductnamecn,
|
||
productcontrollerUpdateproduct,
|
||
productcontrollerGetproductcomponents,
|
||
productcontrollerSetproductcomponents,
|
||
productcontrollerAutobindcomponents,
|
||
productcontrollerGetattributelist,
|
||
} from '@/servers/api/product';
|
||
import { stockcontrollerGetstocks } from '@/servers/api/stock';
|
||
import { request } from '@umijs/max';
|
||
import { templatecontrollerRendertemplate } from '@/servers/api/template';
|
||
import { PlusOutlined } from '@ant-design/icons';
|
||
import {
|
||
ActionType,
|
||
DrawerForm,
|
||
PageContainer,
|
||
ProColumns,
|
||
ProForm,
|
||
ProFormInstance,
|
||
ProFormList,
|
||
ProFormSelect,
|
||
ProFormText,
|
||
ProFormTextArea,
|
||
ProTable,
|
||
} from '@ant-design/pro-components';
|
||
import { App, Button, Popconfirm, Tag, Upload } from 'antd';
|
||
|
||
import AttributeFormItem from '@/pages/Product/Attribute/components/AttributeFormItem';
|
||
|
||
import React, { useMemo, useRef, useState } from 'react';
|
||
const capitalize = (s: string) => s.charAt(0).toLocaleUpperCase() + s.slice(1);
|
||
// TODO
|
||
interface DictItem {
|
||
id: number;
|
||
name: string;
|
||
title: string;
|
||
}
|
||
const NameCn: React.FC<{
|
||
id: number;
|
||
value: string | undefined;
|
||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||
}> = ({ value, tableRef, id }) => {
|
||
const { message } = App.useApp();
|
||
const [editable, setEditable] = React.useState<boolean>(false);
|
||
if (!editable)
|
||
return <div onClick={() => setEditable(true)}>{value || '-'}</div>;
|
||
return (
|
||
<ProFormText
|
||
initialValue={value}
|
||
fieldProps={{
|
||
autoFocus: true,
|
||
onBlur: async (e: React.FocusEvent<HTMLInputElement>) => {
|
||
if (!e.target.value) return setEditable(false);
|
||
const { success, message: errMsg } =
|
||
await productcontrollerUpdateproductnamecn({
|
||
id,
|
||
nameCn: e.target.value,
|
||
});
|
||
setEditable(false);
|
||
if (!success) {
|
||
return message.error(errMsg);
|
||
}
|
||
tableRef?.current?.reloadAndRest?.();
|
||
},
|
||
}}
|
||
/>
|
||
);
|
||
};
|
||
|
||
const AttributesCell: React.FC<{ record: any }> = ({ record }) => {
|
||
return (
|
||
<div>
|
||
{(record.attributes || []).map((data: any, idx: number) => (
|
||
<Tag key={idx} color="purple" style={{ marginBottom: 4 }}>
|
||
{data?.dict?.name}: {data.name}
|
||
</Tag>
|
||
))}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
|
||
const ComponentsCell: React.FC<{ productId: number }> = ({ productId }) => {
|
||
const [components, setComponents] = React.useState<any[]>([]);
|
||
React.useEffect(() => {
|
||
(async () => {
|
||
const { data = [] } = await productcontrollerGetproductcomponents({ id: productId });
|
||
setComponents(data || []);
|
||
})();
|
||
}, [productId]);
|
||
return (
|
||
<div>
|
||
{components && components.length ? (
|
||
components.map((component: any) => (
|
||
<Tag key={component.id} color="blue" style={{ marginBottom: 4 }}>
|
||
{(component.productSku) || `#${component.id}`} × {component.quantity}(库存:
|
||
{component.stock?.map((s: any) => `${s.name}:${s.quantity}`).join(', ') || '-'}
|
||
)
|
||
</Tag>
|
||
))
|
||
) : (
|
||
<span>-</span>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const List: React.FC = () => {
|
||
const actionRef = useRef<ActionType>();
|
||
// 状态:存储当前选中的行
|
||
const [selectedRows, setSelectedRows] = React.useState<API.Product[]>([]);
|
||
|
||
const { message } = App.useApp();
|
||
// 中文注释:导出产品 CSV(带认证请求)
|
||
const handleDownloadProductsCSV = async () => {
|
||
try {
|
||
// 中文注释:发起认证请求获取 CSV Blob
|
||
const blob = await request('/product/export', { responseType: 'blob' });
|
||
// 中文注释:构建下载文件名
|
||
const d = new Date();
|
||
const pad = (n: number) => String(n).padStart(2, '0');
|
||
const filename = `products-${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}.csv`;
|
||
// 中文注释:创建临时链接并触发下载
|
||
const url = window.URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = filename;
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
a.remove();
|
||
window.URL.revokeObjectURL(url);
|
||
} catch (error) {
|
||
message.error('导出失败');
|
||
}
|
||
};
|
||
const columns: ProColumns<API.Product>[] = [
|
||
{
|
||
title: 'sku',
|
||
dataIndex: 'sku',
|
||
},
|
||
{
|
||
title: '名称',
|
||
dataIndex: 'name',
|
||
},
|
||
{
|
||
title: '中文名',
|
||
dataIndex: 'nameCn',
|
||
render: (_, record) => {
|
||
return (
|
||
<NameCn value={record.nameCn} id={record.id} tableRef={actionRef} />
|
||
);
|
||
},
|
||
},
|
||
{
|
||
title: '价格',
|
||
dataIndex: 'price',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '促销价',
|
||
dataIndex: 'promotionPrice',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '属性',
|
||
dataIndex: 'attributes',
|
||
hideInSearch: true,
|
||
render: (_, record) => <AttributesCell record={record} />,
|
||
},
|
||
{
|
||
title: '构成',
|
||
dataIndex: 'components',
|
||
hideInSearch: true,
|
||
render: (_, record) => <ComponentsCell productId={(record as any).id} />,
|
||
},
|
||
|
||
{
|
||
title: '描述',
|
||
dataIndex: 'description',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '更新时间',
|
||
dataIndex: 'updatedAt',
|
||
valueType: 'dateTime',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '创建时间',
|
||
dataIndex: 'createdAt',
|
||
valueType: 'dateTime',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '操作',
|
||
dataIndex: 'option',
|
||
valueType: 'option',
|
||
render: (_, record) => (
|
||
<>
|
||
<EditForm record={record} tableRef={actionRef} />
|
||
<Popconfirm
|
||
title="删除"
|
||
description="确认删除?"
|
||
onConfirm={async () => {
|
||
try {
|
||
const { success, message: errMsg } =
|
||
await productcontrollerDeleteproduct({ id: record.id });
|
||
if (!success) {
|
||
throw new Error(errMsg);
|
||
}
|
||
actionRef.current?.reload();
|
||
} catch (error: any) {
|
||
message.error(error.message);
|
||
}
|
||
}}
|
||
>
|
||
<Button type="link" danger>
|
||
删除
|
||
</Button>
|
||
</Popconfirm>
|
||
</>
|
||
),
|
||
},
|
||
];
|
||
|
||
return (
|
||
<PageContainer header={{ title: '产品列表' }}>
|
||
<ProTable<API.Product>
|
||
headerTitle="查询表格"
|
||
actionRef={actionRef}
|
||
rowKey="id"
|
||
toolBarRender={() => [
|
||
// 中文注释:新建按钮
|
||
<CreateForm tableRef={actionRef} />,
|
||
// 中文注释:导出 CSV(后端返回 text/csv,直接新窗口下载)
|
||
<Button onClick={handleDownloadProductsCSV}>导出CSV</Button>,
|
||
// 中文注释:导入 CSV(上传文件成功后刷新表格)
|
||
<Upload
|
||
name="file"
|
||
action="/product/import"
|
||
accept=".csv"
|
||
showUploadList={false}
|
||
maxCount={1}
|
||
onChange={({ file }) => {
|
||
if (file.status === 'done') {
|
||
message.success('导入完成');
|
||
actionRef.current?.reload();
|
||
} else if (file.status === 'error') {
|
||
message.error('导入失败');
|
||
}
|
||
}}
|
||
>
|
||
<Button>导入CSV</Button>
|
||
</Upload>,
|
||
]}
|
||
request={async (params) => {
|
||
const { data, success } = await productcontrollerGetproductlist(
|
||
params,
|
||
);
|
||
return {
|
||
total: data?.total || 0,
|
||
data: data?.items || [],
|
||
success,
|
||
};
|
||
}}
|
||
columns={columns}
|
||
editable={{
|
||
type: 'single',
|
||
onSave: async (key, record, originRow) => {
|
||
console.log('保存数据:', record);
|
||
},
|
||
}}
|
||
rowSelection={{
|
||
onChange: (_, selectedRows) => setSelectedRows(selectedRows),
|
||
}}
|
||
/>
|
||
</PageContainer>
|
||
);
|
||
};
|
||
|
||
const CreateForm: React.FC<{
|
||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||
}> = ({ tableRef }) => {
|
||
// antd 的消息提醒
|
||
const { message } = App.useApp();
|
||
// 表单引用
|
||
const formRef = useRef<ProFormInstance>();
|
||
const [productType, setProductType] = useState<'single' | 'bundle' | null>(null);
|
||
|
||
|
||
/**
|
||
* @description 生成 SKU
|
||
*/
|
||
const handleGenerateSku = async () => {
|
||
try {
|
||
// 从表单引用中获取当前表单的值
|
||
const formValues = formRef.current?.getFieldsValue();
|
||
const { humidityValues, brandValues, strengthValues, flavorValues } = formValues;
|
||
// 检查是否所有必需的字段都已选择
|
||
if (!brandValues?.length || !strengthValues?.length || !flavorValues?.length || !humidityValues?.length) {
|
||
message.warning('请先选择品牌、强度、口味和干湿');
|
||
return;
|
||
}
|
||
|
||
// 所选值(用于 SKU 模板传入 name)
|
||
const brandName: string = String(brandValues[0]);
|
||
const strengthName: string = String(strengthValues[0]);
|
||
const flavorName: string = String(flavorValues[0]);
|
||
const humidityName: string = String(humidityValues[0]);
|
||
|
||
// 调用模板渲染API来生成SKU
|
||
const { data: rendered, message: msg, success } = await templatecontrollerRendertemplate(
|
||
{ name: 'product.sku' },
|
||
{
|
||
brand: brandName || "",
|
||
strength: strengthName || '',
|
||
flavor: flavorName || '',
|
||
humidity: humidityName ? capitalize(humidityName) : '',
|
||
},
|
||
);
|
||
if (!success) {
|
||
throw new Error(msg);
|
||
}
|
||
|
||
// 将生成的SKU设置到表单字段中
|
||
formRef.current?.setFieldsValue({ sku: rendered });
|
||
} catch (error: any) {
|
||
message.error(`生成失败: ${error.message}`);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @description 生成产品名称
|
||
*/
|
||
const handleGenerateName = async () => {
|
||
try {
|
||
// 从表单引用中获取当前表单的值
|
||
const formValues = formRef.current?.getFieldsValue();
|
||
const { humidityValues, brandValues, strengthValues, flavorValues } = formValues;
|
||
// 检查是否所有必需的字段都已选择
|
||
if (!brandValues?.length || !strengthValues?.length || !flavorValues?.length || !humidityValues?.length) {
|
||
message.warning('请先选择品牌、强度、口味和干湿');
|
||
return;
|
||
}
|
||
|
||
const brandName: string = String(brandValues[0]);
|
||
const strengthName: string = String(strengthValues[0]);
|
||
const flavorName: string = String(flavorValues[0]);
|
||
const humidityName: string = String(humidityValues[0]);
|
||
const brandTitle = brandName;
|
||
const strengthTitle = strengthName;
|
||
const flavorTitle = flavorName;
|
||
|
||
// 调用模板渲染API来生成产品名称
|
||
const { message: msg, data: rendered, success } = await templatecontrollerRendertemplate(
|
||
{ name: 'product.title' },
|
||
{
|
||
brand: brandTitle,
|
||
strength: strengthTitle,
|
||
flavor: flavorTitle,
|
||
model: '',
|
||
humidity: humidityName === 'dry' ? 'Dry' : humidityName === 'moisture' ? 'Moisture' : capitalize(humidityName),
|
||
},
|
||
);
|
||
if (!success) {
|
||
throw new Error(msg);
|
||
}
|
||
// 将生成的名称设置到表单字段中
|
||
formRef.current?.setFieldsValue({ name: rendered });
|
||
} catch (error: any) {
|
||
message.error(`生成失败: ${error.message}`);
|
||
}
|
||
};
|
||
// TODO 可以输入brand等
|
||
return (
|
||
<DrawerForm<any>
|
||
title="新建"
|
||
formRef={formRef} // Pass formRef
|
||
trigger={
|
||
<Button type="primary">
|
||
<PlusOutlined />
|
||
新建
|
||
</Button>
|
||
}
|
||
autoFocusFirstInput
|
||
drawerProps={{
|
||
destroyOnHidden: true,
|
||
}}
|
||
onValuesChange={async (changedValues) => {
|
||
if ('sku' in changedValues) {
|
||
const sku = changedValues.sku;
|
||
if (sku) {
|
||
const { data } = await stockcontrollerGetstocks({ productSku: sku } as any);
|
||
if (data && data.items && data.items.length > 0) {
|
||
setProductType('single');
|
||
} else {
|
||
setProductType('bundle');
|
||
}
|
||
} else {
|
||
setProductType(null);
|
||
}
|
||
}
|
||
}}
|
||
onFinish={async (values) => {
|
||
// 中文注释:组装 attributes(支持输入新值,按标题传入创建/绑定)
|
||
|
||
const attributes = [
|
||
...(values.brandValues || []).map((v: string) => ({ dictName: 'brand', name: v })),
|
||
...(values.strengthValues || []).map((v: string) => ({ dictName: 'strength', name: v })),
|
||
...(values.flavorValues || []).map((v: string) => ({ dictName: 'flavor', name: v })),
|
||
...(values.humidityValues || []).map((v: string) => ({ dictName: 'humidity', name: v })),
|
||
...(values.sizeValues || []).map((v: string) => ({ dictName: 'size', name: v })),
|
||
...(values.category ? [{ dictName: 'category', name: values.category }] : []),
|
||
];
|
||
const payload: any = {
|
||
name: (values as any).name,
|
||
description: (values as any).description,
|
||
sku: (values as any).sku,
|
||
price: (values as any).price,
|
||
promotionPrice: (values as any).promotionPrice,
|
||
attributes,
|
||
};
|
||
const { success, message: errMsg } =
|
||
await productcontrollerCreateproduct(payload);
|
||
if (success) {
|
||
message.success('提交成功');
|
||
tableRef.current?.reloadAndRest?.();
|
||
return true;
|
||
}
|
||
message.error(errMsg);
|
||
return false;
|
||
}}
|
||
>
|
||
|
||
<ProForm.Group>
|
||
<ProFormText
|
||
name="sku"
|
||
label="SKU"
|
||
width="md"
|
||
placeholder="请输入SKU"
|
||
rules={[{ required: true, message: '请输入SKU' }]}
|
||
/>
|
||
<Button style={{ marginTop: '32px' }} onClick={handleGenerateSku}>
|
||
自动生成
|
||
</Button>
|
||
{productType && (
|
||
<Tag style={{ marginTop: '32px' }} color={productType === 'single' ? 'green' : 'orange'}>
|
||
{productType === 'single' ? '单品' : '套装'}
|
||
</Tag>
|
||
)}
|
||
</ProForm.Group>
|
||
<ProForm.Group>
|
||
<ProFormText
|
||
name="name"
|
||
label="名称"
|
||
width="md"
|
||
placeholder="请输入名称"
|
||
rules={[{ required: true, message: '请输入名称' }]}
|
||
/>
|
||
<Button style={{ marginTop: '32px' }} onClick={handleGenerateName}>
|
||
自动生成
|
||
</Button>
|
||
</ProForm.Group>
|
||
<AttributeFormItem dictName="brand" name="brandValues" label="产品品牌" isTag />
|
||
<AttributeFormItem dictName="strength" name="strengthValues" label="强度" isTag />
|
||
<AttributeFormItem dictName="flavor" name="flavorValues" label="口味" isTag />
|
||
<AttributeFormItem dictName="humidity" name="humidityValues" label="干湿" isTag />
|
||
<AttributeFormItem dictName="size" name="sizeValues" label="大小" isTag />
|
||
<AttributeFormItem dictName="category" name="category" label="分类" isTag />
|
||
<ProFormText
|
||
name="price"
|
||
label="价格"
|
||
width="md"
|
||
placeholder="请输入价格"
|
||
rules={[{ required: false }]}
|
||
/>
|
||
<ProFormText
|
||
name="promotionPrice"
|
||
label="促销价"
|
||
width="md"
|
||
placeholder="请输入促销价"
|
||
rules={[{ required: false }]}
|
||
/>
|
||
<ProFormTextArea
|
||
name="description"
|
||
width="lg"
|
||
label="产品描述"
|
||
placeholder="请输入产品描述"
|
||
/>
|
||
</DrawerForm>
|
||
);
|
||
};
|
||
|
||
export default List;
|
||
|
||
const EditForm: React.FC<{
|
||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||
record: API.Product;
|
||
}> = ({ tableRef, record }) => {
|
||
const { message } = App.useApp();
|
||
const formRef = useRef<ProFormInstance>();
|
||
const [components, setComponents] = useState<{ productSku: string; quantity: number }[]>([]);
|
||
const [productType, setProductType] = useState<'single' | 'bundle' | null>(null);
|
||
|
||
React.useEffect(() => {
|
||
// 中文注释:加载当前产品的组成
|
||
(async () => {
|
||
const { data = [] } = await productcontrollerGetproductcomponents({ id: (record as any).id });
|
||
const items = (data || []).map((c: any) => ({ productSku: c.productSku, quantity: c.quantity }));
|
||
setComponents(items as any);
|
||
formRef.current?.setFieldsValue({ components: items });
|
||
|
||
// 检查产品类型
|
||
if (record.sku) {
|
||
const { data: stockData } = await stockcontrollerGetstocks({ productSku: record.sku } as any);
|
||
if (stockData && stockData.items && stockData.items.length > 0) {
|
||
setProductType('single');
|
||
} else {
|
||
setProductType('bundle');
|
||
}
|
||
}
|
||
})();
|
||
}, [record]);
|
||
const initialValues = useMemo(() => {
|
||
if (!record) return {};
|
||
const attributes = record.attributes || [];
|
||
|
||
const attributesGroupedByName = (attributes as any[]).reduce((group, attr) => {
|
||
const dictName = attr.dict.name;
|
||
if (!group[dictName]) {
|
||
group[dictName] = [];
|
||
}
|
||
group[dictName].push(attr.name);
|
||
return group;
|
||
}, {} as Record<string, string[]>);
|
||
|
||
const values: any = {
|
||
...record,
|
||
brandValues: attributesGroupedByName.brand || [],
|
||
strengthValues: attributesGroupedByName.strength || [],
|
||
flavorValues: attributesGroupedByName.flavor || [],
|
||
humidityValues: attributesGroupedByName.humidity || [],
|
||
sizeValues: attributesGroupedByName.size || [],
|
||
category: attributesGroupedByName.category?.[0] || '',
|
||
};
|
||
return values;
|
||
}, [record]);
|
||
return (
|
||
<DrawerForm<any>
|
||
formRef={formRef}
|
||
title="编辑"
|
||
trigger={<Button type="link">编辑</Button>}
|
||
initialValues={initialValues}
|
||
onValuesChange={async (changedValues) => {
|
||
if ('sku' in changedValues) {
|
||
const sku = changedValues.sku;
|
||
if (sku) {
|
||
const { data } = await stockcontrollerGetstocks({ productSku: sku } as any);
|
||
if (data && data.items && data.items.length > 0) {
|
||
setProductType('single');
|
||
} else {
|
||
setProductType('bundle');
|
||
}
|
||
} else {
|
||
setProductType(null);
|
||
}
|
||
}
|
||
}}
|
||
onFinish={async (values) => {
|
||
const attributes = [
|
||
...(values.brandValues || []).map((v: string) => ({ dictName: 'brand', name: v })),
|
||
...(values.strengthValues || []).map((v: string) => ({ dictName: 'strength', name: v })),
|
||
...(values.flavorValues || []).map((v: string) => ({ dictName: 'flavor', name: v })),
|
||
...(values.humidityValues || []).map((v: string) => ({ dictName: 'humidity', name: v })),
|
||
...(values.sizeValues || []).map((v: string) => ({ dictName: 'size', name: v })),
|
||
...(values.category ? [{ dictName: 'category', name: values.category }] : []),
|
||
];
|
||
const updatePayload: any = {
|
||
name: values.name,
|
||
sku: values.sku,
|
||
description: values.description,
|
||
price: values.price,
|
||
promotionPrice: values.promotionPrice,
|
||
attributes,
|
||
};
|
||
const { success, message: errMsg } = await productcontrollerUpdateproduct(
|
||
{ id: (record as any).id },
|
||
updatePayload,
|
||
);
|
||
if (success) {
|
||
// 中文注释:同步更新组成(覆盖式)
|
||
const items = (values as any)?.components || [];
|
||
if (Array.isArray(items)) {
|
||
const payloadItems = items
|
||
.filter((i: any) => i && i.productSku && i.quantity && i.quantity > 0)
|
||
.map((i: any) => ({ productSku: i.productSku, quantity: Number(i.quantity) }));
|
||
await productcontrollerSetproductcomponents({ id: (record as any).id }, { items: payloadItems as any });
|
||
}
|
||
message.success('更新成功');
|
||
tableRef.current?.reloadAndRest?.();
|
||
return true;
|
||
}
|
||
message.error(errMsg);
|
||
return false;
|
||
}}
|
||
>
|
||
{/* 在这里列举attribute字段 */}
|
||
<ProForm.Group>
|
||
<ProFormText
|
||
name="sku"
|
||
label="SKU"
|
||
width="md"
|
||
placeholder="请输入SKU"
|
||
rules={[{ required: true, message: '请输入SKU' }]}
|
||
/>
|
||
{productType && (
|
||
<Tag style={{ marginTop: '32px' }} color={productType === 'single' ? 'green' : 'orange'}>
|
||
{productType === 'single' ? '单品' : '套装'}
|
||
</Tag>
|
||
)}
|
||
<ProFormText
|
||
name="name"
|
||
label="名称"
|
||
width="md"
|
||
placeholder="请输入名称"
|
||
rules={[{ required: true, message: '请输入名称' }]}
|
||
/>
|
||
</ProForm.Group>
|
||
<AttributeFormItem dictName="brand" name="brandValues" label="产品品牌" isTag />
|
||
<AttributeFormItem dictName="strength" name="strengthValues" label="强度" isTag />
|
||
<AttributeFormItem dictName="flavor" name="flavorValues" label="口味" isTag />
|
||
<AttributeFormItem dictName="humidity" name="humidityValues" label="干湿" isTag />
|
||
<AttributeFormItem dictName="size" name="sizeValues" label="大小" isTag />
|
||
<AttributeFormItem dictName="category" name="category" label="分类" />
|
||
<ProFormText
|
||
name="price"
|
||
label="价格"
|
||
width="md"
|
||
placeholder="请输入价格"
|
||
rules={[{ required: false }]}
|
||
/>
|
||
<ProFormText
|
||
name="promotionPrice"
|
||
label="促销价"
|
||
width="md"
|
||
placeholder="请输入促销价"
|
||
rules={[{ required: false }]}
|
||
/>
|
||
<ProFormTextArea
|
||
name="description"
|
||
width="lg"
|
||
label="产品描述"
|
||
placeholder="请输入产品描述"
|
||
/>
|
||
|
||
{/* 中文注释:编辑产品组成(库存ID + 数量) */}
|
||
<ProForm.Group>
|
||
<ProFormText
|
||
name={['__helper']}
|
||
hidden
|
||
/>
|
||
<Button
|
||
disabled={productType === 'single'}
|
||
onClick={async () => {
|
||
await productcontrollerAutobindcomponents({ id: (record as any).id });
|
||
const { data = [] } = await productcontrollerGetproductcomponents({ id: (record as any).id });
|
||
const items = (data || []).map((c: any) => ({ stockId: c.stockId, quantity: c.quantity }));
|
||
formRef.current?.setFieldsValue({ components: items });
|
||
}}
|
||
>
|
||
自动绑定组成(同 SKU)
|
||
</Button>
|
||
</ProForm.Group>
|
||
<ProForm.Item hidden={productType === 'single'}>
|
||
<ProFormList
|
||
name="components"
|
||
label="组成项"
|
||
creatorButtonProps={{ position: 'bottom', creatorButtonText: '新增组成项' }}
|
||
itemRender={({ listDom, action }) => (
|
||
<div style={{ marginBottom: 8, display: 'flex', flexDirection: 'row', alignItems: 'end' }}>
|
||
{listDom}
|
||
{action}
|
||
</div>
|
||
)}
|
||
>
|
||
<ProForm.Group>
|
||
<ProFormText
|
||
name="productSku"
|
||
label="库存SKU"
|
||
width="md"
|
||
placeholder="请输入库存SKU"
|
||
rules={[{ required: true, message: '请输入库存SKU' }]}
|
||
/>
|
||
<ProFormText
|
||
name="quantity"
|
||
label="数量"
|
||
width="md"
|
||
placeholder="请输入数量"
|
||
rules={[{ required: true, message: '请输入数量' }]}
|
||
/>
|
||
</ProForm.Group>
|
||
</ProFormList>
|
||
</ProForm.Item>
|
||
</DrawerForm >
|
||
);
|
||
};
|