车辆租赁合同:列表页需求同步、审批/合同状态重定义、需求说明弹窗更新(2026年3月3日版本)

Made-with: Cursor
This commit is contained in:
王冕
2026-03-03 04:08:27 +08:00
parent b2f96de4c4
commit 3be6b02e9a
11 changed files with 2250 additions and 746 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,292 @@
// 【重要】必须使用 const Component 作为组件变量名
// 提车应收款 - 收费明细(租赁费用管理-提车应收款,第二步由财务填写)
const Component = function() {
var useState = React.useState;
var useCallback = React.useCallback;
var useMemo = React.useMemo;
var antd = window.antd;
var DatePicker = antd ? antd.DatePicker : null;
var Input = antd ? antd.Input : null;
var Upload = antd ? antd.Upload : null;
var Button = antd ? antd.Button : null;
// 模拟:项目信息、车辆(业务提交后数据)、氢费(业务提交后)、开票基础信息
var mockProject = useMemo(function() {
return { contractCode: 'HT-ZL-2025-001', contractType: '正式合同', projectName: '北京朝阳区租赁项目', customerName: '某某科技有限公司', paymentMethod: '预付', paymentCycle: '1个月', department: '运营部', responsible: '张三' };
}, []);
var mockVehicleList = useMemo(function() {
return [
{ brand: '奔驰', model: 'E300L', plateNo: '京A12345', payableRent: '8000.00', rentRemark: '首月', deposit: '2000.00', serviceItems: [{ name: '保养服务', fee: '300.00', remark: '' }, { name: '保险', fee: '200.00', remark: '' }], serviceTotal: '500.00', discount: '0', discountRemark: '' },
{ brand: '宝马', model: '530Li', plateNo: '京B67890', payableRent: '7000.00', rentRemark: '', deposit: '1500.00', serviceItems: [{ name: '保养服务', fee: '250.00', remark: '' }], serviceTotal: '250.00', discount: '0', discountRemark: '' }
];
}, []);
var mockHydrogen = useMemo(function() {
return { paidTotal: '3580.00', discount: '0.00', discountRemark: '' };
}, []);
var mockInvoice = useMemo(function() {
return { customerName: '某某科技有限公司', taxId: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', phone: '0571-88888888', account: '6222021234567890123', bank: '中国工商银行嘉兴分行', mailingAddress: '浙江省嘉兴市南湖区科技大道1号' };
}, []);
var totalPayable = useMemo(function() {
var sum = 0;
for (var i = 0; i < mockVehicleList.length; i++) {
var r = mockVehicleList[i];
sum += parseFloat(r.payableRent) || 0;
sum += parseFloat(r.deposit) || 0;
sum += parseFloat(r.serviceTotal) || 0;
sum -= parseFloat(r.discount) || 0;
}
return sum.toFixed(2);
}, [mockVehicleList]);
var hydrogenPayableTotal = useMemo(function() {
var p = parseFloat(mockHydrogen.paidTotal) || 0;
var d = parseFloat(mockHydrogen.discount) || 0;
return (p - d >= 0 ? p - d : 0).toFixed(2);
}, [mockHydrogen]);
var invoiceAmount = useMemo(function() {
return (parseFloat(totalPayable) + parseFloat(hydrogenPayableTotal)).toFixed(2);
}, [totalPayable, hydrogenPayableTotal]);
var serviceModalRowState = useState(null);
var serviceModalRow = serviceModalRowState[0];
var setServiceModalRow = serviceModalRowState[1];
var cancelConfirmVisibleState = useState(false);
var cancelConfirmVisible = cancelConfirmVisibleState[0];
var setCancelConfirmVisible = cancelConfirmVisibleState[1];
var requirementVisibleState = useState(false);
var requirementVisible = requirementVisibleState[0];
var setRequirementVisible = requirementVisibleState[1];
var invoiceFormState = useState({ invoiceTime: '', remark: '', invoiceFiles: [] });
var invoiceForm = invoiceFormState[0];
var setInvoiceForm = invoiceFormState[1];
var handleSubmit = useCallback(function() {
alert('提交成功,跳转至提车应收款列表页');
}, []);
var handleCancelClick = useCallback(function() { setCancelConfirmVisible(true); }, []);
var handleCancelConfirm = useCallback(function(confirmed) {
setCancelConfirmVisible(false);
if (confirmed) alert('已返回提车应收款列表');
}, []);
var requirementContent = '「车辆管理系统」中的「租赁费用管理」模块下「提车应收款」中「收费明细」模块,第二步由财务填写;\n\n#提车应收款-收款明细\n\n整个页面从上至下为项目信息、车辆首付款、氢费预付款、开票信息4个卡片模块组成\n\n1.项目信息:\n1.1.上方为表单,显示合同编码、合同类型、项目名称、客户名称、付款方式、付款周期、业务部门、业务负责人;\n1.1.1.合同编码:显示租赁合同编号;\n1.1.2.合同类型:显示租赁合同类型,类型有正式合同/试用合同;\n1.1.3.项目名称:显示租赁合同项目名称;\n1.1.4.客户名称:显示租赁合同客户名称;\n1.1.5.付款方式:显示租赁合同付款方式,类型有预付/后付;\n1.1.6.付款周期显示付款周期类型有1个月-12个月\n1.1.7.业务部门:显示租赁合同对应业务部门;\n1.1.8.业务负责人:显示租赁合同对应业务负责人;\n\n2.提车应收款:\n2.1.上方为提车应收款应收总额;\n2.1.1.提车应收款总额显示总金额格式为xx.xx元计算方式为所有应付车辆租金+所有应付保证金+所有应付服务费-所有减免金额;\n2.2.车辆账单:品牌/型号/车牌号/应收车辆租金/车辆租金备注/应收保证金/服务费项目/应收服务费/减免金额/减免金额备注\n2.2.1.品牌:显示租赁合同中对应品牌;\n2.2.2.型号:显示租赁合同中对应型号;\n2.2.3.车牌号:显示租赁合同中对应车牌号,如无则显示为-\n2.2.4.应收车辆租金:显示业务提交的应收车辆租金;\n2.2.5.车辆租金备注:显示业务提交的车辆租金备注;\n2.2.6.应收保证金:显示业务提交的应收车辆保证金;\n2.2.7.服务费项目:点击查看,弹出气泡卡片,卡片中为列表,列表显示租赁合同所有已填写服务项目,列表字段为:服务项目、应收费用、服务费用备注;\n 2.2.8.1.服务项目:显示所有租赁合同中已添加的服务项目名称;\n 2.2.8.2.应收费用:显示所有业务提交的费用金额;\n 2.2.8.3.服务费用备注:显示所有业务提交的备注信息;\n2.1.8.应收服务费:根据服务费项目所有服务项目应付费用计算总和得出,不可修改;\n2.1.9.减免金额显示业务提交的减免金额格式为xx.xx元\n2.1.10.减免金额备注:显示业务提交的减免金额备注信息;\n\n3.氢费预付款:\n3.1.上方为氢费预付款应收总额/氢费预付款应收金额/减免金额/减免金额备注;\n3.1.1.氢费应收总额显示总金额格式为xx.xx元计算方式为氢费预付款实付总额-减免金额;\n3.1.2.氢费预付款应收金额显示业务填写的氢费预付款应收金额格式为xx.xx元\n3.1.2.减免金额显示业务填写的减免金额格式为xx.xx元\n3.1.3.减免金额备注:显示业务填写的减免金额备注;\n\n\n4.开票信息:\n4.1.开票金额显示总金额格式为xx.xx元计算方式为提车应收款总额+氢费应收总额;\n4.2.之后为表单,显示客户名称、纳税人识别号、地址、电话、账户、开户行、邮寄地址、开票时间、发票附件、备注:\n4.2.1.客户名称:显示租赁合同客户名称;\n4.2.2.纳税人识别号:显示租赁合同对应客户纳税人识别号;\n4.2.3.地址:显示租赁合同对应客户地址;\n4.2.4.电话:显示租赁合同对应客户电话;\n4.2.5.账户:显示租赁合同对应客户账户;\n4.2.6.开户行:显示租赁合同对应客户开户行;\n4.2.7.邮寄地址:显示租赁合同对应客户邮寄地址;\n4.2.8.开票金额显示总金额格式为xx.xx元计算方式为提车应收款总额+氢费应收总额;\n4.2.9.开票时间:选填项,日期选择器,精确至天;;\n4.2.10.发票附件:选填项,附件上传按钮,点击后上传发票附件,支持多附件(满足可能会有多张发票组合的情况);\n4.2.11.备注:选填项,文本域,用于记录财务相关备注;\n\n5.页面底部为提交、取消;\n5.1.提交:点击提交,提示:提交成功,跳转至:提车应收款列表页;\n5.2.取消:点击取消,进行二次确认气泡提示,提示为:取消将会丢失所有已添加数据,是否确认取消,点击是则返回提车应收款列表页;点击否则关闭二次确认继续操作;';
var styles = useMemo(function() {
return {
page: { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' },
breadcrumb: { marginBottom: 16, fontSize: 14, color: '#666' },
card: { background: '#fff', borderRadius: 8, padding: 24, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.03)' },
cardTitle: { fontSize: 16, fontWeight: 600, marginBottom: 16, paddingBottom: 12, borderBottom: '1px solid #f0f0f0' },
detailRow: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '24px', marginBottom: 12, fontSize: 14 },
formCol: { display: 'flex', flexDirection: 'column', gap: 4 },
formLabel: { color: '#666', fontSize: 14 },
formValue: { color: '#333', fontSize: 14 },
table: { width: '100%', borderCollapse: 'collapse', fontSize: 14 },
th: { textAlign: 'left', padding: '12px 16px', backgroundColor: '#fafafa', borderBottom: '1px solid #f0f0f0', fontWeight: 600, color: '#333', whiteSpace: 'nowrap' },
td: { padding: '12px 16px', borderBottom: '1px solid #f0f0f0', color: '#333' },
linkBtn: { color: '#1890ff', cursor: 'pointer', background: 'none', border: 'none', padding: 0, fontSize: 14 },
totalLine: { marginBottom: 16, fontSize: 14 },
totalAmount: { color: '#1890ff', fontWeight: 600, fontSize: 16 },
footer: { marginTop: 24, display: 'flex', justifyContent: 'center', gap: 12 },
primaryBtn: { padding: '8px 24px', backgroundColor: '#1890ff', color: '#fff', border: 'none', borderRadius: 4, cursor: 'pointer', fontSize: 14 },
defaultBtn: { padding: '8px 24px', backgroundColor: '#fff', color: '#666', border: '1px solid #d9d9d9', borderRadius: 4, cursor: 'pointer', fontSize: 14 },
formInput: { padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: 4, fontSize: 14, width: '100%', maxWidth: 200 },
modalOverlay: { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.45)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000 },
modalCard: { backgroundColor: '#fff', borderRadius: 8, padding: 24, maxWidth: '90%', maxHeight: '80vh', overflow: 'auto' },
modalHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 },
modalTitle: { fontSize: 16, fontWeight: 600 },
modalClose: { padding: '4px 12px', backgroundColor: '#f5f5f5', border: 'none', borderRadius: 4, cursor: 'pointer' },
modalBody: { maxHeight: '60vh', overflowY: 'auto', whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.6, color: '#333' },
pageHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16, flexWrap: 'wrap', gap: 8 },
requirementLink: { color: '#1890ff', cursor: 'pointer', fontSize: 14, background: 'none', border: 'none', padding: 0 }
};
}, []);
var projectCard = React.createElement('div', { style: styles.card },
React.createElement('div', { style: styles.cardTitle }, '项目信息'),
React.createElement('div', { style: styles.detailRow },
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '合同编码'), React.createElement('div', { style: styles.formValue }, mockProject.contractCode)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '合同类型'), React.createElement('div', { style: styles.formValue }, mockProject.contractType)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '项目名称'), React.createElement('div', { style: styles.formValue }, mockProject.projectName))
),
React.createElement('div', { style: styles.detailRow },
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '客户名称'), React.createElement('div', { style: styles.formValue }, mockProject.customerName)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '付款方式'), React.createElement('div', { style: styles.formValue }, mockProject.paymentMethod)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '付款周期'), React.createElement('div', { style: styles.formValue }, mockProject.paymentCycle))
),
React.createElement('div', { style: styles.detailRow },
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '业务部门'), React.createElement('div', { style: styles.formValue }, mockProject.department)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '业务负责人'), React.createElement('div', { style: styles.formValue }, mockProject.responsible)),
React.createElement('div', { style: styles.formCol }, null)
)
);
var vehicleTableHeader = React.createElement('thead', null,
React.createElement('tr', null,
React.createElement('th', { style: styles.th }, '品牌'),
React.createElement('th', { style: styles.th }, '型号'),
React.createElement('th', { style: styles.th }, '车牌号'),
React.createElement('th', { style: styles.th }, '应收车辆租金'),
React.createElement('th', { style: styles.th }, '车辆租金备注'),
React.createElement('th', { style: styles.th }, '应收保证金'),
React.createElement('th', { style: styles.th }, '服务费项目'),
React.createElement('th', { style: styles.th }, '应收服务费'),
React.createElement('th', { style: styles.th }, '减免金额'),
React.createElement('th', { style: styles.th }, '减免金额备注')
)
);
var vehicleTableBody = React.createElement('tbody', null,
mockVehicleList.map(function(row, rowIndex) {
return React.createElement('tr', { key: rowIndex },
React.createElement('td', { style: styles.td }, row.brand),
React.createElement('td', { style: styles.td }, row.model),
React.createElement('td', { style: styles.td }, row.plateNo || '-'),
React.createElement('td', { style: styles.td }, row.payableRent),
React.createElement('td', { style: styles.td }, row.rentRemark || '-'),
React.createElement('td', { style: styles.td }, row.deposit),
React.createElement('td', { style: styles.td },
React.createElement('button', { type: 'button', style: styles.linkBtn, onClick: function() { setServiceModalRow(rowIndex); } }, '查看')
),
React.createElement('td', { style: styles.td }, row.serviceTotal || '0.00'),
React.createElement('td', { style: styles.td }, (function() { var v = parseFloat(row.discount); return (isNaN(v) ? '0.00' : v.toFixed(2)) + ' 元'; })()),
React.createElement('td', { style: styles.td }, row.discountRemark || '-')
);
})
);
var vehicleBillCard = React.createElement('div', { style: styles.card },
React.createElement('div', { style: styles.cardTitle }, '提车应收款'),
React.createElement('div', { style: styles.totalLine },
React.createElement('span', { style: { marginRight: 8 } }, '提车应收款总额:'),
React.createElement('span', { style: styles.totalAmount }, totalPayable + ' 元')
),
React.createElement('table', { style: styles.table }, vehicleTableHeader, vehicleTableBody)
);
var hydrogenCard = React.createElement('div', { style: styles.card },
React.createElement('div', { style: styles.cardTitle }, '氢费预付款'),
React.createElement('div', { style: styles.totalLine },
React.createElement('span', { style: { marginRight: 8 } }, '氢费应收总额:'),
React.createElement('span', { style: styles.totalAmount }, hydrogenPayableTotal + ' 元')
),
React.createElement('div', { style: styles.detailRow },
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '氢费预付款应收金额'), React.createElement('div', { style: styles.formValue }, mockHydrogen.paidTotal + ' 元')),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '减免金额'), React.createElement('div', { style: styles.formValue }, mockHydrogen.discount + ' 元')),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '减免金额备注'), React.createElement('div', { style: styles.formValue }, mockHydrogen.discountRemark || '-'))
)
);
var dayjs = window.dayjs || null;
var invoiceTimeValue = null;
if (invoiceForm.invoiceTime && dayjs) {
var d = dayjs(invoiceForm.invoiceTime, 'YYYY-MM-DD');
invoiceTimeValue = d && d.isValid && d.isValid() ? d : null;
}
var datePickerEl = DatePicker ? React.createElement(DatePicker, {
style: { width: '100%' },
format: 'YYYY-MM-DD',
placeholder: '请选择开票日期',
value: invoiceTimeValue,
onChange: function(d, dateStr) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { invoiceTime: dateStr || '' }); }); }
}) : React.createElement('input', { type: 'date', style: styles.formInput, value: invoiceForm.invoiceTime, onChange: function(e) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { invoiceTime: e.target.value }); }); } });
var uploadButton = Button ? React.createElement(Button, null, '上传附件') : React.createElement('button', { type: 'button', style: styles.defaultBtn }, '上传附件');
var uploadEl = Upload ? React.createElement(Upload, {
accept: '.pdf',
multiple: true,
fileList: invoiceForm.invoiceFiles || [],
onChange: function(info) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { invoiceFiles: info.fileList }); }); },
beforeUpload: function() { return false; },
showUploadList: true
}, uploadButton) : React.createElement('div', { style: { fontSize: 14, color: '#999' } }, '(需加载 antd Upload');
var remarkEl = Input && Input.TextArea ? React.createElement(Input.TextArea, {
placeholder: '选填,用于记录财务相关备注',
value: invoiceForm.remark || '',
onChange: function(e) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { remark: e.target.value }); }); },
rows: 3,
style: { width: '100%' }
}) : React.createElement('textarea', { style: Object.assign({}, styles.formInput, { minHeight: 60, resize: 'vertical' }), value: invoiceForm.remark || '', onChange: function(e) { setInvoiceForm(function(prev) { return Object.assign({}, prev, { remark: e.target.value }); }); }, placeholder: '选填,用于记录财务相关备注' });
var invoiceCard = React.createElement('div', { style: styles.card },
React.createElement('div', { style: styles.cardTitle }, '开票信息'),
React.createElement('div', { style: styles.totalLine },
React.createElement('span', { style: { marginRight: 8 } }, '开票金额:'),
React.createElement('span', { style: styles.totalAmount }, invoiceAmount + ' 元')
),
React.createElement('div', { style: styles.detailRow },
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '客户名称'), React.createElement('div', { style: styles.formValue }, mockInvoice.customerName)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '纳税人识别号'), React.createElement('div', { style: styles.formValue }, mockInvoice.taxId)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '地址'), React.createElement('div', { style: styles.formValue }, mockInvoice.address))
),
React.createElement('div', { style: styles.detailRow },
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '电话'), React.createElement('div', { style: styles.formValue }, mockInvoice.phone)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '账户'), React.createElement('div', { style: styles.formValue }, mockInvoice.account)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '开户行'), React.createElement('div', { style: styles.formValue }, mockInvoice.bank))
),
React.createElement('div', { style: styles.detailRow },
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '邮寄地址'), React.createElement('div', { style: styles.formValue }, mockInvoice.mailingAddress)),
React.createElement('div', { style: styles.formCol }, null),
React.createElement('div', { style: styles.formCol }, null)
),
React.createElement('div', { style: styles.detailRow },
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '开票时间'), React.createElement('div', { style: { width: '100%' } }, datePickerEl)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '发票附件'), React.createElement('div', { style: { width: '100%' } }, uploadEl)),
React.createElement('div', { style: styles.formCol }, React.createElement('div', { style: styles.formLabel }, '备注'), React.createElement('div', { style: { width: '100%' } }, remarkEl))
)
);
var serviceModal = null;
if (serviceModalRow !== null && mockVehicleList[serviceModalRow]) {
var r = mockVehicleList[serviceModalRow];
var serviceList = r.serviceItems || [];
serviceModal = React.createElement('div', { style: styles.modalOverlay, onClick: function() { setServiceModalRow(null); } },
React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } },
React.createElement('div', { style: styles.modalHeader },
React.createElement('span', { style: styles.modalTitle }, '服务费项目'),
React.createElement('button', { type: 'button', style: styles.modalClose, onClick: function() { setServiceModalRow(null); } }, '关闭')
),
React.createElement('table', { style: styles.table },
React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', { style: styles.th }, '服务项目'), React.createElement('th', { style: styles.th }, '应收费用'), React.createElement('th', { style: styles.th }, '服务费用备注'))),
React.createElement('tbody', null, serviceList.map(function(item, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.td }, item.name), React.createElement('td', { style: styles.td }, item.fee), React.createElement('td', { style: styles.td }, item.remark || '-')); }))
)
)
);
}
var footer = React.createElement('div', { style: styles.footer },
React.createElement('button', { type: 'button', style: styles.primaryBtn, onClick: handleSubmit }, '提交'),
React.createElement('button', { type: 'button', style: styles.defaultBtn, onClick: handleCancelClick }, '取消')
);
var requirementModal = requirementVisible ? React.createElement('div', { style: styles.modalOverlay, onClick: function() { setRequirementVisible(false); } },
React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } },
React.createElement('div', { style: styles.modalHeader }, React.createElement('span', { style: styles.modalTitle }, '需求说明'), React.createElement('button', { type: 'button', style: styles.modalClose, onClick: function() { setRequirementVisible(false); } }, '关闭')),
React.createElement('div', { style: styles.modalBody }, requirementContent)
)
) : null;
var cancelConfirmModal = cancelConfirmVisible ? React.createElement('div', { style: styles.modalOverlay, onClick: function() { setCancelConfirmVisible(false); } },
React.createElement('div', { style: styles.modalCard, onClick: function(e) { e.stopPropagation(); } },
React.createElement('div', { style: styles.modalHeader }, React.createElement('span', { style: styles.modalTitle }, '提示'), null),
React.createElement('div', { style: { marginBottom: 20, fontSize: 14, color: '#333' } }, '取消将会丢失所有已添加数据,是否确认取消?'),
React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 12 } },
React.createElement('button', { type: 'button', style: styles.defaultBtn, onClick: function() { handleCancelConfirm(false); } }, '否'),
React.createElement('button', { type: 'button', style: styles.primaryBtn, onClick: function() { handleCancelConfirm(true); } }, '是')
)
)
) : null;
return React.createElement('div', { style: styles.page },
React.createElement('div', { style: styles.pageHeader },
React.createElement('div', { style: styles.breadcrumb }, '租赁费用管理 > 提车应收款 > 收费明细 > 财务提交'),
React.createElement('button', { type: 'button', style: styles.requirementLink, onClick: function() { setRequirementVisible(true); } }, '查看需求说明')
),
projectCard,
vehicleBillCard,
hydrogenCard,
invoiceCard,
footer,
serviceModal,
requirementModal,
cancelConfirmModal
);
};

