refactor(dealer): 重构经销商模块配置面板

- 使用 BetaSchemaForm 替代原有 Form 组件
- 移除冗余的表单配置渲染逻辑
- 动态生成模块配置表单结构
- 优化模块配置项的展示与交互
- 统一配置面板的数据流处理方式
- 提升配置面板的可维护性和扩展性
This commit is contained in:
shenyifei 2025-11-07 00:36:09 +08:00
parent f7425f5961
commit 0c39c005bd
14 changed files with 933 additions and 628 deletions

View File

@ -15,7 +15,7 @@ import {
RouteContext,
RouteContextType,
} from '@ant-design/pro-components';
import { Col, message, Row, Space } from 'antd';
import { Col, message, Row } from 'antd';
import { useEffect, useState } from 'react';
// 默认模块配置
@ -33,57 +33,446 @@ type DealerIdAndTemplate = Pick<
'dealerId' | 'deliveryTemplate'
>;
const getDefaultModuleConfig = (moduleType: string): ModuleConfig => {
const defaults = {
title: {
text: '西瓜发货清单',
align: 'center',
size: 'large',
},
// 定义各模块的表单配置
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: {
dealerName: '盛京水果',
vehicleNumber: '第188车',
destination: '宁夏',
watermelonGrade: 'A级',
showDealerName: true,
showVehicleNumber: true,
showDestination: true,
showWatermelonGrade: true,
requiredWatermelonGrade: true,
},
shippingInfo: {
shippingFrom: '宁夏',
date: '2025年8月18日',
showShippingFrom: true,
showDate: true,
requiredShippingFrom: true,
},
weightInfo: {
grossWeight: '56960',
boxWeight: '3391',
netWeight: '53569',
unitPrice: '1.60',
amount: '85710',
grade: 'A',
accountCompany: '公司名称',
sumAmount: '85710',
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: [
'boxCategory',
'boxType',
'quantity',
'unitPrice',
'amount',
'unitWeight',
'weight',
{
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,
@ -91,6 +480,85 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => {
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粒',
@ -126,52 +594,36 @@ const getDefaultModuleConfig = (moduleType: string): ModuleConfig => {
licensePlate: '京A12345',
estimatedArrivalTime: '2025年9月24日',
freightDebt: '11000',
seller: '卖货',
strawMatDebt: '100',
remarks: '备注',
freightDebtTitle: '运费欠',
showDriverPhone: true,
showLicensePlate: true,
showEstimatedArrivalTime: true,
showFreightDebt: true,
showSeller: true,
showStrawMatDebt: true,
showRemarks: true,
},
otherFees: {
enabled: true,
feeItems: ['trademark', 'labor', 'paperBox', 'fee', 'codingFee'],
trademark: '400',
labor: '600',
paperBox: '13,253',
fee: '2,000',
codingFee: '300',
laborDetails: false,
},
totalAmount: {
enabled: true,
sumTitle: '合计金额',
amount: '102,263',
farmer: '李荣赞',
showTotalAmount: true,
showFarmer: true,
},
otherInfo: {
enabled: false,
origin: '内蒙古',
supplier: '北京新发龙盛商贸有限公司',
departureTime: '9月2号',
arrivalTime: '9月3号',
productName: 'A级-麒麟爪',
showOrigin: true,
showSupplier: true,
showDepartureTime: true,
showArrivalTime: true,
showProductName: true,
format: 'list',
},
} as any;
return { ...defaults[moduleType] };
};
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) {
@ -199,13 +651,15 @@ export default function DeliveryTemplate(props: IDeliveryTemplateProps) {
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
(module) => module.id === selectedModule.id,
);
if (selectedIndex !== -1) {
@ -213,7 +667,7 @@ export default function DeliveryTemplate(props: IDeliveryTemplateProps) {
const newModules = [...modules];
newModules.splice(selectedIndex + 1, 0, newModule);
setModules(newModules);
setSelectedModule(newModule); // 立即选中新插入的模块
setSelectedModule(newModule); // 立即选中新插入的模块
return;
}
}
@ -221,7 +675,7 @@ export default function DeliveryTemplate(props: IDeliveryTemplateProps) {
// 如果没有选中的模块,则添加到末尾
const newModules = [...modules, newModule];
setModules(newModules);
setSelectedModule(newModule); // 立即选中新插入的模块
setSelectedModule(newModule); // 立即选中新插入的模块
};
const handleModuleSelect = (module: any) => {

View File

@ -1,4 +1,5 @@
import { Card, Checkbox, Empty, Form, Input, Switch } from 'antd';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Card, Empty } from 'antd';
import React from 'react';
interface ConfigPanelProps {
@ -10,14 +11,6 @@ const ConfigPanel: React.FC<ConfigPanelProps> = ({
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, {
@ -27,309 +20,6 @@ const ConfigPanel: React.FC<ConfigPanelProps> = ({
}
};
const renderTitleConfig = () => (
<div className="mb-4 pb-4 border-b border-gray-200">
<div className="font-bold mb-2 text-blue-500"></div>
<Form.Item name="text" label="标题文本">
<Input placeholder="例如:西瓜发货清单" />
</Form.Item>
</div>
);
const renderDealerInfoConfig = () => (
<div className="mb-4 pb-4 border-b border-gray-200">
<div className="font-bold mb-2 text-blue-500"></div>
<Form.Item
name="showDealerName"
valuePropName="checked"
label="显示经销商名称"
>
<Switch />
</Form.Item>
<Form.Item name="dealerName" label="经销商名称">
<Input placeholder="请输入经销商名称" />
</Form.Item>
<Form.Item
name="showVehicleNumber"
valuePropName="checked"
label="显示车次信息"
>
<Switch />
</Form.Item>
<Form.Item
name="showDestination"
valuePropName="checked"
label="显示收货地"
>
<Switch />
</Form.Item>
<Form.Item
name="showWatermelonGrade"
valuePropName="checked"
label="填写西瓜品级"
extra="单条报价有效"
>
<Switch />
</Form.Item>
</div>
);
const renderWeightInfoConfig = () => (
<div className="mb-4 pb-4 border-b border-gray-200">
<div className="font-bold mb-2 text-blue-500"></div>
<Form.Item
name="showGrossWeight"
valuePropName="checked"
label="显示毛重"
>
<Switch />
</Form.Item>
<Form.Item name="showBoxWeight" valuePropName="checked" label="显示箱重">
<Switch />
</Form.Item>
<Form.Item name="showNetWeight" valuePropName="checked" label="显示净重">
<Switch />
</Form.Item>
<Form.Item name="showUnitPrice" valuePropName="checked" label="显示单价">
<Switch />
</Form.Item>
<Form.Item name="showAmount" valuePropName="checked" label="显示金额">
<Switch />
</Form.Item>
<Form.Item name="showGrade" valuePropName="checked" label="填写品级">
<Switch />
</Form.Item>
<Form.Item
name="showAccountCompany"
valuePropName="checked"
label="显示入账公司"
>
<Switch />
</Form.Item>
<Form.Item
name="showSumAmount"
valuePropName="checked"
label="显示总计"
extra={'多条报价之和'}
>
<Switch />
</Form.Item>
</div>
);
const renderPackingSpecConfig = () => (
<div className="mb-4 pb-4 border-b border-gray-200">
<div className="font-bold mb-2 text-blue-500"></div>
<Form.Item
name="showBoxCategory"
valuePropName="checked"
label="显示品牌"
>
<Switch />
</Form.Item>
<Form.Item name="showBoxType" valuePropName="checked" label="显示箱号">
<Switch />
</Form.Item>
<Form.Item name="showQuantity" valuePropName="checked" label="显示数量">
<Switch />
</Form.Item>
<Form.Item name="showUnitPrice" valuePropName="checked" label="显示单价">
<Switch />
</Form.Item>
<Form.Item name="showAmount" valuePropName="checked" label="显示金额">
<Switch />
</Form.Item>
<Form.Item name="showUnitWeight" valuePropName="checked" label="显示单重">
<Switch />
</Form.Item>
<Form.Item name="showWeight" valuePropName="checked" label="显示重量">
<Switch />
</Form.Item>
</div>
);
const renderOtherFeesConfig = () => (
<div className="mb-4 pb-4 border-b border-gray-200">
<div className="font-bold mb-2 text-blue-500"></div>
<Form.Item name="enabled" valuePropName="checked" label="启用费用模块">
<Switch />
</Form.Item>
<Form.Item name="feeItems" label="显示费用项目">
<Checkbox.Group className="flex flex-col gap-1">
<div className="checkbox-item">
<Checkbox value="trademark"></Checkbox>
</div>
<div className="checkbox-item">
<Checkbox value="labor"></Checkbox>
</div>
<div className="checkbox-item">
<Checkbox value="paperBox"></Checkbox>
</div>
<div className="checkbox-item">
<Checkbox value="fee"></Checkbox>
</div>
<div className="checkbox-item">
<Checkbox value="codingFee"></Checkbox>
</div>
</Checkbox.Group>
</Form.Item>
<Form.Item
name="laborDetails"
valuePropName="checked"
label="人工费用细分"
>
<Switch />
</Form.Item>
</div>
);
const renderModuleConfig = () => {
if (!selectedModule) return null;
const configs = {
title: renderTitleConfig(),
dealerInfo: renderDealerInfoConfig(),
shippingInfo: (
<div className="mb-4 pb-4 border-b border-gray-200">
<div className="font-bold mb-2 text-blue-500"></div>
<Form.Item
name="showShippingFrom"
valuePropName="checked"
label="显示发货地"
>
<Switch />
</Form.Item>
<Form.Item
name="showDate"
valuePropName="checked"
label="显示发货日期"
>
<Switch />
</Form.Item>
</div>
),
weightInfo: renderWeightInfoConfig(),
packingSpec: renderPackingSpecConfig(),
vehicleInfo: (
<div className="mb-4 pb-4 border-b border-gray-200">
<div className="font-bold mb-2 text-blue-500"></div>
<Form.Item
name="showDriverPhone"
valuePropName="checked"
label="显示司机号码"
>
<Switch />
</Form.Item>
<Form.Item
name="showLicensePlate"
valuePropName="checked"
label="显示车牌"
>
<Switch />
</Form.Item>
<Form.Item
name="showEstimatedArrivalTime"
valuePropName="checked"
label="填写预计到仓时间"
>
<Switch />
</Form.Item>
<Form.Item
name="showFreightDebt"
valuePropName="checked"
label="显示运费欠"
>
<Switch />
</Form.Item>
<Form.Item name="showSeller" valuePropName="checked" label="填写卖货">
<Switch />
</Form.Item>
<Form.Item
name="showStrawMatDebt"
valuePropName="checked"
label="显示草帘欠"
>
<Switch />
</Form.Item>
<Form.Item
name="showRemarks"
valuePropName="checked"
label="填写备注"
>
<Switch />
</Form.Item>
<Form.Item name="freightDebtTitle" label="运费欠标题">
<Input placeholder="请输入运费欠标题" />
</Form.Item>
</div>
),
otherFees: renderOtherFeesConfig(),
totalAmount: (
<div className="mb-4 pb-4 border-b border-gray-200">
<div className="font-bold mb-2 text-blue-500"></div>
<Form.Item
name="showTotalAmount"
valuePropName="checked"
label="显示合计金额"
>
<Switch />
</Form.Item>
<Form.Item name="showFarmer" valuePropName="checked" label="信息瓜农">
<Switch />
</Form.Item>
<Form.Item name="sumTitle" label="合计金额标题">
<Input placeholder="请输入合计金额标题" />
</Form.Item>
</div>
),
otherInfo: (
<div className="mb-4 pb-4 border-b border-gray-200">
<div className="font-bold mb-2 text-blue-500"></div>
<Form.Item
name="enabled"
valuePropName="checked"
label="启用其他信息"
>
<Switch />
</Form.Item>
<Form.Item name="showOrigin" valuePropName="checked" label="显示产地">
<Switch />
</Form.Item>
<Form.Item
name="showSupplier"
valuePropName="checked"
label="显示供应商"
>
<Switch />
</Form.Item>
<Form.Item
name="showDepartureTime"
valuePropName="checked"
label="显示发车时间"
>
<Switch />
</Form.Item>
<Form.Item
name="showArrivalTime"
valuePropName="checked"
label="显示预计到达"
>
<Switch />
</Form.Item>
<Form.Item
name="showProductName"
valuePropName="checked"
label="显示品名"
>
<Switch />
</Form.Item>
</div>
),
} as any;
return configs[selectedModule.type] || <div></div>;
};
if (!selectedModule) {
return (
<Card
@ -348,9 +38,25 @@ const ConfigPanel: React.FC<ConfigPanelProps> = ({
size="small"
className="bg-white rounded-lg p-4 h-full"
>
<Form form={form} layout="horizontal" onValuesChange={handleValuesChange}>
{renderModuleConfig()}
</Form>
{selectedModule.config && selectedModule.schemas && (
<BetaSchemaForm
key={selectedModule.id}
layoutType="Form"
layout={'horizontal'}
rowProps={{
gutter: [16, 16],
}}
colProps={{
span: 24,
}}
request={() => {
return Promise.resolve(selectedModule.config);
}}
onValuesChange={handleValuesChange}
columns={selectedModule.schemas}
submitter={false}
/>
)}
</Card>
);
};

View File

@ -89,7 +89,7 @@ const DealerInfoModule: React.FC<ModuleProps> = ({
>
<div className="col-span-1"></div>
{config.showDealerName || config.showWatermelonGrade ? (
<div className="col-span-3 border-b border-black flex items-center justify-center">
<div className="col-span-3 border-b border-black flex items-end justify-center">
{config.showWatermelonGrade
? `${config.dealerName}-${config.watermelonGrade}`
: config.dealerName}
@ -98,7 +98,7 @@ const DealerInfoModule: React.FC<ModuleProps> = ({
<div className="col-span-3"></div>
)}
{config.showDestination || config.showVehicleNumber ? (
<div className="col-span-3 border-b border-black flex items-center justify-center">
<div className="col-span-3 border-b border-black flex items-end justify-center">
{config.destination}
{config.vehicleNumber}
</div>

View File

@ -32,13 +32,6 @@ const OtherFeesModule: React.FC<ModuleProps> = ({
canMoveDown,
previewMode = false, // 默认为false
}) => {
const feeLabels = {
trademark: '商标',
labor: '人工',
paperBox: '纸箱',
fee: '费用',
codingFee: '打码费',
} as any;
return (
<div
@ -96,9 +89,9 @@ const OtherFeesModule: React.FC<ModuleProps> = ({
<div className={'preview grid grid-cols-8 gap-0 w-full text-base'}>
{config.feeItems.map((feeType: any) => (
<React.Fragment key={feeType}>
<div className="col-span-1 flex items-center justify-center">{feeLabels[feeType]}</div>
<div className="col-span-1 flex items-end justify-center">{config?.feeLabels?.[feeType] || ''}</div>
<div
className="col-span-1 border-b border-black flex items-center justify-center"
className="col-span-1 border-b border-black flex items-end justify-center"
>
{config[feeType]}
</div>

View File

@ -34,11 +34,11 @@ const OtherInfoModule: React.FC<ModuleProps> = ({
}) => {
return (
<div
className={`${previewMode ? '' : 'border border-gray-300'} rounded p-2 mb-2 bg-white transition-all duration-200 ${
isSelected && !previewMode
? 'border-2 border-blue-500 shadow-[0_0_0_2px_rgba(24,144,255,0.2)]'
: 'hover:border-blue-300'
}`}
className={`${previewMode ? '' : 'border border-gray-300'} rounded p-2 mb-2 bg-white transition-all duration-200 ${
isSelected && !previewMode
? 'border-2 border-blue-500 shadow-[0_0_0_2px_rgba(24,144,255,0.2)]'
: 'hover:border-blue-300'
}`}
onClick={previewMode ? undefined : onSelect}
>
{!previewMode && (

View File

@ -1,124 +1,245 @@
import React from 'react';
import { Button, Typography } from 'antd';
import {
MenuOutlined,
ArrowUpOutlined,
ArrowDownOutlined,
DeleteOutlined
ArrowDownOutlined,
ArrowUpOutlined,
DeleteOutlined,
MenuOutlined,
} from '@ant-design/icons';
import { Button, Typography } from 'antd';
import classNames from 'classnames';
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;
previewMode?: boolean; // 添加预览模式属性
config: any;
isSelected: boolean;
onSelect: () => void;
onDelete: () => void;
onMoveUp?: () => void;
onMoveDown?: () => void;
canMoveUp?: boolean;
canMoveDown?: boolean;
previewMode?: boolean; // 添加预览模式属性
}
const PackingSpecModule: React.FC<ModuleProps> = ({
config,
isSelected,
onSelect,
onDelete,
onMoveUp,
onMoveDown,
canMoveUp,
canMoveDown,
previewMode = false, // 默认为false
config,
isSelected,
onSelect,
onDelete,
onMoveUp,
onMoveDown,
canMoveUp,
canMoveDown,
previewMode = false, // 默认为false
}) => {
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('重量');
// 计算需要显示的列数
const visibleColumnCount =
[
config.showBoxType,
config.showQuantity,
config.showUnitPrice,
config.showAmount,
config.showUnitWeight,
config.showWeight,
].filter(Boolean).length + 1; // +1 是因为"规格:"列总是显示
console.log('visibleColumnCount', visibleColumnCount);
return (
<div
className={`${previewMode ? '' : 'border border-gray-300'} rounded p-2 mb-2 bg-white transition-all duration-200 ${
isSelected && !previewMode
? 'border-2 border-blue-500 shadow-[0_0_0_2px_rgba(24,144,255,0.2)]'
: 'hover:border-blue-300'
}`}
onClick={previewMode ? undefined : onSelect}
>
{!previewMode && (
<div className="flex justify-between items-center mb-2 pb-2 border-b border-gray-100">
<Text strong></Text>
<div className="flex gap-1">
<Button
type="text"
icon={<MenuOutlined />}
size="small"
style={{ cursor: 'move' }}
/>
<Button
type="text"
icon={<ArrowUpOutlined />}
onClick={(e) => {
e.stopPropagation();
if (onMoveUp) onMoveUp();
}}
size="small"
disabled={!canMoveUp}
/>
<Button
type="text"
icon={<ArrowDownOutlined />}
onClick={(e) => {
e.stopPropagation();
if (onMoveDown) onMoveDown();
}}
size="small"
disabled={!canMoveDown}
/>
<Button
type="text"
icon={<DeleteOutlined />}
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
danger
size="small"
/>
</div>
</div>
)}
<div className="py-2 flex flex-col gap-2.5 text-base">
<table className="w-full border-collapse text-lg leading-4">
<thead>
<tr>
{columns.map((col) => (
<th key={col} className="font-bold p-2 text-center">{col}</th>
))}
</tr>
</thead>
<tbody>
{config.data.map((row: any, index: any) => (
<tr key={index}>
{config.showBoxCategory && <td className="p-2 text-center border border-black">{row.boxCategory}</td>}
{config.showBoxType && <td className="p-2 text-center border border-black">{row.boxType}</td>}
{config.showQuantity && <td className="p-2 text-center border border-black">{row.quantity}</td>}
{config.showUnitPrice && <td className="p-2 text-center border border-black">{row.unitPrice}</td>}
{config.showAmount && <td className="p-2 text-center border border-black">{row.amount}</td>}
{config.showUnitWeight && <td className="p-2 text-center border border-black">{row.unitWeight}</td>}
{config.showWeight && <td className="p-2 text-center border border-black">{row.weight}</td>}
</tr>
))}
</tbody>
</table>
</div>
</div>
);
return (
<div
className={`${previewMode ? '' : 'border border-gray-300'} rounded p-2 mb-2 bg-white transition-all duration-200 ${
isSelected && !previewMode
? 'border-2 border-blue-500 shadow-[0_0_0_2px_rgba(24,144,255,0.2)]'
: 'hover:border-blue-300'
}`}
onClick={previewMode ? undefined : onSelect}
>
{!previewMode && (
<div className="flex justify-between items-center mb-2 pb-2 border-b border-gray-100">
<Text strong></Text>
<div className="flex gap-1">
<Button
type="text"
icon={<MenuOutlined />}
size="small"
style={{ cursor: 'move' }}
/>
<Button
type="text"
icon={<ArrowUpOutlined />}
onClick={(e) => {
e.stopPropagation();
if (onMoveUp) onMoveUp();
}}
size="small"
disabled={!canMoveUp}
/>
<Button
type="text"
icon={<ArrowDownOutlined />}
onClick={(e) => {
e.stopPropagation();
if (onMoveDown) onMoveDown();
}}
size="small"
disabled={!canMoveDown}
/>
<Button
type="text"
icon={<DeleteOutlined />}
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
danger
size="small"
/>
</div>
</div>
)}
<div
className={classNames(`grid w-full gap-0 text-base `, {
'grid-cols-1': visibleColumnCount === 1,
'grid-cols-2': visibleColumnCount === 2,
'grid-cols-3': visibleColumnCount === 3,
'grid-cols-4': visibleColumnCount === 4,
'grid-cols-5': visibleColumnCount === 5,
'grid-cols-6': visibleColumnCount === 6,
'grid-cols-7': visibleColumnCount === 7,
'grid-cols-8': visibleColumnCount === 8,
})}
>
<div className={`flex items-end justify-center grid-span-1`}>
</div>
</div>
<div
className={classNames(`grid w-full gap-0 text-base `, {
'grid-cols-1': visibleColumnCount === 1,
'grid-cols-2': visibleColumnCount === 2,
'grid-cols-3': visibleColumnCount === 3,
'grid-cols-4': visibleColumnCount === 4,
'grid-cols-5': visibleColumnCount === 5,
'grid-cols-6': visibleColumnCount === 6,
'grid-cols-7': visibleColumnCount === 7,
'grid-cols-8': visibleColumnCount === 8,
})}
>
{config.columns.map((column: any, index: number) => {
if (index === 0) {
return (
<div key={'title' + index} className="">
&nbsp;
</div>
);
}
if (
(column.dataIndex === 'boxType' && config.showBoxType) ||
(column.dataIndex === 'quantity' && config.showQuantity) ||
(column.dataIndex === 'unitPrice' && config.showUnitPrice) ||
(column.dataIndex === 'amount' && config.showAmount) ||
(column.dataIndex === 'unitWeight' && config.showUnitWeight) ||
(column.dataIndex === 'weight' && config.showWeight)
) {
return (
<div
key={'title' + index}
className="flex items-end justify-center"
>
{column.title}
</div>
);
}
return <></>;
})}
</div>
<div className={'table-border'}>
{config.data?.map((item: any, index: number) => (
<div
key={index}
className={classNames(`grid w-full gap-0 text-base`, {
'grid-cols-1': visibleColumnCount === 1,
'grid-cols-2': visibleColumnCount === 2,
'grid-cols-3': visibleColumnCount === 3,
'grid-cols-4': visibleColumnCount === 4,
'grid-cols-5': visibleColumnCount === 5,
'grid-cols-6': visibleColumnCount === 6,
'grid-cols-7': visibleColumnCount === 7,
'grid-cols-8': visibleColumnCount === 8,
' border-t-0': index > 0,
})}
>
<div className={'flex items-end justify-center'}>
{item.boxCategory}
</div>
{config.showBoxType && (
<div className={'flex items-end justify-center'}>
{item.boxType}
</div>
)}
{config.showQuantity && (
<div className={'flex items-end justify-center'}>
{item.quantity}
</div>
)}
{config.showUnitPrice && (
<div className={'flex items-end justify-center'}>
{item.unitPrice}
</div>
)}
{config.showAmount && (
<div className={'flex items-end justify-center'}>
{item.amount}
</div>
)}
{config.showUnitWeight && (
<div className={'flex items-end justify-center'}>
{item.unitWeight}
</div>
)}
{config.showWeight && (
<div className={'flex items-end justify-center'}>
{item.weight}
</div>
)}
</div>
))}
<div
className={classNames(`grid w-full gap-0 text-base `, {
'grid-cols-1': visibleColumnCount === 1,
'grid-cols-2': visibleColumnCount === 2,
'grid-cols-3': visibleColumnCount === 3,
'grid-cols-4': visibleColumnCount === 4,
'grid-cols-5': visibleColumnCount === 5,
'grid-cols-6': visibleColumnCount === 6,
'grid-cols-7': visibleColumnCount === 7,
'grid-cols-8': visibleColumnCount === 8,
})}
>
<div className={`flex items-end justify-center col-span-2`}>
</div>
<div className={`flex items-end justify-center col-span-1`}>
{config.data?.reduce(
(acc: any, cur: any) => acc + Number(cur.quantity),
0,
)}
</div>
</div>
</div>
</div>
);
};
export default PackingSpecModule;

View File

@ -569,7 +569,6 @@ const PreviewCanvas: React.FC<PreviewCanvasProps> = ({
style={{
width: '21cm',
margin: '0 auto',
height: 'calc(100vh - 280px)',
}}
>
<div ref={canvasRef} id={'preview-canvas'}>

View File

@ -89,20 +89,20 @@ const ShippingInfoModule: React.FC<ModuleProps> = ({
>
{config.showShippingFrom && (
<>
<div className="col-span-1 flex items-center justify-center">
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-3 border-b border-black flex items-center justify-center">
<div className="col-span-3 border-b border-black flex items-end justify-center">
{config.shippingFrom}
</div>
</>
)}
{config.showDate && (
<>
<div className="col-span-1 flex items-center justify-center">
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-3 border-b border-black flex items-center justify-center">
<div className="col-span-3 border-b border-black flex items-end justify-center">
{config.date}
</div>
</>

View File

@ -87,8 +87,8 @@ const TitleModule: React.FC<ModuleProps> = ({
<div
className={'preview grid grid-cols-8 gap-0 w-full text-2xl font-bold'}
>
<div className="col-span-8 flex items-center justify-center">
{config.text}
<div className="col-span-8 flex items-end justify-center">
{config.title}
</div>
</div>
</div>

View File

@ -88,23 +88,23 @@ const TotalAmountModule: React.FC<ModuleProps> = ({
<div className={'preview grid grid-cols-8 gap-0 w-full text-base'}>
{config.showTotalAmount && (
<>
<div className="col-span-1 flex items-center justify-center">
<div className="col-span-1 flex items-end justify-center">
{config.sumTitle || '合计金额'}:
</div>
<div className="col-span-2 border-b border-black flex items-center justify-center">
<div className="col-span-2 border-b border-black flex items-end justify-center">
{config.amount}
</div>
<div className="col-span-1 border-b border-black flex items-center justify-center">
<div className="col-span-1 border-b border-black flex items-end justify-center">
</div>
</>
)}
{config.showFarmer && (
<>
<div className="col-span-1 flex items-center justify-center">
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-3 border-b border-black flex items-center justify-center">
<div className="col-span-3 border-b border-black flex items-end justify-center">
{config.farmer}
</div>
</>

View File

@ -88,71 +88,65 @@ const VehicleInfoModule: React.FC<ModuleProps> = ({
<div className={'preview grid grid-cols-8 gap-0 w-full text-base'}>
{config.showDriverPhone && (
<>
<div className="col-span-2 flex items-center justify-center">
<div className="col-span-2 flex items-end justify-center">
:
</div>
<div className="col-span-6 border-b border-black flex items-center justify-center">
<div className="col-span-6 border-b border-black flex items-end justify-center">
{config.driverPhone}
</div>
</>
)}
{config.showLicensePlate && (
<>
<div className="col-span-2 flex items-center justify-center">
<div className="col-span-2 flex items-end justify-center">
:
</div>
<div className="col-span-6 border-b border-black flex items-center justify-center">
<div className="col-span-6 border-b border-black flex items-end justify-center">
{config.licensePlate}
</div>
</>
)}
{config.showEstimatedArrivalTime && (
<>
<div className="col-span-2 flex items-center justify-center">
<div className="col-span-2 flex items-end justify-center">
:
</div>
<div className="col-span-6 border-b border-black flex items-center justify-center">
<div className="col-span-6 border-b border-black flex items-end justify-center">
{config.estimatedArrivalTime}
</div>
</>
)}
{config.showRemarks && (
<>
<div className="col-span-2 flex items-end justify-center">
:
</div>
<div className="col-span-6 border-b border-black flex items-end justify-center">
{config.remarks}
</div>
</>
)}
{config.showFreightDebt && (
<>
<div className="col-span-2 flex items-center justify-center">
<div className="col-span-2 flex items-end justify-center">
{config.freightDebtTitle || '运费欠'}:
</div>
<div className="col-span-2 border-b border-black flex items-center justify-center">
<div className="col-span-2 border-b border-black flex items-end justify-center">
{config.freightDebt}
</div>
</>
)}
{config.showStrawMatDebt && (
<>
<div className="col-span-2 flex items-center justify-center">
<div className="col-span-2 flex items-end justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-center justify-center">
<div className="col-span-2 border-b border-black flex items-end justify-center">
{config.strawMatDebt}
</div>
</>
)}
</div>
{/*<div className="py-2 text-center flex flex-col gap-2.5">*/}
{/* <div className="grid grid-cols-2 gap-2.5 text-base leading-4 my-0 mx-auto">*/}
{/* {config.showRemarks && (*/}
{/* <div className="flex items-center mb-2">*/}
{/* <span className="min-w-[120px] text-center py-0 px-1.5">*/}
{/* 备注:*/}
{/* </span>*/}
{/* <div className="flex min-w-[180px] py-0 px-2.5 items-center justify-around border-b border-black">*/}
{/* <span className="text-center inline-block">*/}
{/* {config.remarks}*/}
{/* </span>*/}
{/* </div>*/}
{/* </div>*/}
{/* )}*/}
{/* </div>*/}
{/*</div>*/}
</div>
);
};

View File

@ -84,104 +84,114 @@ const WeightInfoModule: React.FC<ModuleProps> = ({
</div>
</div>
)}
<div className={'preview grid grid-cols-8 gap-0 w-full text-base'}>
{config.showGrossWeight && (
<>
<div className="col-span-1 flex items-center justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-center justify-center">
{config.grossWeight}
</div>
<div className="col-span-1 border-b border-black flex items-center justify-center">
</div>
</>
)}
{config.showBoxWeight && (
<>
<div className="col-span-1 flex items-center justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-center justify-center">
{config.boxWeight}
</div>
<div className="col-span-1 border-b border-black flex items-center justify-center">
</div>
</>
)}
{config.showNetWeight && (
<>
<div className="col-span-1 flex items-center justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-center justify-center">
{config.netWeight}
</div>
<div className="col-span-1 border-b border-black flex items-center justify-center">
</div>
</>
)}
{config.showUnitPrice && (
<>
<div className="col-span-1 flex items-center justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-center justify-center">
{config.unitPrice}
</div>
<div className="col-span-1 border-b border-black flex items-center justify-center">
/
</div>
</>
)}
{config.showAmount && (
<>
<div className="col-span-1 flex items-center justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-center justify-center">
{config.amount}
</div>
<div className="col-span-1 border-b border-black flex items-center justify-center">
</div>
</>
)}
{config.showGrade && (
<>
<div className="col-span-1 flex items-center justify-center">
:
</div>
<div className="col-span-3 border-b border-black flex items-center justify-center">
{config.grade}
</div>
</>
)}
{config.data?.map((item: any, index: number) => {
return (
<div
key={'data' + index}
className={'preview grid grid-cols-2 gap-0 w-full text-base'}
>
{config.showNetWeight && (
<div className={'col-span-1 grid grid-cols-4'}>
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-end justify-center">
{item.netWeight}
</div>
<div className="col-span-1 border-b border-black flex items-end justify-center">
{config.netWeightUnit === '1' ? '斤' : '公斤'}
</div>
</div>
)}
{config.showBoxWeight && (
<div className={'col-span-1 grid grid-cols-4'}>
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-end justify-center">
{item.boxWeight}
</div>
<div className="col-span-1 border-b border-black flex items-end justify-center">
{config.boxWeightUnit === '1' ? '斤' : '公斤'}
</div>
</div>
)}
{config.showGrossWeight && (
<div className={'col-span-1 grid grid-cols-4'}>
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-end justify-center">
{item.grossWeight}
</div>
<div className="col-span-1 border-b border-black flex items-end justify-center">
{config.grossWeightUnit === '1' ? '斤' : '公斤'}
</div>
</div>
)}
{config.showUnitPrice && (
<div className={'col-span-1 grid grid-cols-4'}>
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-end justify-center">
{item.unitPrice}
</div>
<div className="col-span-1 border-b border-black flex items-end justify-center">
{config.unitPriceUnit === '1' ? '元/斤' : '元/公斤'}
</div>
</div>
)}
{config.showAmount && (
<div className={'col-span-1 grid grid-cols-4'}>
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-end justify-center">
{item.amount}
</div>
<div className="col-span-1 border-b border-black flex items-end justify-center">
</div>
</div>
)}
{config.showGrade && (
<div className={'col-span-1 grid grid-cols-4'}>
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-3 border-b border-black flex items-end justify-center">
{item.grade}
</div>
</div>
)}
</div>
);
})}
<div className={'preview grid grid-cols-2 gap-0 w-full text-base'}>
{config.showAccountCompany && (
<>
<div className="col-span-1 flex items-center justify-center">
<div className={'col-span-1 grid grid-cols-4'}>
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-3 border-b border-black flex items-center justify-center">
<div className="col-span-3 border-b border-black flex items-end justify-center">
{config.accountCompany}
</div>
</>
</div>
)}
{config.showSumAmount && (
<>
<div className="col-span-1 flex items-center justify-center">
<div className={'col-span-1 grid grid-cols-4'}>
<div className="col-span-1 flex items-end justify-center">
:
</div>
<div className="col-span-2 border-b border-black flex items-center justify-center">
<div className="col-span-2 border-b border-black flex items-end justify-center">
{config.sumAmount}
</div>
<div className="col-span-1 border-b border-black flex items-center justify-center">
<div className="col-span-1 border-b border-black flex items-end justify-center">
</div>
</>
</div>
)}
</div>
</div>

View File

@ -50,10 +50,27 @@ body::-webkit-scrollbar {
.preview {
width: 19cm;
table-layout: fixed;
td {
text-align: center;
vertical-align: middle;
div {
height: 1.1cm;
}
}
.table-border {
border: 2px solid #000;
}
.table-border > div {
border-bottom: 1px solid #000;
}
.table-border > div > div {
border-right: 1px solid #000;
}
.table-border > div > div:last-child {
border-right: none;
}
.table-border > div:last-child {
border-bottom: none;
}

View File

@ -671,6 +671,17 @@ export default {
},
dealer: {
module: {
title: '标题模块',
dealerInfo: '经销商信息模块',
shippingInfo: '发货地信息模块',
weightInfo: '重量金额信息模块',
packingSpec: '装箱规格模块',
vehicleInfo: '车辆信息模块',
otherFees: '其他费用模块',
totalAmount: '合计金额模块',
otherInfo: '其他信息模块',
},
column: {
shortName: '经销商简称',
dealerPaymentAccountVOList: '付款账户',
@ -680,11 +691,11 @@ export default {
'enableShare.tooltip': '是否开启诚信志远分成',
shareRatio: '分成比例',
shareRatioTitle: '分成',
'freightCostFlag': '运费成本',
freightCostFlag: '运费成本',
'freightCostFlag.tooltip': '运费是否作为成本',
'strawMatCostFlag': '草帘成本',
strawMatCostFlag: '草帘成本',
'strawMatCostFlag.tooltip': '草帘是否作为成本',
'includePackingFlag': '含包装费',
includePackingFlag: '含包装费',
'includePackingFlag.tooltip': '发货单合计金额是否含包装费',
documentTypes: '单据类型',
remark: '备注',