Files
ONE-OS/web端/财务管理/提车收款-收费明细.jsx

279 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 【重要】必须使用 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 } }, '取消将会丢失所有已添加数据,是否确认取消?'))
)
);
};