feat(产品列表): 添加CSV导入导出功能并重构属性选择逻辑
重构产品创建和编辑表单中的属性选择组件,支持搜索和新增标签模式 添加CSV导入导出功能,支持通过接口下载产品数据表格 修改属性数据结构,使用name作为值而非id,提高灵活性
This commit is contained in:
parent
6244438f26
commit
0a6ea0396f
|
|
@ -12,7 +12,9 @@ import {
|
|||
productcontrollerSetproductcomponents,
|
||||
productcontrollerAutobindcomponents,
|
||||
productcontrollerGetattributeall,
|
||||
productcontrollerGetattributelist,
|
||||
} from '@/servers/api/product';
|
||||
import { request } from '@umijs/max';
|
||||
import { templatecontrollerRendertemplate } from '@/servers/api/template';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
|
|
@ -28,7 +30,7 @@ import {
|
|||
ProFormTextArea,
|
||||
ProTable,
|
||||
} from '@ant-design/pro-components';
|
||||
import { App, Button, Popconfirm, Tag } from 'antd';
|
||||
import { App, Button, Popconfirm, Tag, Upload } from 'antd';
|
||||
import { allowedDictNames } from '@/pages/Product/Attribute/consts';
|
||||
import React, { useRef, useState } from 'react';
|
||||
const capitalize = (s: string) => s.charAt(0).toLocaleUpperCase() + s.slice(1);
|
||||
|
|
@ -119,6 +121,28 @@ const List: React.FC = () => {
|
|||
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',
|
||||
|
|
@ -215,7 +239,30 @@ const List: React.FC = () => {
|
|||
headerTitle="查询表格"
|
||||
actionRef={actionRef}
|
||||
rowKey="id"
|
||||
toolBarRender={() => [<CreateForm tableRef={actionRef} />]}
|
||||
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,
|
||||
|
|
@ -248,10 +295,12 @@ const CreateForm: React.FC<{
|
|||
const { message } = App.useApp();
|
||||
// 表单引用
|
||||
const formRef = useRef<ProFormInstance>();
|
||||
// 状态:存储品牌、强度和口味的选项
|
||||
const [brandOptions, setBrandOptions] = useState<DictItem[]>([]);
|
||||
const [strengthOptions, setStrengthOptions] = useState<DictItem[]>([]);
|
||||
const [flavorOptions, setFlavorOptions] = useState<DictItem[]>([]);
|
||||
// 状态:存储品牌、强度、口味、规格的选项(label 使用标题,value 使用名称)
|
||||
const [brandOptions, setBrandOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [strengthOptions, setStrengthOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [flavorOptions, setFlavorOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [sizeOptions, setSizeOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [humidityOptions, setHumidityOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
|
||||
/**
|
||||
* @description 生成 SKU
|
||||
|
|
@ -260,26 +309,27 @@ const CreateForm: React.FC<{
|
|||
try {
|
||||
// 从表单引用中获取当前表单的值
|
||||
const formValues = formRef.current?.getFieldsValue();
|
||||
const { humidity, brandId, strengthId, flavorsId } = formValues;
|
||||
const { humidityValues, brandValues, strengthValues, flavorsValues } = formValues;
|
||||
// 检查是否所有必需的字段都已选择
|
||||
if (!brandId || !strengthId || !flavorsId || !humidity) {
|
||||
if (!brandValues?.length || !strengthValues?.length || !flavorsValues?.length || !humidityValues?.length) {
|
||||
message.warning('请先选择品牌、强度、口味和干湿');
|
||||
return;
|
||||
}
|
||||
|
||||
// 从选项中查找所选品牌、强度和口味的完整对象
|
||||
const brand = brandOptions.find((item) => item.id === brandId);
|
||||
const strength = strengthOptions.find((item) => item.id === strengthId);
|
||||
const flavor = flavorOptions.find((item) => item.id === flavorsId);
|
||||
// 所选值(用于 SKU 模板传入 name)
|
||||
const brandName: string = String(brandValues[0]);
|
||||
const strengthName: string = String(strengthValues[0]);
|
||||
const flavorName: string = String(flavorsValues[0]);
|
||||
const humidityName: string = String(humidityValues[0]);
|
||||
|
||||
// 调用模板渲染API来生成SKU
|
||||
const { data: rendered, message: msg, success } = await templatecontrollerRendertemplate(
|
||||
{ name: 'product.sku' },
|
||||
{
|
||||
brand: brand ? brand.name : "",
|
||||
strength: strength ? strength.name : '',
|
||||
flavor: flavor ? flavor.name : '',
|
||||
humidity: humidity ? capitalize(humidity) : '',
|
||||
brand: brandName || "",
|
||||
strength: strengthName || '',
|
||||
flavor: flavorName || '',
|
||||
humidity: humidityName ? capitalize(humidityName) : '',
|
||||
},
|
||||
);
|
||||
if (!success) {
|
||||
|
|
@ -300,27 +350,31 @@ const CreateForm: React.FC<{
|
|||
try {
|
||||
// 从表单引用中获取当前表单的值
|
||||
const formValues = formRef.current?.getFieldsValue();
|
||||
const { humidity, brandId, strengthId, flavorsId } = formValues;
|
||||
const { humidityValues, brandValues, strengthValues, flavorsValues } = formValues;
|
||||
// 检查是否所有必需的字段都已选择
|
||||
if (!brandId || !strengthId || !flavorsId || !humidity) {
|
||||
if (!brandValues?.length || !strengthValues?.length || !flavorsValues?.length || !humidityValues?.length) {
|
||||
message.warning('请先选择品牌、强度、口味和干湿');
|
||||
return;
|
||||
}
|
||||
|
||||
// 从选项中查找所选品牌、强度和口味的完整对象
|
||||
const brand = brandOptions.find((item) => item.id === brandId);
|
||||
const strength = strengthOptions.find((item) => item.id === strengthId);
|
||||
const flavor = flavorOptions.find((item) => item.id === flavorsId);
|
||||
// 获取标题(label),若为新输入值则使用原值作为标题
|
||||
const brandName: string = String(brandValues[0]);
|
||||
const strengthName: string = String(strengthValues[0]);
|
||||
const flavorName: string = String(flavorsValues[0]);
|
||||
const humidityName: string = String(humidityValues[0]);
|
||||
const brandTitle = brandOptions.find(i => i.value === brandName)?.label || brandName;
|
||||
const strengthTitle = strengthOptions.find(i => i.value === strengthName)?.label || strengthName;
|
||||
const flavorTitle = flavorOptions.find(i => i.value === flavorName)?.label || flavorName;
|
||||
|
||||
// 调用模板渲染API来生成产品名称
|
||||
const { message: msg, data: rendered, success } = await templatecontrollerRendertemplate(
|
||||
{ name: 'product.title' },
|
||||
{
|
||||
brand: brand ? brand.title : "",
|
||||
strength: strength ? strength.title : '',
|
||||
flavor: flavor ? flavor.title : '',
|
||||
brand: brandTitle,
|
||||
strength: strengthTitle,
|
||||
flavor: flavorTitle,
|
||||
model: '',
|
||||
humidity: humidity === 'dry' ? 'Dry' : 'Moisture',
|
||||
humidity: humidityName === 'dry' ? 'Dry' : humidityName === 'moisture' ? 'Moisture' : capitalize(humidityName),
|
||||
},
|
||||
);
|
||||
if (!success) {
|
||||
|
|
@ -334,7 +388,7 @@ const CreateForm: React.FC<{
|
|||
};
|
||||
// TODO 可以输入brand等
|
||||
return (
|
||||
<DrawerForm<API.CreateProductDTO>
|
||||
<DrawerForm<any>
|
||||
title="新建"
|
||||
formRef={formRef} // Pass formRef
|
||||
trigger={
|
||||
|
|
@ -348,20 +402,30 @@ const CreateForm: React.FC<{
|
|||
destroyOnHidden: true,
|
||||
}}
|
||||
onFinish={async (values) => {
|
||||
// 中文注释:将选择的字典项ID与干湿属性组装为后端需要的 attributes
|
||||
// 中文注释:组装 attributes(支持输入新值,按标题传入创建/绑定)
|
||||
const toArray = (v: any) => (Array.isArray(v) ? v : v ? [v] : []);
|
||||
const brandValues = toArray((values as any).brandValues);
|
||||
const strengthValues = toArray((values as any).strengthValues);
|
||||
const flavorsValues = toArray((values as any).flavorsValues);
|
||||
const sizeValues = toArray((values as any).sizeValues);
|
||||
const humidityValues = toArray((values as any).humidityValues);
|
||||
const mapWithLabel = (vals: string[], opts: { label: string; value: string }[], dictName: string) => (
|
||||
vals.map(v => ({ dictName, title: (opts.find(o => o.value === v)?.label || v) }))
|
||||
);
|
||||
const attributes = [
|
||||
...mapWithLabel(brandValues, brandOptions, 'brand'),
|
||||
...mapWithLabel(strengthValues, strengthOptions, 'strength'),
|
||||
...mapWithLabel(flavorsValues, flavorOptions, 'flavor'),
|
||||
...mapWithLabel(sizeValues, sizeOptions, 'size'),
|
||||
...mapWithLabel(humidityValues, humidityOptions, 'humidity'),
|
||||
].filter(Boolean);
|
||||
const payload: any = {
|
||||
name: values.name,
|
||||
description: values.description,
|
||||
sku: values.sku,
|
||||
price: values.price,
|
||||
promotionPrice: values.promotionPrice,
|
||||
attributes: [
|
||||
values.brandId ? { id: values.brandId } : null,
|
||||
values.strengthId ? { id: values.strengthId } : null,
|
||||
values.flavorsId ? { id: values.flavorsId } : null,
|
||||
values.sizeId ? { id: values.sizeId } : null,
|
||||
values.humidity ? { id: values.humidityId } : null,
|
||||
].filter(Boolean),
|
||||
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);
|
||||
|
|
@ -374,71 +438,127 @@ const CreateForm: React.FC<{
|
|||
return false;
|
||||
}}
|
||||
>
|
||||
{/* 品牌(可搜索、可新增) */}
|
||||
<ProFormSelect
|
||||
name="brandId"
|
||||
name="brandValues"
|
||||
width="lg"
|
||||
label="产品品牌"
|
||||
placeholder="请选择产品品牌"
|
||||
request={async () => {
|
||||
const { data = [] } = await productcontrollerCompatbrandall();
|
||||
setBrandOptions(data);
|
||||
return data.map((item: DictItem) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
placeholder="请输入或选择产品品牌"
|
||||
fieldProps={{
|
||||
mode: 'tags',
|
||||
showSearch: true,
|
||||
filterOption: false,
|
||||
onSearch: async (val) => {
|
||||
const items = await productcontrollerGetattributelist({ dictName: 'brand', name: val, current: 1, pageSize: 20 } as any);
|
||||
const options = (items?.data?.items || []).map((it: any) => ({ label: it.title, value: it.name }));
|
||||
setBrandOptions(options);
|
||||
},
|
||||
}}
|
||||
request={async () => {
|
||||
const items = await productcontrollerGetattributelist({ dictName: 'brand', current: 1, pageSize: 20 } as any);
|
||||
const options = (items?.data?.items || []).map((it: any) => ({ label: it.title, value: it.name }));
|
||||
setBrandOptions(options);
|
||||
return options;
|
||||
}}
|
||||
options={brandOptions}
|
||||
rules={[{ required: true, message: '请选择产品品牌' }]}
|
||||
/>
|
||||
{/* 强度(可搜索、可新增) */}
|
||||
<ProFormSelect
|
||||
name="strengthId"
|
||||
name="strengthValues"
|
||||
width="lg"
|
||||
label="强度"
|
||||
placeholder="请选择强度"
|
||||
request={async () => {
|
||||
const { data = [] } = await productcontrollerCompatstrengthall();
|
||||
setStrengthOptions(data);
|
||||
return data.map((item: DictItem) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
placeholder="请输入或选择强度"
|
||||
fieldProps={{
|
||||
mode: 'tags',
|
||||
showSearch: true,
|
||||
filterOption: false,
|
||||
onSearch: async (val) => {
|
||||
const items = await productcontrollerGetattributelist({ dictName: 'strength', name: val, current: 1, pageSize: 20 } as any);
|
||||
const options = (items?.data?.items || []).map((it: any) => ({ label: it.title, value: it.name }));
|
||||
setStrengthOptions(options);
|
||||
},
|
||||
}}
|
||||
request={async () => {
|
||||
const items = await productcontrollerGetattributelist({ dictName: 'strength', current: 1, pageSize: 20 } as any);
|
||||
const options = (items?.data?.items || []).map((it: any) => ({ label: it.title, value: it.name }));
|
||||
setStrengthOptions(options);
|
||||
return options;
|
||||
}}
|
||||
options={strengthOptions}
|
||||
rules={[{ required: true, message: '请选择强度' }]}
|
||||
/>
|
||||
{/* 口味(可搜索、可新增) */}
|
||||
<ProFormSelect
|
||||
name="flavorsId"
|
||||
name="flavorsValues"
|
||||
width="lg"
|
||||
label="口味"
|
||||
placeholder="请选择口味"
|
||||
request={async () => {
|
||||
const { data = [] } = await productcontrollerCompatflavorsall();
|
||||
setFlavorOptions(data);
|
||||
return data.map((item: DictItem) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
placeholder="请输入或选择口味"
|
||||
fieldProps={{
|
||||
mode: 'tags',
|
||||
showSearch: true,
|
||||
filterOption: false,
|
||||
onSearch: async (val) => {
|
||||
const items = await productcontrollerGetattributelist({ dictName: 'flavor', name: val, current: 1, pageSize: 20 } as any);
|
||||
const options = (items?.data?.items || []).map((it: any) => ({ label: it.title, value: it.name }));
|
||||
setFlavorOptions(options);
|
||||
},
|
||||
}}
|
||||
request={async () => {
|
||||
const items = await productcontrollerGetattributelist({ dictName: 'flavor', current: 1, pageSize: 20 } as any);
|
||||
const options = (items?.data?.items || []).map((it: any) => ({ label: it.title, value: it.name }));
|
||||
setFlavorOptions(options);
|
||||
return options;
|
||||
}}
|
||||
options={flavorOptions}
|
||||
rules={[{ required: true, message: '请选择口味' }]}
|
||||
/>
|
||||
<ProFormSelect
|
||||
name="sizeId"
|
||||
name="sizeValues"
|
||||
width="lg"
|
||||
label="规格"
|
||||
placeholder="请选择规格"
|
||||
request={async () => {
|
||||
const { data = [] } = await productcontrollerCompatsizeall();
|
||||
return (data || []).map((item: any) => ({ label: item.name, value: item.id }));
|
||||
placeholder="请输入或选择规格"
|
||||
fieldProps={{
|
||||
mode: 'tags',
|
||||
showSearch: true,
|
||||
filterOption: false,
|
||||
onSearch: async (val) => {
|
||||
const items = await productcontrollerGetattributelist({ dictName: 'size', name: val, current: 1, pageSize: 20 } as any);
|
||||
const options = (items?.data?.items || []).map((it: any) => ({ label: it.title, value: it.name }));
|
||||
setSizeOptions(options);
|
||||
},
|
||||
}}
|
||||
request={async () => {
|
||||
const items = await productcontrollerGetattributelist({ dictName: 'size', current: 1, pageSize: 20 } as any);
|
||||
const options = (items?.data?.items || []).map((it: any) => ({ label: it.title, value: it.name }));
|
||||
setSizeOptions(options);
|
||||
return options;
|
||||
}}
|
||||
options={sizeOptions}
|
||||
rules={[{ required: false }]}
|
||||
/>
|
||||
<ProFormSelect
|
||||
name="humidity"
|
||||
name="humidityValues"
|
||||
width="lg"
|
||||
label="干湿"
|
||||
placeholder="请选择干湿"
|
||||
request={async () => {
|
||||
const { data = [] } = await productcontrollerGetattributeall({ dictName: 'humidity' } as any);
|
||||
return (data || []).map((item: any) => ({ label: item.name, value: item.name }));
|
||||
placeholder="请输入或选择干湿"
|
||||
fieldProps={{
|
||||
mode: 'tags',
|
||||
showSearch: true,
|
||||
filterOption: false,
|
||||
onSearch: async (val) => {
|
||||
const items = await productcontrollerGetattributelist({ dictName: 'humidity', name: val, current: 1, pageSize: 20 } as any);
|
||||
const options = (items?.data?.items || []).map((it: any) => ({ label: it.title, value: it.name }));
|
||||
setHumidityOptions(options);
|
||||
},
|
||||
}}
|
||||
request={async () => {
|
||||
const items = await productcontrollerGetattributelist({ dictName: 'humidity', current: 1, pageSize: 20 } as any);
|
||||
const options = (items?.data?.items || []).map((it: any) => ({ label: it.title, value: it.name }));
|
||||
setHumidityOptions(options);
|
||||
return options;
|
||||
}}
|
||||
options={humidityOptions}
|
||||
rules={[{ required: true, message: '请选择干湿' }]}
|
||||
/>
|
||||
<ProForm.Group>
|
||||
|
|
@ -497,29 +617,24 @@ const EditForm: React.FC<{
|
|||
}> = ({ tableRef, record }) => {
|
||||
const { message } = App.useApp();
|
||||
const formRef = useRef<ProFormInstance>();
|
||||
const [brandOptions, setBrandOptions] = useState<DictItem[]>([]);
|
||||
const [strengthOptions, setStrengthOptions] = useState<DictItem[]>([]);
|
||||
const [flavorOptions, setFlavorOptions] = useState<DictItem[]>([]);
|
||||
// 中文注释:各属性的选择项(使用 {label,value},value 用 name,便于 tags 输入匹配)
|
||||
const [brandOptions, setBrandOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [strengthOptions, setStrengthOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [flavorOptions, setFlavorOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [humidityOptions, setHumidityOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [components, setComponents] = useState<{ stockId: number; quantity: number }[]>([]);
|
||||
|
||||
const setInitialIds = () => {
|
||||
const brand = brandOptions.find((item) => item.title === (record.brand?.name));
|
||||
const strength = strengthOptions.find((item) => item.title === (record.strength?.name));
|
||||
const flavor = flavorOptions.find((item) => item.title === (record.flavors?.name));
|
||||
formRef.current?.setFieldsValue({
|
||||
brandId: brand?.id,
|
||||
strengthId: strength?.id,
|
||||
flavorsId: flavor?.id,
|
||||
});
|
||||
// 中文注释:通用远程选项加载器(支持关键词搜索)
|
||||
const fetchDictOptions = async (dictName: string, keyword?: string) => {
|
||||
// 条件判断:构造查询参数
|
||||
const params: any = { dictName, current: 1, pageSize: 20 };
|
||||
if (keyword) params.name = keyword;
|
||||
const { data } = await productcontrollerGetattributelist(params);
|
||||
const items = data?.items || [];
|
||||
// 中文注释:统一转为 {label,value},value 使用 name
|
||||
return items.map((it: DictItem) => ({ label: it.name, value: it.name }));
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (brandOptions.length && strengthOptions.length && flavorOptions.length) {
|
||||
setInitialIds();
|
||||
}
|
||||
}, [brandOptions, strengthOptions, flavorOptions]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// 中文注释:加载干湿选项
|
||||
(async () => {
|
||||
|
|
@ -536,7 +651,7 @@ const EditForm: React.FC<{
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<DrawerForm<API.UpdateProductDTO>
|
||||
<DrawerForm<any>
|
||||
formRef={formRef}
|
||||
title="编辑"
|
||||
trigger={<Button type="link">编辑</Button>}
|
||||
|
|
@ -548,14 +663,22 @@ const EditForm: React.FC<{
|
|||
promotionPrice: record.promotionPrice,
|
||||
components,
|
||||
attributes: record.attributes || [],
|
||||
// 中文注释:为可搜索可新增的选择框提供初始值(使用 name 作为值)
|
||||
brandValues: record.brand?.name ? [record.brand.name] : [],
|
||||
strengthValues: record.strength?.name ? [record.strength.name] : [],
|
||||
flavorsValues: record.flavors?.name ? [record.flavors.name] : [],
|
||||
}}
|
||||
onFinish={async (values) => {
|
||||
// 中文注释:组装 attributes(若选择了则发送)
|
||||
const toArray = (v: any) => (Array.isArray(v) ? v : v ? [v] : []);
|
||||
const brandValues = toArray((values as any).brandValues);
|
||||
const strengthValues = toArray((values as any).strengthValues);
|
||||
const flavorsValues = toArray((values as any).flavorsValues);
|
||||
const attrs = [
|
||||
values.brandId ? { id: values.brandId } : null,
|
||||
values.strengthId ? { id: values.strengthId } : null,
|
||||
values.flavorsId ? { id: values.flavorsId } : null,
|
||||
values.sizeId ? { id: values.sizeId } : null,
|
||||
// 条件判断:将 tags 值转为 { dictName, title }
|
||||
...brandValues.map((t: string) => ({ dictName: 'brand', title: String(t).trim() })),
|
||||
...strengthValues.map((t: string) => ({ dictName: 'strength', title: String(t).trim() })),
|
||||
...flavorsValues.map((t: string) => ({ dictName: 'flavor', title: String(t).trim() })),
|
||||
values.humidity ? { dictName: 'humidity', name: values.humidity } : null,
|
||||
].filter(Boolean);
|
||||
const updatePayload: any = {
|
||||
|
|
@ -604,38 +727,71 @@ const EditForm: React.FC<{
|
|||
rules={[{ required: true, message: '请输入名称' }]}
|
||||
/>
|
||||
</ProForm.Group>
|
||||
{/* 中文注释:品牌(可搜索、可新增) */}
|
||||
<ProFormSelect
|
||||
name="brandId"
|
||||
name="brandValues"
|
||||
width="lg"
|
||||
label="产品品牌"
|
||||
placeholder="请选择产品品牌"
|
||||
request={async () => {
|
||||
const { data = [] } = await productcontrollerCompatbrandall();
|
||||
setBrandOptions(data);
|
||||
return data.map((item: DictItem) => ({ label: item.name, value: item.id }));
|
||||
placeholder="请输入或选择产品品牌"
|
||||
fieldProps={{
|
||||
mode: 'tags',
|
||||
showSearch: true,
|
||||
filterOption: false,
|
||||
onSearch: async (val) => {
|
||||
const options = await fetchDictOptions('brand', val);
|
||||
setBrandOptions(options);
|
||||
},
|
||||
}}
|
||||
request={async () => {
|
||||
const options = await fetchDictOptions('brand');
|
||||
setBrandOptions(options);
|
||||
return options;
|
||||
}}
|
||||
options={brandOptions}
|
||||
/>
|
||||
{/* 中文注释:强度(可搜索、可新增) */}
|
||||
<ProFormSelect
|
||||
name="strengthId"
|
||||
name="strengthValues"
|
||||
width="lg"
|
||||
label="强度"
|
||||
placeholder="请选择强度"
|
||||
request={async () => {
|
||||
const { data = [] } = await productcontrollerCompatstrengthall();
|
||||
setStrengthOptions(data);
|
||||
return data.map((item: DictItem) => ({ label: item.name, value: item.id }));
|
||||
placeholder="请输入或选择强度"
|
||||
fieldProps={{
|
||||
mode: 'tags',
|
||||
showSearch: true,
|
||||
filterOption: false,
|
||||
onSearch: async (val) => {
|
||||
const options = await fetchDictOptions('strength', val);
|
||||
setStrengthOptions(options);
|
||||
},
|
||||
}}
|
||||
request={async () => {
|
||||
const options = await fetchDictOptions('strength');
|
||||
setStrengthOptions(options);
|
||||
return options;
|
||||
}}
|
||||
options={strengthOptions}
|
||||
/>
|
||||
{/* 中文注释:口味(可搜索、可新增) */}
|
||||
<ProFormSelect
|
||||
name="flavorsId"
|
||||
name="flavorsValues"
|
||||
width="lg"
|
||||
label="口味"
|
||||
placeholder="请选择口味"
|
||||
request={async () => {
|
||||
const { data = [] } = await productcontrollerCompatflavorsall();
|
||||
setFlavorOptions(data);
|
||||
return data.map((item: DictItem) => ({ label: item.name, value: item.id }));
|
||||
placeholder="请输入或选择口味"
|
||||
fieldProps={{
|
||||
mode: 'tags',
|
||||
showSearch: true,
|
||||
filterOption: false,
|
||||
onSearch: async (val) => {
|
||||
const options = await fetchDictOptions('flavor', val);
|
||||
setFlavorOptions(options);
|
||||
},
|
||||
}}
|
||||
request={async () => {
|
||||
const options = await fetchDictOptions('flavor');
|
||||
setFlavorOptions(options);
|
||||
return options;
|
||||
}}
|
||||
options={flavorOptions}
|
||||
/>
|
||||
<ProFormSelect
|
||||
name="humidity"
|
||||
|
|
|
|||
Loading…
Reference in New Issue