- 使用 BetaSchemaForm 替代原有 Form 组件 - 移除冗余的表单配置渲染逻辑 - 动态生成模块配置表单结构 - 优化模块配置项的展示与交互 - 统一配置面板的数据流处理方式 - 提升配置面板的可维护性和扩展性
802 lines
17 KiB
TypeScript
802 lines
17 KiB
TypeScript
import {
|
|
ButtonAccess,
|
|
ConfigPanel,
|
|
InsertPosition,
|
|
ModuleLibrary,
|
|
PreviewCanvas,
|
|
} from '@/components';
|
|
import { business } from '@/services';
|
|
import { formLayout } from '@/utils/formLayout';
|
|
import { useIntl } from '@@/exports';
|
|
import {
|
|
DrawerForm,
|
|
ProCard,
|
|
ProFormText,
|
|
RouteContext,
|
|
RouteContextType,
|
|
} from '@ant-design/pro-components';
|
|
import { Col, message, Row } from 'antd';
|
|
import { useEffect, useState } from 'react';
|
|
|
|
// 默认模块配置
|
|
interface ModuleConfig {
|
|
[key: string]: any;
|
|
}
|
|
export interface IDeliveryTemplateProps {
|
|
dealerVO: BusinessAPI.DealerVO;
|
|
onFinish: () => void;
|
|
insertPosition?: InsertPosition;
|
|
}
|
|
|
|
type DealerIdAndTemplate = Pick<
|
|
BusinessAPI.DealerUpdateCmd,
|
|
'dealerId' | 'deliveryTemplate'
|
|
>;
|
|
|
|
// 定义各模块的表单配置
|
|
const getModuleFormSchema = (moduleType: string) => {
|
|
const schemas = {
|
|
title: [
|
|
{
|
|
title: '显示配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'title',
|
|
title: '标题文本',
|
|
valueType: 'text',
|
|
fieldProps: {
|
|
placeholder: '例如:西瓜发货清单',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
dealerInfo: [
|
|
{
|
|
title: '显示配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'showDealerName',
|
|
title: '显示经销商名称',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'dealerName',
|
|
title: '经销商名称',
|
|
valueType: 'text',
|
|
fieldProps: {
|
|
placeholder: '请输入经销商名称',
|
|
},
|
|
},
|
|
{
|
|
dataIndex: 'showVehicleNumber',
|
|
title: '显示车次信息',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showDestination',
|
|
title: '显示收货地',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showWatermelonGrade',
|
|
title: '显示西瓜品级',
|
|
valueType: 'switch',
|
|
tooltip: '单条报价有效',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: '内容配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'requiredWatermelonGrade',
|
|
title: '填写西瓜品级',
|
|
valueType: 'switch',
|
|
tooltip: '单条报价有效',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
shippingInfo: [
|
|
{
|
|
title: '显示配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'showShippingFrom',
|
|
title: '显示发货地',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showDate',
|
|
title: '显示发货日期',
|
|
valueType: 'switch',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: '内容配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'requiredShippingFrom',
|
|
title: '填写发货地',
|
|
valueType: 'switch',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
weightInfo: [
|
|
{
|
|
title: '显示配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'showGrossWeight',
|
|
title: '显示毛重',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'grossWeightUnit',
|
|
title: '单位',
|
|
valueType: 'select',
|
|
valueEnum: {
|
|
1: '斤',
|
|
2: '公斤',
|
|
},
|
|
},
|
|
{
|
|
dataIndex: 'showBoxWeight',
|
|
title: '显示箱重',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'boxWeightUnit',
|
|
title: '单位',
|
|
valueType: 'select',
|
|
valueEnum: {
|
|
1: '斤',
|
|
2: '公斤',
|
|
},
|
|
},
|
|
{
|
|
dataIndex: 'showNetWeight',
|
|
title: '显示净重',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'netWeightUnit',
|
|
title: '单位',
|
|
valueType: 'select',
|
|
valueEnum: {
|
|
1: '斤',
|
|
2: '公斤',
|
|
},
|
|
},
|
|
{
|
|
dataIndex: 'showUnitPrice',
|
|
title: '显示单价',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'unitPriceUnit',
|
|
title: '单位',
|
|
valueType: 'select',
|
|
valueEnum: {
|
|
1: '元/斤',
|
|
2: '元/公斤',
|
|
},
|
|
},
|
|
{
|
|
dataIndex: 'showAmount',
|
|
title: '显示金额',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showGrade',
|
|
title: '显示品级',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showAccountCompany',
|
|
title: '显示入账公司',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showSumAmount',
|
|
title: '显示总计',
|
|
valueType: 'switch',
|
|
tooltip: '多条报价之和',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: '内容配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'requiredGrade',
|
|
title: '填写西瓜品级',
|
|
valueType: 'switch',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
packingSpec: [
|
|
{
|
|
title: '显示配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'showBoxType',
|
|
title: '显示箱号',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showQuantity',
|
|
title: '显示数量',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showUnitPrice',
|
|
title: '显示单价',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showAmount',
|
|
title: '显示金额',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showUnitWeight',
|
|
title: '显示单重',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showWeight',
|
|
title: '显示重量',
|
|
valueType: 'switch',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
vehicleInfo: [
|
|
{
|
|
title: '显示配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'showDriverPhone',
|
|
title: '显示司机号码',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showLicensePlate',
|
|
title: '显示车牌',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showEstimatedArrivalTime',
|
|
title: '显示预计到仓时间',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showFreightDebt',
|
|
title: '显示运费欠',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showStrawMatDebt',
|
|
title: '显示草帘欠',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showRemarks',
|
|
title: '显示备注',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'freightDebtTitle',
|
|
title: '运费欠标题',
|
|
valueType: 'text',
|
|
fieldProps: {
|
|
placeholder: '请输入运费欠标题',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: '内容配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'requiredEstimatedArrivalTime',
|
|
title: '填写预计到仓时间',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'requiredRemarks',
|
|
title: '填写备注',
|
|
valueType: 'switch',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
otherFees: [
|
|
{
|
|
title: '显示配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'feeItems',
|
|
title: '显示费用项目',
|
|
valueType: 'checkbox',
|
|
valueEnum: {
|
|
trademark: '商标',
|
|
labor: '人工',
|
|
paperBox: '纸箱',
|
|
fee: '费用',
|
|
codingFee: '打码费',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
totalAmount: [
|
|
{
|
|
title: '显示配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'showTotalAmount',
|
|
title: '显示合计金额',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showFarmer',
|
|
title: '信息瓜农',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'sumTitle',
|
|
title: '合计金额标题',
|
|
valueType: 'text',
|
|
fieldProps: {
|
|
placeholder: '请输入合计金额标题',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
otherInfo: [
|
|
{
|
|
title: '显示配置',
|
|
valueType: 'group',
|
|
columns: [
|
|
{
|
|
dataIndex: 'showOrigin',
|
|
title: '显示产地',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showSupplier',
|
|
title: '显示供应商',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showDepartureTime',
|
|
title: '显示发车时间',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showArrivalTime',
|
|
title: '显示预计到达',
|
|
valueType: 'switch',
|
|
},
|
|
{
|
|
dataIndex: 'showProductName',
|
|
title: '显示品名',
|
|
valueType: 'switch',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
} as any;
|
|
|
|
return schemas[moduleType] || [];
|
|
};
|
|
|
|
// 模块结构配置 - 定义每个模块的字段和默认显示设置
|
|
const getModuleStructure = (moduleType: string) => {
|
|
const structures = {
|
|
title: {},
|
|
dealerInfo: {
|
|
showDealerName: true,
|
|
showVehicleNumber: true,
|
|
showDestination: true,
|
|
showWatermelonGrade: true,
|
|
requiredWatermelonGrade: true,
|
|
},
|
|
shippingInfo: {
|
|
showShippingFrom: true,
|
|
showDate: true,
|
|
requiredShippingFrom: true,
|
|
},
|
|
weightInfo: {
|
|
showGrossWeight: true,
|
|
grossWeightUnit: '1',
|
|
showBoxWeight: true,
|
|
boxWeightUnit: '1',
|
|
showNetWeight: true,
|
|
netWeightUnit: '1',
|
|
showUnitPrice: true,
|
|
unitPriceUnit: '1',
|
|
showAmount: true,
|
|
showGrade: true,
|
|
showAccountCompany: true,
|
|
showSumAmount: true,
|
|
requiredGrade: true,
|
|
},
|
|
packingSpec: {
|
|
columns: [
|
|
{
|
|
dataIndex: 'boxCategory',
|
|
title: '',
|
|
},
|
|
{
|
|
dataIndex: 'boxType',
|
|
title: '箱号',
|
|
},
|
|
{
|
|
dataIndex: 'quantity',
|
|
title: '数量',
|
|
},
|
|
{
|
|
dataIndex: 'unitPrice',
|
|
title: '单价',
|
|
},
|
|
{
|
|
dataIndex: 'amount',
|
|
title: '金额',
|
|
},
|
|
{
|
|
dataIndex: 'unitWeight',
|
|
title: '单重',
|
|
},
|
|
{
|
|
dataIndex: 'weight',
|
|
title: '重量',
|
|
},
|
|
],
|
|
|
|
showBoxCategory: true,
|
|
showBoxType: true,
|
|
showQuantity: true,
|
|
showUnitPrice: true,
|
|
showAmount: true,
|
|
showUnitWeight: true,
|
|
showWeight: true,
|
|
},
|
|
vehicleInfo: {
|
|
freightDebtTitle: '运费欠',
|
|
showDriverPhone: true,
|
|
showLicensePlate: true,
|
|
showEstimatedArrivalTime: true,
|
|
showFreightDebt: true,
|
|
showStrawMatDebt: true,
|
|
showRemarks: true,
|
|
requiredEstimatedArrivalTime: true,
|
|
requiredRemarks: true,
|
|
},
|
|
otherFees: {
|
|
feeItems: ['trademark', 'labor', 'paperBox', 'fee', 'codingFee'],
|
|
feeLabels: {
|
|
trademark: '商标',
|
|
labor: '人工',
|
|
paperBox: '纸箱',
|
|
fee: '费用',
|
|
codingFee: '打码费',
|
|
},
|
|
},
|
|
totalAmount: {
|
|
sumTitle: '合计金额',
|
|
showTotalAmount: true,
|
|
showFarmer: true,
|
|
},
|
|
otherInfo: {
|
|
showOrigin: true,
|
|
showSupplier: true,
|
|
showDepartureTime: true,
|
|
showArrivalTime: true,
|
|
showProductName: true,
|
|
format: 'list',
|
|
},
|
|
};
|
|
|
|
return structures[moduleType as keyof typeof structures] || {};
|
|
};
|
|
|
|
// 模块示例数据 - 用于模板编辑时的预览
|
|
const getModuleExampleData = (moduleType: string) => {
|
|
const examples = {
|
|
title: {
|
|
title: '西瓜发货清单',
|
|
},
|
|
dealerInfo: {
|
|
dealerName: '盛京水果',
|
|
vehicleNumber: '第188车',
|
|
destination: '宁夏',
|
|
watermelonGrade: 'A级',
|
|
},
|
|
shippingInfo: {
|
|
shippingFrom: '宁夏',
|
|
date: '2025年8月18日',
|
|
},
|
|
weightInfo: {
|
|
data: [
|
|
{
|
|
grossWeight: '56960',
|
|
boxWeight: '3391',
|
|
netWeight: '53569',
|
|
unitPrice: '1.60',
|
|
amount: '85710',
|
|
grade: 'A',
|
|
},
|
|
{
|
|
grossWeight: '56960',
|
|
boxWeight: '3391',
|
|
netWeight: '53569',
|
|
unitPrice: '1.60',
|
|
amount: '85710',
|
|
grade: 'A',
|
|
},
|
|
],
|
|
accountCompany: '公司名称',
|
|
sumAmount: '85710',
|
|
},
|
|
packingSpec: {
|
|
data: [
|
|
{
|
|
boxCategory: '4粒',
|
|
boxType: '1号',
|
|
quantity: '376',
|
|
unitPrice: '8.70',
|
|
amount: '3,271',
|
|
unitWeight: '2.70',
|
|
weight: '1,015',
|
|
},
|
|
{
|
|
boxCategory: '2粒',
|
|
boxType: 'A',
|
|
quantity: '700',
|
|
unitPrice: '6.30',
|
|
amount: '4,410',
|
|
unitWeight: '1.50',
|
|
weight: '1,050',
|
|
},
|
|
{
|
|
boxCategory: '2粒',
|
|
boxType: 'AA',
|
|
quantity: '456',
|
|
unitPrice: '6.60',
|
|
amount: '3,010',
|
|
unitWeight: '1.60',
|
|
weight: '730',
|
|
},
|
|
],
|
|
},
|
|
vehicleInfo: {
|
|
driverPhone: '13800138000',
|
|
licensePlate: '京A12345',
|
|
estimatedArrivalTime: '2025年9月24日',
|
|
freightDebt: '11000',
|
|
strawMatDebt: '100',
|
|
remarks: '备注',
|
|
},
|
|
otherFees: {
|
|
trademark: '400',
|
|
labor: '600',
|
|
paperBox: '13,253',
|
|
fee: '2,000',
|
|
codingFee: '300',
|
|
},
|
|
totalAmount: {
|
|
amount: '102,263',
|
|
farmer: '李荣赞',
|
|
},
|
|
otherInfo: {
|
|
origin: '内蒙古',
|
|
supplier: '北京新发龙盛商贸有限公司',
|
|
departureTime: '9月2号',
|
|
arrivalTime: '9月3号',
|
|
productName: 'A级-麒麟爪',
|
|
},
|
|
};
|
|
|
|
return examples[moduleType as keyof typeof examples] || {};
|
|
};
|
|
|
|
const getDefaultModuleConfig = (moduleType: string): ModuleConfig => {
|
|
const structure = getModuleStructure(moduleType);
|
|
const exampleData = getModuleExampleData(moduleType);
|
|
return { ...structure, ...exampleData };
|
|
};
|
|
|
|
export default function DeliveryTemplate(props: IDeliveryTemplateProps) {
|
|
const { dealerVO, onFinish } = props;
|
|
const intl = useIntl();
|
|
const intlPrefix = 'dealer';
|
|
|
|
// 主组件
|
|
const [modules, setModules] = useState<any[]>([]);
|
|
const [selectedModule, setSelectedModule] = useState<any>(null);
|
|
|
|
// 加载经销商的发货单模板数据
|
|
useEffect(() => {
|
|
if (dealerVO.deliveryTemplate) {
|
|
try {
|
|
const savedModules = JSON.parse(dealerVO.deliveryTemplate);
|
|
setModules(savedModules);
|
|
} catch (e) {
|
|
console.error('Failed to parse delivery template', e);
|
|
}
|
|
}
|
|
}, [dealerVO.deliveryTemplate]);
|
|
|
|
const handleModuleAdd = (moduleType: string) => {
|
|
const newModule = {
|
|
id: `module_${Date.now()}`,
|
|
type: moduleType,
|
|
title: intl.formatMessage({ id: `${intlPrefix}.module.${moduleType}` }),
|
|
config: getDefaultModuleConfig(moduleType),
|
|
schemas: getModuleFormSchema(moduleType),
|
|
};
|
|
|
|
// 如果有选中的模块,则将新模块插入到选中模块的下方
|
|
if (selectedModule) {
|
|
const selectedIndex = modules.findIndex(
|
|
(module) => module.id === selectedModule.id,
|
|
);
|
|
|
|
if (selectedIndex !== -1) {
|
|
// 在选中模块后插入新模块
|
|
const newModules = [...modules];
|
|
newModules.splice(selectedIndex + 1, 0, newModule);
|
|
setModules(newModules);
|
|
setSelectedModule(newModule); // 立即选中新插入的模块
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 如果没有选中的模块,则添加到末尾
|
|
const newModules = [...modules, newModule];
|
|
setModules(newModules);
|
|
setSelectedModule(newModule); // 立即选中新插入的模块
|
|
};
|
|
|
|
const handleModuleSelect = (module: any) => {
|
|
setSelectedModule(module);
|
|
};
|
|
|
|
const handleModuleConfigChange = (moduleId: string, config: any) => {
|
|
setModules(
|
|
modules.map((module) =>
|
|
module.id === moduleId ? { ...module, config } : module,
|
|
),
|
|
);
|
|
|
|
if (selectedModule && selectedModule.id === moduleId) {
|
|
setSelectedModule({ ...selectedModule, config });
|
|
}
|
|
};
|
|
|
|
const handleModuleDelete = (moduleId: string) => {
|
|
setModules(modules.filter((module) => module.id !== moduleId));
|
|
if (selectedModule && selectedModule.id === moduleId) {
|
|
setSelectedModule(null);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<RouteContext.Consumer>
|
|
{(value: RouteContextType) => {
|
|
const { isMobile } = value;
|
|
return (
|
|
<DrawerForm<DealerIdAndTemplate>
|
|
title={intl.formatMessage({
|
|
id: intlPrefix + '.modal.deliveryTemplate.title',
|
|
})}
|
|
{...formLayout(isMobile)}
|
|
width={isMobile ? '100%' : '85%'}
|
|
drawerProps={{
|
|
destroyOnHidden: true,
|
|
}}
|
|
trigger={
|
|
<ButtonAccess key={'audit'} permission={''} type={'link'}>
|
|
{intl.formatMessage({
|
|
id: intlPrefix + '.modal.deliveryTemplate.button',
|
|
})}
|
|
</ButtonAccess>
|
|
}
|
|
request={async () => {
|
|
const { data } = await business.dealer.showDealer({
|
|
dealerShowQry: {
|
|
dealerId: dealerVO.dealerId,
|
|
},
|
|
});
|
|
|
|
if (data?.deliveryTemplate) {
|
|
const savedModules = JSON.parse(
|
|
data?.deliveryTemplate as string,
|
|
);
|
|
setModules(savedModules);
|
|
}
|
|
|
|
return {
|
|
dealerId: data?.dealerId,
|
|
} as DealerIdAndTemplate;
|
|
}}
|
|
onFinish={async (formData) => {
|
|
// 将模板数据序列化为JSON字符串
|
|
const deliveryTemplate = JSON.stringify(modules);
|
|
|
|
const { success } = await business.dealer.updateDealer({
|
|
dealerId: formData.dealerId!,
|
|
deliveryTemplate,
|
|
} as any);
|
|
|
|
if (success) {
|
|
message.success(
|
|
intl.formatMessage({
|
|
id: intlPrefix + '.modal.deliveryTemplate.success',
|
|
}),
|
|
);
|
|
onFinish();
|
|
}
|
|
|
|
return true;
|
|
}}
|
|
>
|
|
<ProFormText name="dealerId" hidden={true} />
|
|
<ProCard ghost={true}>
|
|
<Row gutter={24}>
|
|
{/* 左侧模块库 */}
|
|
<Col span={5}>
|
|
<ModuleLibrary onModuleAdd={handleModuleAdd} />
|
|
</Col>
|
|
|
|
{/* 中间预览画布 */}
|
|
<Col span={14}>
|
|
<PreviewCanvas
|
|
modules={modules}
|
|
selectedModule={selectedModule}
|
|
onModuleSelect={handleModuleSelect}
|
|
onModuleDelete={handleModuleDelete}
|
|
onModuleReorder={setModules}
|
|
onClearAll={() => {
|
|
setModules([]);
|
|
setSelectedModule(null);
|
|
}}
|
|
/>
|
|
</Col>
|
|
|
|
{/* 右侧配置面板 */}
|
|
<Col span={5}>
|
|
<ConfigPanel
|
|
selectedModule={selectedModule}
|
|
onConfigChange={handleModuleConfigChange}
|
|
/>
|
|
</Col>
|
|
</Row>
|
|
</ProCard>
|
|
</DrawerForm>
|
|
);
|
|
}}
|
|
</RouteContext.Consumer>
|
|
);
|
|
}
|