From d17eb56a4db60396cf024440c8b5966693134e91 Mon Sep 17 00:00:00 2001 From: shenyifei Date: Mon, 3 Nov 2025 18:18:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(delivery-template):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=8F=91=E8=B4=A7=E6=A8=A1=E6=9D=BF=E5=88=9B=E5=BB=BA=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=B9=B6=E6=96=B0=E5=A2=9E=E6=89=93=E5=8D=B0=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将模块库、预览画布和配置面板拆分为独立组件 - 简化页面主组件结构,移除大量内联样式和冗余代码 - 新增 PrintPreview 组件用于模板打印预览 - 更新装箱规格表格数据结构,增加箱类字段 - 扩展车辆信息和合计金额模块的配置选项 - 优化导入语句,仅保留必要的第三方组件引用 - 移除 react-beautiful-dnd 拖拽相关依赖和实现 - 导出 DeliveryTemplate 组件供其他模块使用 --- .../DeliveryTemplate/ConfigPanel.tsx | 354 ++++ .../DeliveryTemplate/DealerInfoModule.tsx | 101 ++ .../DeliveryTemplate/ModuleLibrary.tsx | 112 ++ .../DeliveryTemplate/OtherFeesModule.tsx | 108 ++ .../DeliveryTemplate/OtherInfoModule.tsx | 145 ++ .../DeliveryTemplate/PackingSpecModule.tsx | 118 ++ .../DeliveryTemplate/PreviewCanvas.tsx | 164 ++ .../DeliveryTemplate/PrintPreview.tsx | 539 ++++++ .../DeliveryTemplate/ShippingInfoModule.tsx | 105 ++ .../DeliveryTemplate/TitleModule.tsx | 91 + .../DeliveryTemplate/TotalAmountModule.tsx | 104 ++ .../DeliveryTemplate/VehicleInfoModule.tsx | 179 ++ .../DeliveryTemplate/WeightInfoModule.tsx | 150 ++ .../src/components/DeliveryTemplate/index.ts | 15 + .../app-operation/src/components/index.ts | 1 + packages/app-operation/src/global.less | 167 -- .../src/pages/DeliveryTemplateCreate.tsx | 1524 +---------------- 17 files changed, 2323 insertions(+), 1654 deletions(-) create mode 100644 packages/app-operation/src/components/DeliveryTemplate/ConfigPanel.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/DealerInfoModule.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/ModuleLibrary.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/OtherFeesModule.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/OtherInfoModule.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/PackingSpecModule.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/PreviewCanvas.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/PrintPreview.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/ShippingInfoModule.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/TitleModule.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/TotalAmountModule.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/VehicleInfoModule.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/WeightInfoModule.tsx create mode 100644 packages/app-operation/src/components/DeliveryTemplate/index.ts diff --git a/packages/app-operation/src/components/DeliveryTemplate/ConfigPanel.tsx b/packages/app-operation/src/components/DeliveryTemplate/ConfigPanel.tsx new file mode 100644 index 0000000..1dc137b --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/ConfigPanel.tsx @@ -0,0 +1,354 @@ +import React from 'react'; +import { Card, Checkbox, Empty, Form, Input, Switch } from 'antd'; + +interface ConfigPanelProps { + selectedModule: any; + onConfigChange: (moduleId: string, config: any) => void; +} + +const ConfigPanel: React.FC = ({ + selectedModule, + onConfigChange, +}) => { + const [form] = Form.useForm(); + + React.useEffect(() => { + if (selectedModule) { + form.setFieldsValue(selectedModule.config); + } + }, [selectedModule, form]); + + const handleValuesChange = (changedValues: any, allValues: any) => { + if (selectedModule) { + onConfigChange(selectedModule.id, { + ...selectedModule.config, + ...allValues, + }); + } + }; + + const renderTitleConfig = () => ( +
+
标题配置
+ + + +
+ ); + + const renderDealerInfoConfig = () => ( +
+
经销商信息配置
+ + + + + + + + + + + + + + + +
+ ); + + const renderWeightInfoConfig = () => ( +
+
重量信息配置
+ + + + + + + + + + + + + + + + + + + + + +
+ ); + + const renderPackingSpecConfig = () => ( +
+
表格列配置
+ + + + + + + + + + + + + + + + + + + + + +
+ ); + + const renderOtherFeesConfig = () => ( +
+
费用项目配置
+ + + + + +
+ 商标 +
+
+ 人工 +
+
+ 纸箱 +
+
+ 费用 +
+
+ 打码费 +
+
+
+ + + +
+ ); + + const renderModuleConfig = () => { + if (!selectedModule) return null; + + const configs = { + title: renderTitleConfig(), + dealerInfo: renderDealerInfoConfig(), + shippingInfo: ( +
+
发货信息配置
+ + + + + + +
+ ), + weightInfo: renderWeightInfoConfig(), + packingSpec: renderPackingSpecConfig(), + vehicleInfo: ( +
+
车辆信息配置
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ ), + otherFees: renderOtherFeesConfig(), + totalAmount: ( +
+
合计金额配置
+ + + + + + + + + +
+ ), + otherInfo: ( +
+
其他信息配置
+ + + + + + + + + + + + + + + + + + +
+ ), + } as any; + + return configs[selectedModule.type] ||
该模块暂无配置项
; + }; + + if (!selectedModule) { + return ( + + + + ); + } + + return ( + +
+ {renderModuleConfig()} +
+
+ ); +}; + +export default ConfigPanel; diff --git a/packages/app-operation/src/components/DeliveryTemplate/DealerInfoModule.tsx b/packages/app-operation/src/components/DeliveryTemplate/DealerInfoModule.tsx new file mode 100644 index 0000000..75a53ab --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/DealerInfoModule.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import { Button, Typography } from 'antd'; +import { + MenuOutlined, + ArrowUpOutlined, + ArrowDownOutlined, + DeleteOutlined +} from '@ant-design/icons'; + +const { Text } = Typography; + +interface ModuleProps { + config: any; + isSelected: boolean; + onSelect: () => void; + onDelete: () => void; + onMoveUp?: () => void; + onMoveDown?: () => void; + canMoveUp?: boolean; + canMoveDown?: boolean; +} + +const DealerInfoModule: React.FC = ({ + config, + isSelected, + onSelect, + onDelete, + onMoveUp, + onMoveDown, + canMoveUp, + canMoveDown, +}) => { + return ( +
+
+ 经销商信息模块 +
+
+
+
+
+ {config.showDealerName && ( + {config.dealerName} + )} + {config.showWatermelonGrade && ( + {config.watermelonGrade} + )} + {config.showDestination && ( + {config.destination} + )} + {config.showVehicleNumber && ( + {config.vehicleNumber} + )} +
+
+
+ ); +}; + +export default DealerInfoModule; diff --git a/packages/app-operation/src/components/DeliveryTemplate/ModuleLibrary.tsx b/packages/app-operation/src/components/DeliveryTemplate/ModuleLibrary.tsx new file mode 100644 index 0000000..9cdcf1d --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/ModuleLibrary.tsx @@ -0,0 +1,112 @@ +import React from 'react'; +import { Card, List, Typography } from 'antd'; +import { + FileTextOutlined, + ShopOutlined, + EnvironmentOutlined, + InboxOutlined, + TableOutlined, + CarOutlined, + DollarOutlined, + InfoCircleOutlined, +} from '@ant-design/icons'; + +const { Text } = Typography; + +interface ModuleLibraryItem { + type: string; + name: string; + icon: React.ReactNode; + description: string; +} + +interface ModuleLibraryProps { + onModuleAdd: (moduleType: string) => void; +} + +const moduleLibrary: ModuleLibraryItem[] = [ + { + type: 'title', + name: '发货单标题', + icon: React.createElement(FileTextOutlined), + description: '发货单主标题', + }, + { + type: 'dealerInfo', + name: '经销商信息', + icon: React.createElement(ShopOutlined), + description: '经销商名称、车次等信息', + }, + { + type: 'shippingInfo', + name: '发货地信息', + icon: React.createElement(EnvironmentOutlined), + description: '发货地、日期等信息', + }, + { + type: 'weightInfo', + name: '重量金额信息', + icon: React.createElement(InboxOutlined), + description: '毛重、净重、单价、金额', + }, + { + type: 'packingSpec', + name: '装箱规格表格', + icon: React.createElement(TableOutlined), + description: '箱号、数量、单价表格', + }, + { + type: 'vehicleInfo', + name: '车辆信息', + icon: React.createElement(CarOutlined), + description: '司机、车牌、运费信息', + }, + { + type: 'otherFees', + name: '其他费用明细', + icon: React.createElement(DollarOutlined), + description: '商标、人工、纸箱等费用', + }, + { + type: 'totalAmount', + name: '合计金额', + icon: React.createElement(DollarOutlined), + description: '总金额显示', + }, + { + type: 'otherInfo', + name: '其他信息', + icon: React.createElement(InfoCircleOutlined), + description: '产地、供应商等信息', + }, +]; + +const ModuleLibrary: React.FC = ({ onModuleAdd }) => { + return ( + + ( +
onModuleAdd(module.type)} + > +
{module.icon}
+
+
+ {module.name} +
+
+ + {module.description} + +
+
+
+ )} + /> +
+ ); +}; + +export default ModuleLibrary; diff --git a/packages/app-operation/src/components/DeliveryTemplate/OtherFeesModule.tsx b/packages/app-operation/src/components/DeliveryTemplate/OtherFeesModule.tsx new file mode 100644 index 0000000..9852b87 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/OtherFeesModule.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { Button, Typography } from 'antd'; +import { + MenuOutlined, + ArrowUpOutlined, + ArrowDownOutlined, + DeleteOutlined +} from '@ant-design/icons'; + +const { Text } = Typography; + +interface ModuleProps { + config: any; + isSelected: boolean; + onSelect: () => void; + onDelete: () => void; + onMoveUp?: () => void; + onMoveDown?: () => void; + canMoveUp?: boolean; + canMoveDown?: boolean; +} + +const OtherFeesModule: React.FC = ({ + config, + isSelected, + onSelect, + onDelete, + onMoveUp, + onMoveDown, + canMoveUp, + canMoveDown, +}) => { + const feeLabels = { + trademark: '商标', + labor: '人工', + paperBox: '纸箱', + fee: '费用', + codingFee: '打码费', + } as any; + + return ( +
+
+ 其他费用明细模块 +
+
+
+
+
+ {config.feeItems.map((feeType: any) => ( +
+ {feeLabels[feeType]}: +
+ {config[feeType]} + +
+
+ ))} +
+
+
+ ); +}; + +export default OtherFeesModule; diff --git a/packages/app-operation/src/components/DeliveryTemplate/OtherInfoModule.tsx b/packages/app-operation/src/components/DeliveryTemplate/OtherInfoModule.tsx new file mode 100644 index 0000000..98112c8 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/OtherInfoModule.tsx @@ -0,0 +1,145 @@ +import React from 'react'; +import { Button, Typography } from 'antd'; +import { + MenuOutlined, + ArrowUpOutlined, + ArrowDownOutlined, + DeleteOutlined +} from '@ant-design/icons'; + +const { Text } = Typography; + +interface ModuleProps { + config: any; + isSelected: boolean; + onSelect: () => void; + onDelete: () => void; + onMoveUp?: () => void; + onMoveDown?: () => void; + canMoveUp?: boolean; + canMoveDown?: boolean; +} + +const OtherInfoModule: React.FC = ({ + config, + isSelected, + onSelect, + onDelete, + onMoveUp, + onMoveDown, + canMoveUp, + canMoveDown, +}) => { + return ( +
+
+ 其他信息模块 +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
车次:第175车
收货地:北京果多美
产地:内蒙
供应商:北京新发龙盛商贸有限公司
发车时间:9月8号
到达时间:9月9号
品名:A级-麒麟瓜
发货重量:以公司入库重量为准。
净瓜单价:3元/斤
大约重量:29062
箱数688
车号:蒙L80367
手机号:15849849656
+
+
+ ); +}; + +export default OtherInfoModule; diff --git a/packages/app-operation/src/components/DeliveryTemplate/PackingSpecModule.tsx b/packages/app-operation/src/components/DeliveryTemplate/PackingSpecModule.tsx new file mode 100644 index 0000000..03a1889 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/PackingSpecModule.tsx @@ -0,0 +1,118 @@ +import React from 'react'; +import { Button, Typography } from 'antd'; +import { + MenuOutlined, + ArrowUpOutlined, + ArrowDownOutlined, + DeleteOutlined +} from '@ant-design/icons'; + +const { Text } = Typography; + +interface ModuleProps { + config: any; + isSelected: boolean; + onSelect: () => void; + onDelete: () => void; + onMoveUp?: () => void; + onMoveDown?: () => void; + canMoveUp?: boolean; + canMoveDown?: boolean; +} + +const PackingSpecModule: React.FC = ({ + config, + isSelected, + onSelect, + onDelete, + onMoveUp, + onMoveDown, + canMoveUp, + canMoveDown, +}) => { + const columns = []; + if (config.showBoxCategory) columns.push(''); + if (config.showBoxType) columns.push('箱号'); + if (config.showQuantity) columns.push('数量'); + if (config.showUnitPrice) columns.push('单价'); + if (config.showAmount) columns.push('金额'); + if (config.showUnitWeight) columns.push('单重'); + if (config.showWeight) columns.push('重量'); + + return ( +
+
+ 装箱规格表格模块 +
+
+
+
+ + + + {columns.map((col) => ( + + ))} + + + + {config.data.map((row: any, index: any) => ( + + {config.showBoxCategory && } + {config.showBoxType && } + {config.showQuantity && } + {config.showUnitPrice && } + {config.showAmount && } + {config.showUnitWeight && } + {config.showWeight && } + + ))} + +
{col}
{row.boxCategory}{row.boxType}{row.quantity}{row.unitPrice}{row.amount}{row.unitWeight}{row.weight}
+
+
+ ); +}; + +export default PackingSpecModule; diff --git a/packages/app-operation/src/components/DeliveryTemplate/PreviewCanvas.tsx b/packages/app-operation/src/components/DeliveryTemplate/PreviewCanvas.tsx new file mode 100644 index 0000000..cd20067 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/PreviewCanvas.tsx @@ -0,0 +1,164 @@ +import React, { Suspense } from 'react'; +import { Card, Empty } from 'antd'; +import { + DragDropContext, + Droppable, + Draggable, + DropResult, +} from 'react-beautiful-dnd'; + +// 直接导入组件而不是使用 React.lazy +import TitleModule from './TitleModule'; +import DealerInfoModule from './DealerInfoModule'; +import ShippingInfoModule from './ShippingInfoModule'; +import WeightInfoModule from './WeightInfoModule'; +import PackingSpecModule from './PackingSpecModule'; +import VehicleInfoModule from './VehicleInfoModule'; +import OtherFeesModule from './OtherFeesModule'; +import TotalAmountModule from './TotalAmountModule'; +import OtherInfoModule from './OtherInfoModule'; + +interface PreviewCanvasProps { + modules: any[]; + selectedModule: any; + onModuleSelect: (module: any) => void; + onModuleDelete: (moduleId: string) => void; + onModuleReorder: (newModules: any[]) => void; +} + +const PreviewCanvas: React.FC = ({ + modules, + selectedModule, + onModuleSelect, + onModuleDelete, + onModuleReorder, +}) => { + const handleDragEnd = (result: DropResult) => { + // 如果没有拖拽到有效位置,则直接返回 + if (!result.destination) { + return; + } + + const items = Array.from(modules); + const [reorderedItem] = items.splice(result.source.index, 1); + items.splice(result.destination.index, 0, reorderedItem); + + onModuleReorder(items); + }; + + const renderModule = (module: any, index: number) => { + const props = { + config: module.config, + isSelected: selectedModule?.id === module.id, + onSelect: () => onModuleSelect(module), + onDelete: () => onModuleDelete(module.id), + onMoveUp: () => { + if (index > 0) { + const newModules = [...modules]; + const temp = newModules[index]; + newModules[index] = newModules[index - 1]; + newModules[index - 1] = temp; + onModuleReorder(newModules); + } + }, + onMoveDown: () => { + if (index < modules.length - 1) { + const newModules = [...modules]; + const temp = newModules[index]; + newModules[index] = newModules[index + 1]; + newModules[index + 1] = temp; + onModuleReorder(newModules); + } + }, + canMoveUp: index > 0, + canMoveDown: index < modules.length - 1, + }; + + // 创建组件映射对象 + const componentMap = { + title: TitleModule, + dealerInfo: DealerInfoModule, + shippingInfo: ShippingInfoModule, + weightInfo: WeightInfoModule, + packingSpec: PackingSpecModule, + vehicleInfo: VehicleInfoModule, + otherFees: OtherFeesModule, + totalAmount: TotalAmountModule, + otherInfo: OtherInfoModule, + }; + + const Component = componentMap[module.type as keyof typeof componentMap]; + + return ( + + {(provided, snapshot) => ( +
+ {Component ? : null} +
+ )} +
+ ); + }; + + // 为 Droppable 组件创建一个不使用 memo 的包装组件 + const DroppableWrapper = ({ children }: { children: React.ReactNode }) => ( + + {(provided, snapshot) => ( +
+ {children} + {provided.placeholder} +
+ )} +
+ ); + + return ( + + {modules.length === 0 ? ( +
+ +
+ ) : ( + + + {modules.map((module, index) => renderModule(module, index))} + + + )} +
+ ); +}; + +export default PreviewCanvas; diff --git a/packages/app-operation/src/components/DeliveryTemplate/PrintPreview.tsx b/packages/app-operation/src/components/DeliveryTemplate/PrintPreview.tsx new file mode 100644 index 0000000..ca65d57 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/PrintPreview.tsx @@ -0,0 +1,539 @@ +import React from 'react'; +import { Button, message } from 'antd'; +import { PrinterOutlined } from '@ant-design/icons'; + +interface PrintPreviewProps { + modules: any[]; +} + +const PrintPreview: React.FC = ({ modules }) => { + const handlePrint = () => { + // 创建一个用于打印的隐藏iframe + const printWindow = window.open('', '_blank'); + if (printWindow) { + // 构建打印内容 + let printContent = ` + + + 发货单打印预览 + + + + + + + + + + `; + + // 写入内容到打印窗口 + printWindow.document.write(printContent); + printWindow.document.close(); + } else { + message.error('无法打开打印预览窗口,请检查浏览器设置'); + } + }; + + return ( + + ); +}; + +export default PrintPreview; diff --git a/packages/app-operation/src/components/DeliveryTemplate/ShippingInfoModule.tsx b/packages/app-operation/src/components/DeliveryTemplate/ShippingInfoModule.tsx new file mode 100644 index 0000000..54a2b68 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/ShippingInfoModule.tsx @@ -0,0 +1,105 @@ +import React from 'react'; +import { Button, Typography } from 'antd'; +import { + MenuOutlined, + ArrowUpOutlined, + ArrowDownOutlined, + DeleteOutlined +} from '@ant-design/icons'; + +const { Text } = Typography; + +interface ModuleProps { + config: any; + isSelected: boolean; + onSelect: () => void; + onDelete: () => void; + onMoveUp?: () => void; + onMoveDown?: () => void; + canMoveUp?: boolean; + canMoveDown?: boolean; +} + +const ShippingInfoModule: React.FC = ({ + config, + isSelected, + onSelect, + onDelete, + onMoveUp, + onMoveDown, + canMoveUp, + canMoveDown, +}) => { + return ( +
+
+ 发货地信息模块 +
+
+
+
+
+ {config.showShippingFrom && ( +
+ 发货地: +
+ {config.shippingFrom} +
+
+ )} + {config.showDate && ( +
+ 日期: +
+ {config.date} +
+
+ )} +
+
+
+ ); +}; + +export default ShippingInfoModule; diff --git a/packages/app-operation/src/components/DeliveryTemplate/TitleModule.tsx b/packages/app-operation/src/components/DeliveryTemplate/TitleModule.tsx new file mode 100644 index 0000000..4d2af75 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/TitleModule.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { Button, Typography } from 'antd'; +import { + MenuOutlined, + ArrowUpOutlined, + ArrowDownOutlined, + DeleteOutlined +} from '@ant-design/icons'; + +const { Text } = Typography; + +interface ModuleProps { + config: any; + isSelected: boolean; + onSelect: () => void; + onDelete: () => void; + onMoveUp?: () => void; + onMoveDown?: () => void; + canMoveUp?: boolean; + canMoveDown?: boolean; +} + +const TitleModule: React.FC = ({ + config, + isSelected, + onSelect, + onDelete, + onMoveUp, + onMoveDown, + canMoveUp, + canMoveDown, +}) => { + + return ( +
+
+ 标题模块 +
+
+
+
+
+ {config.text} +
+
+
+ ); +}; + +export default TitleModule; diff --git a/packages/app-operation/src/components/DeliveryTemplate/TotalAmountModule.tsx b/packages/app-operation/src/components/DeliveryTemplate/TotalAmountModule.tsx new file mode 100644 index 0000000..2bc7ca5 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/TotalAmountModule.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { Button, Typography } from 'antd'; +import { + MenuOutlined, + ArrowUpOutlined, + ArrowDownOutlined, + DeleteOutlined +} from '@ant-design/icons'; + +const { Text } = Typography; + +interface ModuleProps { + config: any; + isSelected: boolean; + onSelect: () => void; + onDelete: () => void; + onMoveUp?: () => void; + onMoveDown?: () => void; + canMoveUp?: boolean; + canMoveDown?: boolean; +} + +const TotalAmountModule: React.FC = ({ + config, + isSelected, + onSelect, + onDelete, + onMoveUp, + onMoveDown, + canMoveUp, + canMoveDown, +}) => { + return ( +
+
+ 合计金额模块 +
+
+
+
+
+
+ {config.sumTitle}: +
+ {config.amount} + +
+
+ {config.showFarmer && ( +
+ 瓜农: +
+ {config.farmer} +
+
+ )} +
+
+
+ ); +}; + +export default TotalAmountModule; diff --git a/packages/app-operation/src/components/DeliveryTemplate/VehicleInfoModule.tsx b/packages/app-operation/src/components/DeliveryTemplate/VehicleInfoModule.tsx new file mode 100644 index 0000000..7796c92 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/VehicleInfoModule.tsx @@ -0,0 +1,179 @@ +import { + ArrowDownOutlined, + ArrowUpOutlined, + DeleteOutlined, + MenuOutlined, +} from '@ant-design/icons'; +import { Button, Typography } from 'antd'; +import React from 'react'; + +const { Text } = Typography; + +interface ModuleProps { + config: any; + isSelected: boolean; + onSelect: () => void; + onDelete: () => void; + onMoveUp?: () => void; + onMoveDown?: () => void; + canMoveUp?: boolean; + canMoveDown?: boolean; +} + +const VehicleInfoModule: React.FC = ({ + config, + isSelected, + onSelect, + onDelete, + onMoveUp, + onMoveDown, + canMoveUp, + canMoveDown, +}) => { + return ( +
+
+ 车辆信息模块 +
+
+
+
+
+ {config.showDriverPhone && ( +
+ + 司机号码: + +
+ + {config.driverPhone} + +
+
+ )} + {config.showLicensePlate && ( +
+ + 车牌: + +
+ + {config.licensePlate} + +
+
+ )} + {config.showEstimatedArrivalTime && ( +
+ + 预计到仓时间: + +
+ + {config.estimatedArrivalTime} + +
+
+ )} +
+
+ {config.showFreightDebt && ( +
+ + {config.freightDebtTitle || '运费欠'}: + +
+ + {config.freightDebt} + + +
+
+ )} + {config.showRemarks && ( +
+ + 备注: + +
+ + {config.remarks} + +
+
+ )} + {config.showSeller && ( +
+ + 卖货: + +
+ + {config.seller} + +
+
+ )} + {config.showStrawMatDebt && ( +
+ + 草帘欠: + +
+ + {config.strawMatDebt} + + +
+
+ )} +
+
+
+ ); +}; + +export default VehicleInfoModule; diff --git a/packages/app-operation/src/components/DeliveryTemplate/WeightInfoModule.tsx b/packages/app-operation/src/components/DeliveryTemplate/WeightInfoModule.tsx new file mode 100644 index 0000000..ea5e4b3 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/WeightInfoModule.tsx @@ -0,0 +1,150 @@ +import React from 'react'; +import { Button, Typography } from 'antd'; +import { + MenuOutlined, + ArrowUpOutlined, + ArrowDownOutlined, + DeleteOutlined +} from '@ant-design/icons'; + +const { Text } = Typography; + +interface ModuleProps { + config: any; + isSelected: boolean; + onSelect: () => void; + onDelete: () => void; + onMoveUp?: () => void; + onMoveDown?: () => void; + canMoveUp?: boolean; + canMoveDown?: boolean; +} + +const WeightInfoModule: React.FC = ({ + config, + isSelected, + onSelect, + onDelete, + onMoveUp, + onMoveDown, + canMoveUp, + canMoveDown, +}) => { + return ( +
+
+ 重量金额信息模块 +
+
+
+
+
+ {config.showGrossWeight && ( +
+ 毛重: +
+ {config.grossWeight} + +
+
+ )} + {config.showBoxWeight && ( +
+ 箱重: +
+ {config.boxWeight} + +
+
+ )} + {config.showNetWeight && ( +
+ 净重: +
+ {config.netWeight} + +
+
+ )} + {config.showUnitPrice && ( +
+ 单价: +
+ {config.unitPrice} + 元/斤 +
+
+ )} + {config.showAmount && ( +
+ 金额: +
+ {config.amount} + +
+
+ )} + {config.showGrade && ( +
+ 品级: +
+ {config.grade} +
+
+ )} + {config.showAccountCompany && ( +
+ 入账公司: +
+ {config.accountCompany} +
+
+ )} +
+
+
+ ); +}; + +export default WeightInfoModule; diff --git a/packages/app-operation/src/components/DeliveryTemplate/index.ts b/packages/app-operation/src/components/DeliveryTemplate/index.ts new file mode 100644 index 0000000..8b1f603 --- /dev/null +++ b/packages/app-operation/src/components/DeliveryTemplate/index.ts @@ -0,0 +1,15 @@ +export { default as ModuleLibrary } from './ModuleLibrary'; +export { default as PreviewCanvas } from './PreviewCanvas'; +export { default as ConfigPanel } from './ConfigPanel'; +export { default as PrintPreview } from './PrintPreview'; + +// Individual module components +export { default as TitleModule } from './TitleModule'; +export { default as DealerInfoModule } from './DealerInfoModule'; +export { default as ShippingInfoModule } from './ShippingInfoModule'; +export { default as WeightInfoModule } from './WeightInfoModule'; +export { default as PackingSpecModule } from './PackingSpecModule'; +export { default as VehicleInfoModule } from './VehicleInfoModule'; +export { default as OtherFeesModule } from './OtherFeesModule'; +export { default as TotalAmountModule } from './TotalAmountModule'; +export { default as OtherInfoModule } from './OtherInfoModule'; \ No newline at end of file diff --git a/packages/app-operation/src/components/index.ts b/packages/app-operation/src/components/index.ts index 7d7ce58..5422937 100644 --- a/packages/app-operation/src/components/index.ts +++ b/packages/app-operation/src/components/index.ts @@ -18,3 +18,4 @@ export * from './Role'; export * from './Setting'; export * from './User'; export * from './Purchase'; +export * from './DeliveryTemplate'; diff --git a/packages/app-operation/src/global.less b/packages/app-operation/src/global.less index 4c20377..aa9753b 100644 --- a/packages/app-operation/src/global.less +++ b/packages/app-operation/src/global.less @@ -47,170 +47,3 @@ body::-webkit-scrollbar { padding: 12px !important; } } - - -// 发货单模板配置 -.module-library { - background: white; - border-radius: 6px; - padding: 16px; - height: 100%; -} -.module-item { - padding: 12px; - border: 1px solid #d9d9d9; - border-radius: 6px; - margin-bottom: 8px; - cursor: pointer; - transition: all 0.3s; - display: flex; - align-items: center; -} -.module-item:hover { - border-color: #1890ff; - background-color: #f0f8ff; -} -.module-icon { - margin-right: 12px; - font-size: 16px; - color: #1890ff; -} -.preview-canvas { - background: white; - border-radius: 6px; - padding: 16px; - min-height: 600px; - border: 1px solid #d9d9d9; -} -.config-panel { - background: white; - border-radius: 6px; - padding: 16px; - height: 100%; -} -.preview-module { - margin-bottom: 16px; - border: 1px solid #d9d9d9; - border-radius: 6px; - overflow: hidden; -} -.preview-module.selected { - border: 2px solid #1890ff; -} -.module-header { - background: #fafafa; - padding: 8px 16px; - border-bottom: 1px solid #d9d9d9; - display: flex; - justify-content: space-between; - align-items: center; -} -.module-content { - padding: 16px; -} -.empty-preview { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 400px; - color: #bfbfbf; -} -.template-preview { - width: 100%; - border-collapse: collapse; - margin-top: 10px; -} -.template-preview th, .template-preview td { - border: 1px solid #d9d9d9; - padding: 8px 12px; - text-align: left; -} -.template-preview th { - background-color: #fafafa; - font-weight: 500; -} -.ant-divider { - margin: 16px 0; -} -.config-section { - margin-bottom: 20px; -} -.config-section-title { - font-weight: 500; - margin-bottom: 12px; - color: #262626; -} -.field-row { - display: flex; - align-items: center; - margin-bottom: 8px; -} -.field-label { - width: 100px; - color: #595959; -} -.field-control { - flex: 1; -} -.checkbox-group { - display: flex; - flex-direction: column; - gap: 8px; -} -.checkbox-item { - display: flex; - align-items: center; -} -.template-title { - text-align: center; - margin: 10px 0; - font-weight: bold; -} -.template-title.large { - font-size: 20px; -} -.template-title.medium { - font-size: 18px; -} -.template-title.small { - font-size: 16px; -} -.template-title.left { - text-align: left; -} -.template-title.center { - text-align: center; -} -.template-title.right { - text-align: right; -} -.info-row { - display: flex; - margin-bottom: 8px; -} -.info-label { - width: 80px; - color: #595959; -} -.info-value { - flex: 1; -} -.weight-info { - display: flex; - flex-wrap: wrap; - gap: 20px; -} -.weight-item { - display: flex; - align-items: center; -} -.fee-list { - display: flex; - flex-wrap: wrap; - gap: 12px; -} -.fee-item { - display: flex; - align-items: center; -} diff --git a/packages/app-operation/src/pages/DeliveryTemplateCreate.tsx b/packages/app-operation/src/pages/DeliveryTemplateCreate.tsx index 41bbc74..713e590 100644 --- a/packages/app-operation/src/pages/DeliveryTemplateCreate.tsx +++ b/packages/app-operation/src/pages/DeliveryTemplateCreate.tsx @@ -1,123 +1,14 @@ import { PageContainer } from '@/components'; import { - ArrowDownOutlined, - ArrowUpOutlined, - CarOutlined, - DeleteOutlined, - DollarOutlined, - EnvironmentOutlined, - EyeOutlined, - FileTextOutlined, - InboxOutlined, - InfoCircleOutlined, - MenuOutlined, - SaveOutlined, - ShopOutlined, - TableOutlined, -} from '@ant-design/icons'; + ConfigPanel, + ModuleLibrary, + PreviewCanvas, + PrintPreview, +} from '@/components/DeliveryTemplate'; +import { SaveOutlined } from '@ant-design/icons'; import { ProCard, ProFormText } from '@ant-design/pro-components'; -import { - Button, - Card, - Checkbox, - Col, - Empty, - Form, - Input, - List, - message, - Row, - Select, - Space, - Switch, - Typography, -} from 'antd'; -import React, { useState } from 'react'; -import { - DragDropContext, - Draggable, - Droppable, - DropResult, -} from 'react-beautiful-dnd'; - -const { Text } = Typography; -const { Option } = Select; - -// 模块库数据 -interface ModuleLibraryItem { - type: string; - name: string; - icon: React.ReactNode; - description: string; -} - -interface ModuleProps { - config: any; - isSelected: boolean; - onSelect: () => void; - onDelete: () => void; - onMoveUp?: () => void; - onMoveDown?: () => void; - canMoveUp?: boolean; - canMoveDown?: boolean; -} - -const moduleLibrary: ModuleLibraryItem[] = [ - { - type: 'title', - name: '发货单标题', - icon: React.createElement(FileTextOutlined), - description: '发货单主标题', - }, - { - type: 'dealerInfo', - name: '经销商信息', - icon: React.createElement(ShopOutlined), - description: '经销商名称、车次等信息', - }, - { - type: 'shippingInfo', - name: '发货地信息', - icon: React.createElement(EnvironmentOutlined), - description: '发货地、日期等信息', - }, - { - type: 'weightInfo', - name: '重量金额信息', - icon: React.createElement(InboxOutlined), - description: '毛重、净重、单价、金额', - }, - { - type: 'packingSpec', - name: '装箱规格表格', - icon: React.createElement(TableOutlined), - description: '箱型、数量、单价表格', - }, - { - type: 'vehicleInfo', - name: '车辆信息', - icon: React.createElement(CarOutlined), - description: '司机、车牌、运费信息', - }, - { - type: 'otherFees', - name: '其他费用明细', - icon: React.createElement(DollarOutlined), - description: '商标、人工、纸箱等费用', - }, - { - type: 'totalAmount', - name: '合计金额', - icon: React.createElement(DollarOutlined), - description: '总金额显示', - }, - { - type: 'otherInfo', - name: '其他信息', - icon: React.createElement(InfoCircleOutlined), - description: '产地、供应商等信息', - }, -]; +import { Button, Col, message, Row, Space, Typography } from 'antd'; +import { useState } from 'react'; // 默认模块配置 interface ModuleConfig { @@ -135,9 +26,11 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => { dealerName: '盛京水果', vehicleNumber: '第188车', destination: '宁夏', + watermelonGrade: 'A级', showDealerName: true, showVehicleNumber: true, showDestination: true, + showWatermelonGrade: true, }, shippingInfo: { shippingFrom: '宁夏', @@ -151,14 +44,19 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => { netWeight: '53569', unitPrice: '1.60', amount: '85710', + grade: 'A', + accountCompany: '公司名称', showGrossWeight: true, showBoxWeight: true, showNetWeight: true, showUnitPrice: true, showAmount: true, + showGrade: true, + showAccountCompany: true, }, packingSpec: { columns: [ + 'boxCategory', 'boxType', 'quantity', 'unitPrice', @@ -166,6 +64,7 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => { 'unitWeight', 'weight', ], + showBoxCategory: true, showBoxType: true, showQuantity: true, showUnitPrice: true, @@ -174,7 +73,8 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => { showWeight: true, data: [ { - boxType: '4粒-1号', + boxCategory: '4粒', + boxType: '1号', quantity: '376', unitPrice: '8.70', amount: '3,271', @@ -182,7 +82,8 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => { weight: '1,015', }, { - boxType: '2粒-A', + boxCategory: '2粒', + boxType: 'A', quantity: '700', unitPrice: '6.30', amount: '4,410', @@ -190,7 +91,8 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => { weight: '1,050', }, { - boxType: '2粒-AA', + boxCategory: '2粒', + boxType: 'AA', quantity: '456', unitPrice: '6.60', amount: '3,010', @@ -202,10 +104,19 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => { vehicleInfo: { driverPhone: '13800138000', licensePlate: '京A12345', - freight: '11000', + estimatedArrivalTime: '2025年9月24日', + freightDebt: '11000', + seller: '卖货', + strawMatDebt: '100', + remarks: '备注', + freightDebtTitle: '运费欠', showDriverPhone: true, showLicensePlate: true, - showFreight: true, + showEstimatedArrivalTime: true, + showFreightDebt: true, + showSeller: true, + showStrawMatDebt: true, + showRemarks: true, }, otherFees: { enabled: true, @@ -219,8 +130,10 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => { }, totalAmount: { enabled: true, + sumTitle: '合计金额', amount: '102,263', - position: 'bottom', + farmer: '李荣赞', + showFarmer: true, }, otherInfo: { enabled: false, @@ -240,1370 +153,7 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => { return { ...defaults[moduleType] }; }; -// 标题模块组件 -interface ModuleProps { - config: any; - isSelected: boolean; - onSelect: () => void; - onDelete: () => void; - onMoveUp?: () => void; - onMoveDown?: () => void; - canMoveUp?: boolean; - canMoveDown?: boolean; - dragIndex?: number; - onDragStart?: (index: number) => void; - onDragEnter?: (index: number) => void; - onDragEnd?: () => void; -} - -const TitleModule: React.FC = ({ - config, - isSelected, - onSelect, - onDelete, - onMoveUp, - onMoveDown, - canMoveUp, - canMoveDown, -}) => { - return ( -
-
- 标题模块 -
-
-
-
-
- {config.text} -
-
-
- ); -}; - -// 经销商信息模块 -const DealerInfoModule: React.FC = ({ - config, - isSelected, - onSelect, - onDelete, - onMoveUp, - onMoveDown, - canMoveUp, - canMoveDown, -}) => { - return ( -
-
- 经销商信息模块 -
-
-
-
- {config.showDealerName && ( -
-
经销商:
-
{config.dealerName}
-
- )} - {config.showVehicleNumber && ( -
-
车次:
-
{config.vehicleNumber}
-
- )} - {config.showDestination && ( -
-
收货地:
-
{config.destination}
-
- )} -
-
- ); -}; - -// 发货地信息模块 -const ShippingInfoModule: React.FC = ({ - config, - isSelected, - onSelect, - onDelete, - onMoveUp, - onMoveDown, - canMoveUp, - canMoveDown, -}) => { - return ( -
-
- 发货地信息模块 -
-
-
-
- {config.showShippingFrom && ( -
-
发货地:
-
{config.shippingFrom}
-
- )} - {config.showDate && ( -
-
日期:
-
{config.date}
-
- )} -
-
- ); -}; - -// 重量金额信息模块 -const WeightInfoModule: React.FC = ({ - config, - isSelected, - onSelect, - onDelete, - onMoveUp, - onMoveDown, - canMoveUp, - canMoveDown, -}) => { - return ( -
-
- 重量金额信息模块 -
-
-
-
-
- {config.showGrossWeight && ( -
- 毛重: {config.grossWeight} 斤 -
- )} - {config.showBoxWeight && ( -
- 箱重: {config.boxWeight} 斤 -
- )} - {config.showNetWeight && ( -
- 净重: {config.netWeight} 斤 -
- )} - {config.showUnitPrice && ( -
- 单价: {config.unitPrice} 元/斤 -
- )} - {config.showAmount && ( -
- 金额: {config.amount} 元 -
- )} -
-
-
- ); -}; - -// 装箱规格表格模块 -const PackingSpecModule: React.FC = ({ - config, - isSelected, - onSelect, - onDelete, - onMoveUp, - onMoveDown, - canMoveUp, - canMoveDown, -}) => { - const columns = []; - if (config.showBoxType) columns.push('箱型'); - if (config.showQuantity) columns.push('数量'); - if (config.showUnitPrice) columns.push('单价'); - if (config.showAmount) columns.push('金额'); - if (config.showUnitWeight) columns.push('单重'); - if (config.showWeight) columns.push('重量'); - - return ( -
-
- 装箱规格表格模块 -
-
-
-
- - - - {columns.map((col) => ( - - ))} - - - - {config.data.map((row: any, index: any) => ( - - {config.showBoxType && } - {config.showQuantity && } - {config.showUnitPrice && } - {config.showAmount && } - {config.showUnitWeight && } - {config.showWeight && } - - ))} - -
{col}
{row.boxType}{row.quantity}{row.unitPrice}{row.amount}{row.unitWeight}{row.weight}
-
-
- ); -}; - -// 车辆信息模块 -const VehicleInfoModule: React.FC = ({ - config, - isSelected, - onSelect, - onDelete, - onMoveUp, - onMoveDown, - canMoveUp, - canMoveDown, -}) => { - return ( -
-
- 车辆信息模块 -
-
-
-
- {config.showDriverPhone && ( -
-
司机号码:
-
{config.driverPhone}
-
- )} - {config.showLicensePlate && ( -
-
车牌:
-
{config.licensePlate}
-
- )} - {config.showFreight && ( -
-
运费欠:
-
{config.freight} 元
-
- )} -
-
- ); -}; - -// 其他费用明细模块 -const OtherFeesModule: React.FC = ({ - config, - isSelected, - onSelect, - onDelete, - onMoveUp, - onMoveDown, - canMoveUp, - canMoveDown, -}) => { - const feeLabels = { - trademark: '商标', - labor: '人工', - paperBox: '纸箱', - fee: '费用', - codingFee: '打码费', - } as any; - - return ( -
-
- 其他费用明细模块 -
-
-
-
-
- {config.feeItems.map((feeType: any) => ( -
- - {feeLabels[feeType]}: {config[feeType]} 元 - -
- ))} -
-
-
- ); -}; - -// 合计金额模块 -const TotalAmountModule: React.FC = ({ - config, - isSelected, - onSelect, - onDelete, - onMoveUp, - onMoveDown, - canMoveUp, - canMoveDown, -}) => { - return ( -
-
- 合计金额模块 -
-
-
-
-
- 合计金额: {config.amount} 元 -
-
-
- ); -}; - -// 其他信息模块 -const OtherInfoModule: React.FC = ({ - config, - isSelected, - onSelect, - onDelete, - onMoveUp, - onMoveDown, - canMoveUp, - canMoveDown, -}) => { - return ( -
-
- 其他信息模块 -
-
-
-
- {config.showOrigin && ( -
-
产地:
-
{config.origin}
-
- )} - {config.showSupplier && ( -
-
供应商:
-
{config.supplier}
-
- )} - {config.showDepartureTime && ( -
-
发车时间:
-
{config.departureTime}
-
- )} - {config.showArrivalTime && ( -
-
预计到达:
-
{config.arrivalTime}
-
- )} - {config.showProductName && ( -
-
品名:
-
{config.productName}
-
- )} -
-
- ); -}; - -// 模块库组件 -interface ModuleLibraryProps { - onModuleAdd: (moduleType: string) => void; -} - -const ModuleLibrary: React.FC = ({ onModuleAdd }) => { - return ( - - ( -
onModuleAdd(module.type)}> -
{module.icon}
-
-
- {module.name} -
-
- - {module.description} - -
-
-
- )} - /> -
- ); -}; - -// 预览画布组件 -interface PreviewCanvasProps { - modules: any[]; - selectedModule: any; - onModuleSelect: (module: any) => void; - onModuleDelete: (moduleId: string) => void; - onModuleReorder: (newModules: any[]) => void; -} - -const PreviewCanvas: React.FC = ({ - modules, - selectedModule, - onModuleSelect, - onModuleDelete, - onModuleReorder, -}) => { - const handleDragEnd = (result: DropResult) => { - // 如果没有拖拽到有效位置,则直接返回 - if (!result.destination) { - return; - } - - const items = Array.from(modules); - const [reorderedItem] = items.splice(result.source.index, 1); - items.splice(result.destination.index, 0, reorderedItem); - - onModuleReorder(items); - }; - - const renderModule = (module: any, index: number) => { - const props = { - config: module.config, - isSelected: selectedModule?.id === module.id, - onSelect: () => onModuleSelect(module), - onDelete: () => onModuleDelete(module.id), - onMoveUp: () => { - if (index > 0) { - const newModules = [...modules]; - const temp = newModules[index]; - newModules[index] = newModules[index - 1]; - newModules[index - 1] = temp; - onModuleReorder(newModules); - } - }, - onMoveDown: () => { - if (index < modules.length - 1) { - const newModules = [...modules]; - const temp = newModules[index]; - newModules[index] = newModules[index + 1]; - newModules[index + 1] = temp; - onModuleReorder(newModules); - } - }, - canMoveUp: index > 0, - canMoveDown: index < modules.length - 1, - }; - - const components = { - title: TitleModule, - dealerInfo: DealerInfoModule, - shippingInfo: ShippingInfoModule, - weightInfo: WeightInfoModule, - packingSpec: PackingSpecModule, - vehicleInfo: VehicleInfoModule, - otherFees: OtherFeesModule, - totalAmount: TotalAmountModule, - otherInfo: OtherInfoModule, - } as any; - - const Component = components[module.type]; - - return ( - - {(provided, snapshot) => ( -
- {Component ? React.createElement(Component, props) : null} -
- )} -
- ); - }; - - return ( - - {modules.length === 0 ? ( -
- - 或拖拽模块到此区域 -
- ) : ( - - - {(provided, snapshot) => ( -
- {modules.map((module, index) => renderModule(module, index))} - {provided.placeholder} -
- )} -
-
- )} -
- ); -}; - -// 配置面板组件 -interface ConfigPanelProps { - selectedModule: any; - onConfigChange: (moduleId: string, config: any) => void; -} - -const ConfigPanel: React.FC = ({ - selectedModule, - onConfigChange, -}) => { - const [form] = Form.useForm(); - - React.useEffect(() => { - if (selectedModule) { - form.setFieldsValue(selectedModule.config); - } - }, [selectedModule, form]); - - const handleValuesChange = (changedValues: any, allValues: any) => { - if (selectedModule) { - onConfigChange(selectedModule.id, { - ...selectedModule.config, - ...allValues, - }); - } - }; - - const renderTitleConfig = () => ( -
-
标题配置
- - - - - - - - - -
- ); - - const renderDealerInfoConfig = () => ( -
-
经销商信息配置
- - - - - - - - - -
- ); - - const renderWeightInfoConfig = () => ( -
-
重量信息配置
- - - - - - - - - - - - - - - -
- ); - - const renderPackingSpecConfig = () => ( -
-
表格列配置
- - - - - - - - - - - - - - - - - - -
- ); - - const renderOtherFeesConfig = () => ( -
-
费用项目配置
- - - - - -
- 商标 -
-
- 人工 -
-
- 纸箱 -
-
- 费用 -
-
- 打码费 -
-
-
- - - -
- ); - - const renderModuleConfig = () => { - if (!selectedModule) return null; - - const configs = { - title: renderTitleConfig(), - dealerInfo: renderDealerInfoConfig(), - shippingInfo: ( -
-
发货信息配置
- - - - - - -
- ), - weightInfo: renderWeightInfoConfig(), - packingSpec: renderPackingSpecConfig(), - vehicleInfo: ( -
-
车辆信息配置
- - - - - - - - - -
- ), - otherFees: renderOtherFeesConfig(), - totalAmount: ( -
-
合计金额配置
- - - -
- ), - otherInfo: ( -
-
其他信息配置
- - - - - - - - - - - - - - - - - - -
- ), - } as any; - - return configs[selectedModule.type] ||
该模块暂无配置项
; - }; - - if (!selectedModule) { - return ( - - - - ); - } - - return ( - -
- {renderModuleConfig()} -
-
- ); -}; - export default function Page() { - // 添加样式 - const style = ` - .module-actions { - display: flex; - gap: 4px; - } - - .module-actions .ant-btn { - padding: 0 4px; - } - - .preview-module { - border: 1px solid #d9d9d9; - border-radius: 4px; - padding: 8px; - margin-bottom: 8px; - background: #fff; - transition: all 0.2s; - } - - .preview-module:hover { - border-color: #40a9ff; - } - - .preview-module.selected { - border-color: #1890ff; - box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); - } - - .module-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 8px; - padding-bottom: 8px; - border-bottom: 1px solid #f0f0f0; - } - - .module-content { - padding: 8px 0; - } - - .template-title { - font-weight: bold; - } - - .template-title.small { - font-size: 16px; - } - - .template-title.medium { - font-size: 20px; - } - - .template-title.large { - font-size: 24px; - } - - .template-title.left { - text-align: left; - } - - .template-title.center { - text-align: center; - } - - .template-title.right { - text-align: right; - } - - .info-row { - display: flex; - margin-bottom: 4px; - } - - .info-label { - font-weight: bold; - margin-right: 8px; - flex-shrink: 0; - } - - .info-value { - flex: 1; - } - - .weight-info { - display: flex; - flex-wrap: wrap; - gap: 8px; - } - - .weight-item { - flex: 1; - min-width: 120px; - } - - .template-preview { - width: 100%; - border-collapse: collapse; - } - - .template-preview th, - .template-preview td { - border: 1px solid #d9d9d9; - padding: 8px; - text-align: center; - } - - .template-preview th { - background-color: #fafafa; - font-weight: bold; - } - - .fee-list { - display: flex; - flex-direction: column; - gap: 4px; - } - - .module-library .module-item { - display: flex; - align-items: center; - padding: 8px; - cursor: pointer; - border: 1px solid #d9d9d9; - border-radius: 4px; - margin-bottom: 8px; - transition: all 0.2s; - } - - .module-library .module-item:hover { - border-color: #40a9ff; - background-color: #f0f8ff; - } - - .module-library .module-icon { - font-size: 18px; - margin-right: 8px; - color: #1890ff; - } - - .config-section { - margin-bottom: 16px; - padding-bottom: 16px; - border-bottom: 1px solid #f0f0f0; - } - - .config-section-title { - font-weight: bold; - margin-bottom: 8px; - color: #1890ff; - } - - .checkbox-group { - display: flex; - flex-direction: column; - gap: 4px; - } - `; - - React.useEffect(() => { - const styleElement = document.createElement('style'); - styleElement.innerHTML = style; - document.head.appendChild(styleElement); - - return () => { - if (styleElement.parentNode) { - styleElement.parentNode.removeChild(styleElement); - } - }; - }, []); // 主组件 const [modules, setModules] = useState([]); const [selectedModule, setSelectedModule] = useState(null); @@ -1668,7 +218,7 @@ export default function Page() { - + } >