View File

@@ -0,0 +1,278 @@
// 【重要】必须使用 const Component 作为组件变量名
// 提车应收款 - 收费明细(租赁费用管理-提车应收款第一步由业务填写antd 规范)
const Component = function() {
var useState = React.useState;
var useCallback = React.useCallback;
var useMemo = React.useMemo;
var antd = window.antd;
var Breadcrumb = antd.Breadcrumb;
var Card = antd.Card;
var Input = antd.Input;
var Button = antd.Button;
var Table = antd.Table;
var Modal = antd.Modal;
var message = antd.message;
var App = antd.App;
var mockProject = useMemo(function() {
return { contractCode: 'HT-ZL-2025-001', contractType: '正式合同', projectName: '北京朝阳区租赁项目', customerName: '某某科技有限公司', paymentMethod: '预付', paymentCycle: '1个月', department: '运营部', responsible: '张三' };
}, []);
var mockVehicleList = useMemo(function() {
return [
{ brand: '奔驰', model: 'E300L', plateNo: '京A12345', monthlyRent: '8000.00', deposit: '2000.00', serviceItems: [{ name: '保养服务', fee: '300.00', remark: '' }, { name: '保险', fee: '200.00', remark: '' }] },
{ brand: '宝马', model: '530Li', plateNo: '京B67890', monthlyRent: '7000.00', deposit: '1500.00', serviceItems: [{ name: '保养服务', fee: '250.00', remark: '' }] }
];
}, []);
var mockHydrogen = useMemo(function() {
return { bearer: '客户', paymentMethod: '预付', prepaymentAmount: '3580.00' };
}, []);
var mockInvoice = useMemo(function() {
return { customerName: '某某科技有限公司', taxId: '91330400MA2XXXXX1', address: '浙江省嘉兴市南湖区科技大道1号', phone: '0571-88888888', account: '6222021234567890123', bank: '中国工商银行嘉兴分行', mailingAddress: '浙江省嘉兴市南湖区科技大道1号' };
}, []);
var vehicleRowsState = useState([]);
var vehicleRows = vehicleRowsState[0];
var setVehicleRows = vehicleRowsState[1];
var serviceModalRowState = useState(null);
var serviceModalRow = serviceModalRowState[0];
var setServiceModalRow = serviceModalRowState[1];
var requirementVisibleState = useState(false);
var requirementVisible = requirementVisibleState[0];
var setRequirementVisible = requirementVisibleState[1];
var cancelConfirmVisibleState = useState(false);
var cancelConfirmVisible = cancelConfirmVisibleState[0];
var setCancelConfirmVisible = cancelConfirmVisibleState[1];
var requirementContent = '「车辆管理系统」中的「租赁费用管理」模块下「提车应收款」中「收费明细」模块,第一步由业务填写;\n\n#提车应收款-收款明细\n\n整个页面从上至下为项目信息、车辆首付款、氢费预付款、开票信息4个卡片模块组成\n\n1.项目信息:\n1.1.上方为表单,显示合同编码、合同类型、项目名称、客户名称、付款方式、付款周期、业务部门、业务负责人;\n1.1.1.合同编码:显示租赁合同编号;\n1.1.2.合同类型:显示租赁合同类型,类型有正式合同/试用合同;\n1.1.3.项目名称:显示租赁合同项目名称;\n1.1.4.客户名称:显示租赁合同客户名称;\n1.1.5.付款方式:显示租赁合同付款方式,类型有预付/后付;\n1.1.6.付款周期显示付款周期类型有1个月-12个月\n1.1.7.业务部门:显示租赁合同对应业务部门;\n1.1.8.业务负责人:显示租赁合同对应业务负责人;\n\n2.提车应收款:\n2.1.上方为提车应收款应收总额;\n2.1.1.提车应收款总额显示总金额格式为xx.xx元计算方式为所有应付车辆租金+所有应付保证金+所有应付服务费-所有减免金额;\n2.2.车辆账单:品牌/型号/车牌号/应收车辆租金/车辆租金备注/应收保证金/服务费项目/应收服务费/减免金额/减免金额备注\n2.2.1.-2.2.7. 品牌至服务费项目(点击管理弹出卡片);\n2.1.8.应收服务费:根据服务费项目计算总和;\n2.1.9.减免金额选填默认为0\n2.1.10.减免金额备注:选填;\n\n3.氢费预付款:\n3.1.氢费应收总额(=应收金额-减免金额);氢费预付款应收金额、减免金额、减免金额备注;\n\n4.开票信息:\n4.1.开票金额(=提车应收款总额+氢费应收总额);\n4.2.客户名称、纳税人识别号、地址、电话、账户、开户行、邮寄地址、开票时间(待开票)、发票附件(待上传)、备注(无)\n\n5.页面底部:提交审核、取消;取消二次确认;\n\n6.审批流程:标准/非标情况说明。';
var hydrogenFormState = useState(function() {
var isCustomerPrepay = (mockHydrogen.bearer === '客户' && mockHydrogen.paymentMethod === '预付');
var paid = isCustomerPrepay ? (mockHydrogen.prepaymentAmount || '0.00') : '0.00';
return { paidTotal: paid, discount: '0.00', discountRemark: '' };
});
var hydrogenForm = hydrogenFormState[0];
var setHydrogenForm = hydrogenFormState[1];
var hydrogenPayableTotal = useMemo(function() {
var paid = parseFloat(hydrogenForm.paidTotal) || 0;
var discount = parseFloat(hydrogenForm.discount) || 0;
var v = paid - discount;
return (v >= 0 ? v : 0).toFixed(2);
}, [hydrogenForm.paidTotal, hydrogenForm.discount]);
function formatTwoDecimals(val) {
var n = parseFloat(String(val).replace(/[^\d.-]/g, ''));
if (isNaN(n)) return '0.00';
return n.toFixed(2);
}
React.useEffect(function() {
var rows = mockVehicleList.map(function(v) {
var serviceItems = (v.serviceItems || []).map(function(s) {
return { name: s.name, fee: s.fee || '', remark: s.remark || '' };
});
return {
brand: v.brand,
model: v.model,
plateNo: v.plateNo || '-',
payableRent: v.monthlyRent || '',
rentRemark: '',
deposit: v.deposit || '',
serviceItems: serviceItems,
discount: '0',
discountRemark: ''
};
});
setVehicleRows(rows);
}, [mockVehicleList]);
function getRowServiceTotal(row) {
var total = 0;
var list = row.serviceItems || [];
for (var i = 0; i < list.length; i++) {
var n = parseFloat(list[i].fee);
if (!isNaN(n)) total += n;
}
return total.toFixed(2);
}
var totalPayable = useMemo(function() {
var rentSum = 0, depositSum = 0, serviceSum = 0, discountSum = 0;
for (var i = 0; i < vehicleRows.length; i++) {
var r = vehicleRows[i];
rentSum += parseFloat(r.payableRent) || 0;
depositSum += parseFloat(r.deposit) || 0;
serviceSum += parseFloat(getRowServiceTotal(r)) || 0;
discountSum += parseFloat(r.discount) || 0;
}
return (rentSum + depositSum + serviceSum - discountSum).toFixed(2);
}, [vehicleRows]);
var invoiceAmount = useMemo(function() {
return (parseFloat(totalPayable) + parseFloat(hydrogenPayableTotal)).toFixed(2);
}, [totalPayable, hydrogenPayableTotal]);
var handleVehicleChange = useCallback(function(rowIndex, field, value) {
setVehicleRows(function(prev) {
var next = prev.slice();
var row = next[rowIndex] ? Object.assign({}, next[rowIndex]) : next[rowIndex];
row[field] = value;
next[rowIndex] = row;
return next;
});
}, []);
var handleServiceItemChange = useCallback(function(rowIndex, itemIndex, field, value) {
setVehicleRows(function(prev) {
var next = prev.slice();
var row = next[rowIndex] ? Object.assign({}, next[rowIndex]) : next[rowIndex];
var items = (row.serviceItems || []).slice();
var item = items[itemIndex] ? Object.assign({}, items[itemIndex]) : items[itemIndex];
item[field] = value;
items[itemIndex] = item;
row.serviceItems = items;
next[rowIndex] = row;
return next;
});
}, []);
var handleSubmit = useCallback(function() {
var err = [];
for (var i = 0; i < vehicleRows.length; i++) {
var r = vehicleRows[i];
if (!r.payableRent || String(r.payableRent).trim() === '') err.push('第' + (i + 1) + '行应收车辆租金');
if (!r.deposit || String(r.deposit).trim() === '') err.push('第' + (i + 1) + '行应收保证金');
var items = r.serviceItems || [];
for (var j = 0; j < items.length; j++) {
if (!items[j].fee || String(items[j].fee).trim() === '') err.push('第' + (i + 1) + '行服务费项「' + (items[j].name || '') + '」应收费用');
}
}
if (!hydrogenForm.paidTotal || String(hydrogenForm.paidTotal).trim() === '') err.push('氢费预付款应收金额');
if (!hydrogenForm.discount || String(hydrogenForm.discount).trim() === '') err.push('氢费减免金额');
if (err.length > 0) {
message.warning('请填写必填项:' + err.join('、'));
return;
}
message.success('提交成功,该条记录已移至审核中心,审核状态为待审核');
}, [vehicleRows, hydrogenForm]);
var handleCancelClick = useCallback(function() { setCancelConfirmVisible(true); }, []);
var handleCancelConfirm = useCallback(function(confirmed) {
setCancelConfirmVisible(false);
if (confirmed) message.info('已返回提车应收款列表');
}, []);
var layoutStyle = { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', fontSize: 14 };
var formRowStyle = { display: 'flex', flexWrap: 'wrap', marginBottom: 16 };
var formColStyle = { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 };
var labelStyle = { display: 'block', marginBottom: 6, color: 'rgba(0,0,0,0.85)' };
var labelRequiredStyle = { color: '#ff4d4f', marginRight: 4 };
var totalLineStyle = { marginBottom: 16, fontSize: 14 };
var totalAmountStyle = { color: '#1890ff', fontWeight: 600, fontSize: 16 };
var footerStyle = { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, zIndex: 99 };
var FormItem = function(props) {
return React.createElement('div', { style: props.colStyle || formColStyle },
React.createElement('label', { style: labelStyle }, props.required ? React.createElement('span', { style: labelRequiredStyle }, '*') : null, props.label),
props.children
);
};
var projectFields = React.createElement('div', { style: formRowStyle },
React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: mockProject.contractCode, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '合同类型' }, React.createElement(Input, { value: mockProject.contractType, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '项目名称' }, React.createElement(Input, { value: mockProject.projectName, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '客户名称' }, React.createElement(Input, { value: mockProject.customerName, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '付款方式' }, React.createElement(Input, { value: mockProject.paymentMethod, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '付款周期' }, React.createElement(Input, { value: mockProject.paymentCycle, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '业务部门' }, React.createElement(Input, { value: mockProject.department, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '业务负责人' }, React.createElement(Input, { value: mockProject.responsible, disabled: true, style: { width: '100%' } }))
);
var vehicleColumns = [
{ title: '品牌', dataIndex: 'brand', key: 'brand', width: 90 },
{ title: '型号', dataIndex: 'model', key: 'model', width: 100 },
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 },
{ title: '应收车辆租金', key: 'payableRent', width: 120, render: function(_, r, idx) { return React.createElement(Input, { value: r.payableRent, onChange: function(e) { handleVehicleChange(idx, 'payableRent', e.target.value); }, style: { width: '100%' }, addonAfter: '元' }); } },
{ title: '车辆租金备注', key: 'rentRemark', width: 110, render: function(_, r, idx) { return React.createElement(Input, { value: r.rentRemark || '', onChange: function(e) { handleVehicleChange(idx, 'rentRemark', e.target.value); }, placeholder: '选填', style: { width: '100%' } }); } },
{ title: '应收保证金', dataIndex: 'deposit', key: 'deposit', width: 100 },
{ title: '服务费项目', key: 'service', width: 90, render: function(_, r, idx) { return React.createElement(Button, { type: 'link', size: 'small', onClick: function() { setServiceModalRow(idx); } }, '管理'); } },
{ title: '应收服务费', key: 'serviceTotal', width: 100, render: function(_, r) { return getRowServiceTotal(r); } },
{ title: '减免金额', key: 'discount', width: 100, render: function(_, r, idx) { return React.createElement(Input, { value: r.discount, onChange: function(e) { handleVehicleChange(idx, 'discount', e.target.value); }, placeholder: '0', style: { width: '100%' } }); } },
{ title: '减免金额备注', key: 'discountRemark', width: 110, render: function(_, r, idx) { return React.createElement(Input, { value: r.discountRemark || '', onChange: function(e) { handleVehicleChange(idx, 'discountRemark', e.target.value); }, placeholder: '选填', style: { width: '100%' } }); } }
];
var vehicleTable = React.createElement(Table, {
rowKey: function(_, i) { return String(i); },
columns: vehicleColumns,
dataSource: vehicleRows,
pagination: false,
size: 'small',
scroll: { x: 1000 }
});
var hydrogenFields = React.createElement('div', { style: formRowStyle },
React.createElement(FormItem, { label: '氢费预付款应收金额', required: true }, React.createElement(Input, { value: hydrogenForm.paidTotal, onChange: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { paidTotal: e.target.value }); }); }, onBlur: function() { setHydrogenForm(function(prev) { return Object.assign({}, prev, { paidTotal: formatTwoDecimals(prev.paidTotal) }); }); }, placeholder: '0.00', addonAfter: '元', style: { width: '100%' } })),
React.createElement(FormItem, { label: '减免金额', required: true }, React.createElement(Input, { value: hydrogenForm.discount, onChange: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { discount: e.target.value }); }); }, onBlur: function() { setHydrogenForm(function(prev) { return Object.assign({}, prev, { discount: formatTwoDecimals(prev.discount) }); }); }, placeholder: '0.00', addonAfter: '元', style: { width: '100%' } })),
React.createElement(FormItem, { label: '减免金额备注' }, React.createElement(Input, { value: hydrogenForm.discountRemark || '', onChange: function(e) { setHydrogenForm(function(prev) { return Object.assign({}, prev, { discountRemark: e.target.value }); }); }, placeholder: '选填', style: { width: '100%' } }))
);
var invoiceFields = React.createElement('div', { style: formRowStyle },
React.createElement(FormItem, { label: '客户名称' }, React.createElement(Input, { value: mockInvoice.customerName, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '纳税人识别号' }, React.createElement(Input, { value: mockInvoice.taxId, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '地址' }, React.createElement(Input, { value: mockInvoice.address, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '电话' }, React.createElement(Input, { value: mockInvoice.phone, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '账户' }, React.createElement(Input, { value: mockInvoice.account, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '开户行' }, React.createElement(Input, { value: mockInvoice.bank, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '邮寄地址' }, React.createElement(Input, { value: mockInvoice.mailingAddress, disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '开票时间' }, React.createElement(Input, { value: '待开票', disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '发票附件' }, React.createElement(Input, { value: '待上传', disabled: true, style: { width: '100%' } })),
React.createElement(FormItem, { label: '备注' }, React.createElement(Input, { value: '无', disabled: true, style: { width: '100%' } }))
);
var serviceModalDataSource = serviceModalRow !== null && vehicleRows[serviceModalRow] ? (vehicleRows[serviceModalRow].serviceItems || []) : [];
var serviceModalColumns = [
{ title: '服务项目', dataIndex: 'name', key: 'name', width: 140 },
{ title: '应收费用', key: 'fee', width: 140, render: function(_, item, itemIndex) { return React.createElement(Input, { value: item.fee, onChange: function(e) { handleServiceItemChange(serviceModalRow, itemIndex, 'fee', e.target.value); }, style: { width: '100%' } }); } },
{ title: '服务费用备注', key: 'remark', width: 180, render: function(_, item, itemIndex) { return React.createElement(Input, { value: item.remark || '', onChange: function(e) { handleServiceItemChange(serviceModalRow, itemIndex, 'remark', e.target.value); }, placeholder: '选填', style: { width: '100%' } }); } }
];
var serviceModalOpen = serviceModalRow !== null;
var serviceModalContent = serviceModalOpen ? React.createElement(Table, { rowKey: function(_, i) { return String(i); }, columns: serviceModalColumns, dataSource: serviceModalDataSource, pagination: false, size: 'small' }) : null;
var serviceModalFooter = React.createElement(Button, { onClick: function() { setServiceModalRow(null); } }, '关闭');
var requirementModalFooter = React.createElement(Button, { onClick: function() { setRequirementVisible(false); } }, '关闭');
var cancelModalOk = function() { handleCancelConfirm(true); };
var cancelModalCancel = function() { setCancelConfirmVisible(false); };
return React.createElement(App, null,
React.createElement('div', { style: layoutStyle },
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } },
React.createElement(Breadcrumb, { items: [{ title: '租赁费用管理' }, { title: '提车应收款' }, { title: '收费明细' }] }),
React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function() { setRequirementVisible(true); } }, '查看需求说明')
),
React.createElement(Card, { title: '项目信息', style: { marginBottom: 16 } }, projectFields),
React.createElement(Card, { title: '提车应收款', style: { marginBottom: 16 } },
React.createElement('div', { style: totalLineStyle },
React.createElement('span', { style: { marginRight: 8 } }, '提车应收款总额:'),
React.createElement('span', { style: totalAmountStyle }, totalPayable + ' 元')
),
vehicleTable
),
React.createElement(Card, { title: '氢费预付款', style: { marginBottom: 16 } },
React.createElement('div', { style: totalLineStyle },
React.createElement('span', { style: { marginRight: 8 } }, '氢费应收总额:'),
React.createElement('span', { style: totalAmountStyle }, hydrogenPayableTotal + ' 元')
),
hydrogenFields
),
React.createElement(Card, { title: '开票信息', style: { marginBottom: 80 } },
React.createElement('div', { style: totalLineStyle },
React.createElement('span', { style: { marginRight: 8 } }, '开票金额:'),
React.createElement('span', { style: totalAmountStyle }, invoiceAmount + ' 元')
),
invoiceFields
),
React.createElement('div', { style: footerStyle },
React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交审核'),
React.createElement(Button, { onClick: handleCancelClick }, '取消')
),
React.createElement(Modal, { title: '服务费项目', open: serviceModalOpen, onCancel: function() { setServiceModalRow(null); }, footer: serviceModalFooter, destroyOnClose: true }, serviceModalContent),
React.createElement(Modal, { title: '需求说明', open: requirementVisible, onCancel: function() { setRequirementVisible(false); }, width: 720, footer: requirementModalFooter, bodyStyle: { maxHeight: '70vh', overflow: 'auto' } }, React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.6 } }, requirementContent)),
React.createElement(Modal, { title: '提示', open: cancelConfirmVisible, onCancel: cancelModalCancel, onOk: cancelModalOk, okText: '是', cancelText: '否' }, React.createElement('div', { style: { fontSize: 14 } }, '取消将会丢失所有已添加数据,是否确认取消?'))
)
);
};