ERPTurbo_Admin/packages/app-operation/src/components/Dealer/DeliveryTemplate.tsx
shenyifei 0c39c005bd refactor(dealer): 重构经销商模块配置面板
- 使用 BetaSchemaForm 替代原有 Form 组件
- 移除冗余的表单配置渲染逻辑
- 动态生成模块配置表单结构
- 优化模块配置项的展示与交互
- 统一配置面板的数据流处理方式
- 提升配置面板的可维护性和扩展性
2025-11-07 00:36:09 +08:00

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>
);
}