Files
ONE-OS/web端/业务管理/租赁账单.jsx
王冕 09cc45db36 Initial commit: ONE-OS project
Made-with: Cursor
2026-02-27 18:11:40 +08:00

934 lines
43 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 useEffect = React.useEffect;
var antd = window.antd;
var Breadcrumb = antd.Breadcrumb;
var Card = antd.Card;
var Select = antd.Select;
var Button = antd.Button;
var Table = antd.Table;
var Tag = antd.Tag;
var Modal = antd.Modal;
var Input = antd.Input;
var message = antd.message;
var Space = antd.Space;
var Row = antd.Row;
var Col = antd.Col;
// 当前视图list | detail
var viewState = useState('list');
var currentView = viewState[0];
var setCurrentView = viewState[1];
// 分页
var pageState = useState(1);
var currentPage = pageState[0];
var setCurrentPage = pageState[1];
var pageSizeState = useState(10);
var pageSize = pageSizeState[0];
var setPageSize = pageSizeState[1];
// 选中项Table rowSelection 用)
var selectedRowKeysState = useState([]);
var selectedRowKeys = selectedRowKeysState[0];
var setSelectedRowKeys = selectedRowKeysState[1];
// 筛选器(表单值)
var contractFilterState = useState(undefined);
var contractFilter = contractFilterState[0];
var setContractFilter = contractFilterState[1];
var projectFilterState = useState(undefined);
var projectFilter = projectFilterState[0];
var setProjectFilter = projectFilterState[1];
var customerFilterState = useState(undefined);
var customerFilter = customerFilterState[0];
var setCustomerFilter = customerFilterState[1];
var statusFilterState = useState([]);
var statusFilter = statusFilterState[0];
var setStatusFilter = statusFilterState[1];
// 已应用的筛选条件(点击查询后生效)
var appliedFilterState = useState({ contract: '', project: '', customer: '', status: [] });
var appliedFilter = appliedFilterState[0];
var setAppliedFilter = appliedFilterState[1];
// 弹窗:应付金额明细 | 服务费明细
var popoverState = useState({ type: null, data: null });
var popover = popoverState[0];
var setPopover = popoverState[1];
// 照片查看器
var photoViewerState = useState({ visible: false, photos: [], currentIndex: 0 });
var photoViewer = photoViewerState[0];
var setPhotoViewer = photoViewerState[1];
var viewingBillIdState = useState(null);
var viewingBillId = viewingBillIdState[0];
var setViewingBillId = viewingBillIdState[1];
// 详情模式view | payment
var detailModeState = useState('view');
var detailMode = detailModeState[0];
var setDetailMode = detailModeState[1];
// 付款表单数据
var paymentFormState = useState({
discountAmount: '',
discountReason: '',
vehicleList: [],
hydrogenList: [],
violationList: [],
returnFeeList: []
});
var paymentForm = paymentFormState[0];
var setPaymentForm = paymentFormState[1];
// 列表内成本编辑
var editingListCellState = useState(null);
var editingListCell = editingListCellState[0];
var setEditingListCell = editingListCellState[1];
var editingInputValueState = useState('');
var editingInputValue = editingInputValueState[0];
var setEditingInputValue = editingInputValueState[1];
var listEditableCostsState = useState({});
var listEditableCosts = listEditableCostsState[0];
var setListEditableCosts = listEditableCostsState[1];
// 模拟选项与数据(与原文一致)
var contractOptions = useMemo(function() {
return [
{ value: 'HT001', label: 'HT001 - 租赁合同A' },
{ value: 'HT002', label: 'HT002 - 租赁合同B' },
{ value: 'HT003', label: 'HT003 - 租赁合同C' }
];
}, []);
var projectOptions = useMemo(function() {
return [
{ value: 'XM001', label: 'XM001 - 北京项目' },
{ value: 'XM002', label: 'XM002 - 上海项目' },
{ value: 'XM003', label: 'XM003 - 广州项目' }
];
}, []);
var customerOptions = useMemo(function() {
return [
{ value: 'KH001', label: 'KH001 - 科技公司A' },
{ value: 'KH002', label: 'KH002 - 制造企业B' },
{ value: 'KH003', label: 'KH003 - 物流公司C' }
];
}, []);
var statusOptions = [
{ value: 'paid', label: '已付款' },
{ value: 'partial', label: '部分付款' },
{ value: 'unpaid', label: '未付款' }
];
var mockBillList = useMemo(function() {
var list = [];
for (var i = 1; i <= 25; i++) {
var statuses = ['paid', 'partial', 'unpaid'];
var status = statuses[i % 3];
list.push({
id: 'BILL' + i,
contractCode: 'HT00' + (i % 3 + 1),
projectName: 'XM00' + (i % 3 + 1) + ' - 项目' + i,
customerName: 'KH00' + (i % 3 + 1) + ' - 客户' + i,
period: i,
payableAmount: (15000 + i * 500).toFixed(2),
paidAmount: status === 'paid' ? (15000 + i * 500).toFixed(2) : status === 'partial' ? (8000).toFixed(2) : '0.00',
discountAmount: (i % 5 === 0 ? 200 : 0).toFixed(2),
remark: status !== 'unpaid' ? '已付款备注' + i : '-',
vehicleCost: (5000 + i * 100).toFixed(2),
hydrogenCost: (300 + i * 20).toFixed(2),
otherCost: (200 + i * 10).toFixed(2),
status: status
});
}
return list;
}, []);
var mockPayableDetail = useMemo(function() {
return [
{ startDate: '2025-01-01', endDate: '2025-01-31', plateNo: '京A12345', rent: '8000.00', serviceFee: '500.00', deposit: '2000.00' },
{ startDate: '2025-01-01', endDate: '2025-01-31', plateNo: '京B67890', rent: '7000.00', serviceFee: '400.00', deposit: '1500.00' }
];
}, []);
var mockBillDetail = useMemo(function() {
return {
startDate: '2025-01-01',
endDate: '2025-01-31',
contractCode: 'HT001',
projectName: 'XM001 - 北京项目',
customerName: 'KH001 - 科技公司A',
department: '运营部',
responsible: '张三',
paymentCycle: '先付付款周期1个月',
discountTotal: '200.00',
discountReason: '长期合作客户优惠,首期账单减免部分金额'
};
}, []);
var mockVehicleList = useMemo(function() {
return [
{ brand: '奔驰', model: 'E300L', plateNo: '京A12345', planDelivery: '2024-12-25', actualDelivery: '2024-12-28', billStart: '2025-01-01', billEnd: '2025-01-31', monthlyRent: '8000.00', paidMonthlyRent: '7600.00', serviceFee: '500.00', paidServiceFee: '480.00', deposit: '2000.00', paidDeposit: '2000.00', serviceItems: [{ name: '保养服务', price: '300.00', effectiveDate: '2025-01-01' }, { name: '保险', price: '200.00', effectiveDate: '2025-01-01' }] },
{ brand: '宝马', model: '530Li', plateNo: '京B67890', planDelivery: '2024-12-20', actualDelivery: '2024-12-22', billStart: '2025-01-01', billEnd: '2025-01-31', monthlyRent: '7000.00', paidMonthlyRent: '7000.00', serviceFee: '400.00', paidServiceFee: '380.00', deposit: '1500.00', paidDeposit: '1500.00', serviceItems: [{ name: '保养服务', price: '250.00', effectiveDate: '2025-01-01' }] }
];
}, []);
var mockReturnFeeData = useMemo(function() {
return {
totalAmount: '2850.00',
paidTotalAmount: '2700.00',
list: [
{ feeName: '车辆外观损伤费', amount: '800.00', paidAmount: '760.00', photos: ['https://picsum.photos/80/80?random=1', 'https://picsum.photos/80/80?random=2'], attachments: [{ name: '外观损伤说明.pdf' }] },
{ feeName: '轮胎磨损费', amount: '1200.00', paidAmount: '1150.00', photos: ['https://picsum.photos/80/80?random=3'], attachments: [{ name: '轮胎检测报告.pdf' }, { name: '维修单据.pdf' }] },
{ feeName: '内饰清洁费', amount: '450.00', paidAmount: '430.00', photos: ['https://picsum.photos/80/80?random=4', 'https://picsum.photos/80/80?random=5', 'https://picsum.photos/80/80?random=6'], attachments: [] },
{ feeName: '油量补充费', amount: '400.00', paidAmount: '360.00', photos: [], attachments: [{ name: '加油凭证.jpg' }] }
]
};
}, []);
var mockViolationData = useMemo(function() {
return {
violationCount: 3,
totalAmount: '650.00',
paidTotalAmount: '600.00',
list: [
{ violationTime: '2025-01-10 08:30:00', plateNo: '京A12345', violationType: '违停', location: '北京市朝阳区xxx路', fineAmount: '200.00', paidFineAmount: '200.00' },
{ violationTime: '2025-01-18 14:20:00', plateNo: '京B67890', violationType: '超速', location: '北京市海淀区xxx大道', fineAmount: '200.00', paidFineAmount: '180.00' },
{ violationTime: '2025-01-25 09:15:00', plateNo: '京A12345', violationType: '闯红灯', location: '北京市东城区xxx路口', fineAmount: '250.00', paidFineAmount: '220.00' }
]
};
}, []);
var mockHydrogenData = useMemo(function() {
return {
refuelCount: 12,
balance: '3580.00',
list: [
{ refuelTime: '2025-01-15 09:30:00', stationName: '北京朝阳加氢站', plateNo: '京A12345', amount: '15.5', costPrice: '28.00', feePrice: '434.00', paidFeePrice: '420.00' },
{ refuelTime: '2025-01-18 14:20:00', stationName: '北京海淀加氢站', plateNo: '京B67890', amount: '12.0', costPrice: '28.00', feePrice: '336.00', paidFeePrice: '336.00' },
{ refuelTime: '2025-01-22 11:00:00', stationName: '北京朝阳加氢站', plateNo: '京A12345', amount: '18.2', costPrice: '28.00', feePrice: '509.60', paidFeePrice: '490.00' }
]
};
}, []);
var vehicleBillTotals = useMemo(function() {
var list = mockVehicleList || [];
var monthlyRentTotal = 0, paidRentTotal = 0, serviceFeeTotal = 0, paidServiceFeeTotal = 0, depositTotal = 0, paidDepositTotal = 0;
list.forEach(function(v) {
monthlyRentTotal += parseFloat(v.monthlyRent || 0);
paidRentTotal += parseFloat(v.paidMonthlyRent || 0);
serviceFeeTotal += parseFloat(v.serviceFee || 0);
paidServiceFeeTotal += parseFloat(v.paidServiceFee || 0);
depositTotal += parseFloat(v.deposit || 0);
paidDepositTotal += parseFloat(v.paidDeposit || 0);
});
return {
monthlyRentTotal: monthlyRentTotal.toFixed(2),
paidRentTotal: paidRentTotal.toFixed(2),
serviceFeeTotal: serviceFeeTotal.toFixed(2),
paidServiceFeeTotal: paidServiceFeeTotal.toFixed(2),
depositTotal: depositTotal.toFixed(2),
paidDepositTotal: paidDepositTotal.toFixed(2)
};
}, [mockVehicleList]);
var billInfoTotals = useMemo(function() {
var monthlyRentTotal = parseFloat(vehicleBillTotals.monthlyRentTotal || 0);
var serviceFeeTotal = parseFloat(vehicleBillTotals.serviceFeeTotal || 0);
var depositTotal = parseFloat(vehicleBillTotals.depositTotal || 0);
var hydrogenTotal = 0;
(mockHydrogenData.list || []).forEach(function(item) { hydrogenTotal += parseFloat(item.feePrice || 0); });
var violationTotal = parseFloat(mockViolationData.totalAmount || 0);
var returnFeeTotal = parseFloat(mockReturnFeeData.totalAmount || 0);
var payableTotal = monthlyRentTotal + serviceFeeTotal + depositTotal + hydrogenTotal + violationTotal + returnFeeTotal;
var paidTotal = 0;
(mockVehicleList || []).forEach(function(v) {
paidTotal += parseFloat(v.paidMonthlyRent || 0) + parseFloat(v.paidServiceFee || 0) + parseFloat(v.paidDeposit || 0);
});
var discountTotal = parseFloat(mockBillDetail.discountTotal || 0);
var unpaidTotal = Math.max(0, payableTotal - paidTotal - discountTotal);
return {
payableTotal: payableTotal.toFixed(2),
paidTotal: paidTotal.toFixed(2),
unpaidTotal: unpaidTotal.toFixed(2)
};
}, [vehicleBillTotals, mockHydrogenData, mockViolationData, mockReturnFeeData, mockVehicleList, mockBillDetail]);
var vehicleTotals = billInfoTotals;
var filteredList = useMemo(function() {
var list = (mockBillList || []).slice();
var c = (appliedFilter.contract || '').trim();
var p = (appliedFilter.project || '').trim();
var cust = (appliedFilter.customer || '').trim();
var st = Array.isArray(appliedFilter.status) ? appliedFilter.status : [];
if (c) list = list.filter(function(item) { return String(item.contractCode || '').toLowerCase().indexOf(c.toLowerCase()) >= 0; });
if (p) list = list.filter(function(item) { return String(item.projectName || '').toLowerCase().indexOf(p.toLowerCase()) >= 0; });
if (cust) list = list.filter(function(item) { return String(item.customerName || '').toLowerCase().indexOf(cust.toLowerCase()) >= 0; });
if (st.length > 0) {
var statusSet = {};
st.forEach(function(s) { statusSet[s] = true; });
list = list.filter(function(item) { return statusSet[item.status]; });
}
return list;
}, [mockBillList, appliedFilter.contract, appliedFilter.project, appliedFilter.customer, appliedFilter.status]);
var totalCount = filteredList.length;
var paginatedList = useMemo(function() {
var start = (currentPage - 1) * pageSize;
return filteredList.slice(start, start + pageSize);
}, [filteredList, currentPage, pageSize]);
var handleSearch = useCallback(function() {
setAppliedFilter({
contract: (contractFilter != null && contractFilter !== '') ? String(contractFilter) : '',
project: (projectFilter != null && projectFilter !== '') ? String(projectFilter) : '',
customer: (customerFilter != null && customerFilter !== '') ? String(customerFilter) : '',
status: Array.isArray(statusFilter) ? statusFilter.slice() : []
});
setCurrentPage(1);
}, [contractFilter, projectFilter, customerFilter, statusFilter]);
var handleReset = useCallback(function() {
setContractFilter(undefined);
setProjectFilter(undefined);
setCustomerFilter(undefined);
setStatusFilter([]);
setAppliedFilter({ contract: '', project: '', customer: '', status: [] });
setCurrentPage(1);
}, []);
var handleExport = useCallback(function() {
if (selectedRowKeys.length === 0) {
message.warning('请先选择要导出的数据');
return;
}
message.success('导出 ' + selectedRowKeys.length + ' 条数据');
}, [selectedRowKeys.length]);
var handleView = useCallback(function(id, isPayment) {
setViewingBillId(id);
setDetailMode(isPayment ? 'payment' : 'view');
if (isPayment) {
var vl = mockVehicleList.map(function(v) {
return { paidMonthlyRent: v.paidMonthlyRent || '', paidServiceFee: v.paidServiceFee || '', paidDeposit: v.paidDeposit || '' };
});
var hl = mockHydrogenData.list.map(function(item) { return { paidFeePrice: item.paidFeePrice || '' }; });
var viol = mockViolationData.list.map(function(item) { return { paidFineAmount: item.paidFineAmount || '' }; });
var rfl = mockReturnFeeData.list.map(function(item) { return { paidAmount: item.paidAmount || '' }; });
setPaymentForm({
discountAmount: mockBillDetail.discountTotal || '',
discountReason: mockBillDetail.discountReason || '',
vehicleList: vl,
hydrogenList: hl,
violationList: viol,
returnFeeList: rfl
});
}
setCurrentView('detail');
}, []);
var handleBackToList = useCallback(function() {
setCurrentView('list');
setViewingBillId(null);
setDetailMode('view');
}, []);
var handlePaymentFormChange = useCallback(function(section, index, field, value) {
setPaymentForm(function(prev) {
var next = {
discountAmount: prev.discountAmount,
discountReason: prev.discountReason,
vehicleList: prev.vehicleList.slice(),
hydrogenList: prev.hydrogenList.slice(),
violationList: prev.violationList.slice(),
returnFeeList: prev.returnFeeList.slice()
};
if (section === 'bill') {
if (field === 'discountAmount') next.discountAmount = value;
if (field === 'discountReason') next.discountReason = value;
} else if (section === 'vehicle' && index >= 0 && index < next.vehicleList.length) {
next.vehicleList[index] = Object.assign({}, next.vehicleList[index], { [field]: value });
} else if (section === 'hydrogen' && index >= 0 && index < next.hydrogenList.length) {
next.hydrogenList[index] = Object.assign({}, next.hydrogenList[index], { [field]: value });
} else if (section === 'violation' && index >= 0 && index < next.violationList.length) {
next.violationList[index] = Object.assign({}, next.violationList[index], { [field]: value });
} else if (section === 'returnFee' && index >= 0 && index < next.returnFeeList.length) {
next.returnFeeList[index] = Object.assign({}, next.returnFeeList[index], { [field]: value });
}
return next;
});
}, []);
var handleSubmit = useCallback(function() {
var errors = [];
if (!paymentForm.discountAmount || String(paymentForm.discountAmount).trim() === '') errors.push('减免金额');
if (!paymentForm.discountReason || String(paymentForm.discountReason).trim() === '') errors.push('减免原因');
paymentForm.vehicleList.forEach(function(v, i) {
if (!v.paidMonthlyRent || String(v.paidMonthlyRent).trim() === '') errors.push('车辆账单-实付月租金(第' + (i + 1) + '行)');
if (!v.paidServiceFee || String(v.paidServiceFee).trim() === '') errors.push('车辆账单-实付服务费(第' + (i + 1) + '行)');
if (!v.paidDeposit || String(v.paidDeposit).trim() === '') errors.push('车辆账单-实付保证金(第' + (i + 1) + '行)');
});
paymentForm.hydrogenList.forEach(function(h, i) {
if (!h.paidFeePrice || String(h.paidFeePrice).trim() === '') errors.push('氢费账单-实付氢费金额(第' + (i + 1) + '行)');
});
paymentForm.violationList.forEach(function(v, i) {
if (!v.paidFineAmount || String(v.paidFineAmount).trim() === '') errors.push('违章费用-实付罚款金额(第' + (i + 1) + '行)');
});
paymentForm.returnFeeList.forEach(function(r, i) {
if (!r.paidAmount || String(r.paidAmount).trim() === '') errors.push('还车费用-实付金额(第' + (i + 1) + '行)');
});
if (errors.length > 0) {
message.warning('请填写必填项:' + errors.join('、'));
return;
}
message.success('提交成功');
handleBackToList();
}, [paymentForm, handleBackToList]);
var handleSave = useCallback(function() {
message.success('保存成功');
}, []);
function getStatusText(status) {
if (status === 'paid') return '已付款';
if (status === 'partial') return '部分付款';
return '未付款';
}
function getStatusTagColor(status) {
if (status === 'paid') return 'success';
if (status === 'partial') return 'warning';
return 'error';
}
// 列表表格列(含行内编辑氢费/其他成本)
function renderEditableCost(row, field, label) {
var rowId = row.id;
var val = (listEditableCosts[rowId] && listEditableCosts[rowId][field] !== undefined)
? listEditableCosts[rowId][field]
: (row[field] != null ? row[field] : '0.00');
var num = (parseFloat(val) || 0).toFixed(2);
var isEditing = editingListCell && editingListCell.rowId === rowId && editingListCell.field === field;
if (isEditing) {
return React.createElement(Space, { key: 'edit', size: 4 },
React.createElement(Input, {
value: editingInputValue,
onChange: function(e) { setEditingInputValue(e.target.value); },
onBlur: function() {
var n = parseFloat(editingInputValue);
var formatted = (isNaN(n) ? 0 : n).toFixed(2);
setListEditableCosts(function(prev) {
var next = Object.assign({}, prev);
next[rowId] = Object.assign({}, next[rowId], { [field]: formatted });
return next;
});
setEditingListCell(null);
setEditingInputValue('');
},
style: { width: 80 },
autoFocus: true
}),
React.createElement('span', null, '元')
);
}
return React.createElement('span', {
style: { cursor: 'pointer' },
onClick: function() {
setEditingInputValue(num);
setEditingListCell({ rowId: rowId, field: field });
}
}, num);
}
var listColumns = useMemo(function() {
return [
{ title: '付款状态', dataIndex: 'status', key: 'status', width: 100, render: function(s) { return React.createElement(Tag, { color: getStatusTagColor(s) }, getStatusText(s)); } },
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 120 },
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160 },
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160 },
{ title: '当前期数', dataIndex: 'period', key: 'period', width: 90 },
{ title: '应付金额', dataIndex: 'payableAmount', key: 'payableAmount', width: 110, render: function(_, row) {
return React.createElement('a', {
onClick: function() { setPopover({ type: 'payable', data: mockPayableDetail }); },
style: { color: '#1677ff', fontWeight: 600, textDecoration: 'underline', cursor: 'pointer' }
}, row.payableAmount);
}},
{ title: '实付金额', dataIndex: 'paidAmount', key: 'paidAmount', width: 110 },
{ title: '减免金额', dataIndex: 'discountAmount', key: 'discountAmount', width: 100 },
{ title: '未付金额', key: 'unpaid', width: 100, render: function(_, row) {
return (parseFloat(row.payableAmount || 0) - parseFloat(row.paidAmount || 0) - parseFloat(row.discountAmount || 0)).toFixed(2);
}},
{ title: '备注', dataIndex: 'remark', key: 'remark', width: 120 },
{ title: '车辆成本(元)', dataIndex: 'vehicleCost', key: 'vehicleCost', width: 120, render: function(val) { return (parseFloat(val) || 0).toFixed(2); } },
{ title: '氢费成本(元)', key: 'hydrogenCost', width: 120, render: function(_, row) { return renderEditableCost(row, 'hydrogenCost', '氢费'); } },
{ title: '其他成本(元)', key: 'otherCost', width: 120, render: function(_, row) { return renderEditableCost(row, 'otherCost', '其他'); } },
{ title: '操作', key: 'action', width: 140, fixed: 'right', render: function(_, row) {
return React.createElement(Space, null,
React.createElement(Button, { type: 'link', size: 'small', onClick: function() { handleView(row.id, false); } }, '查看'),
React.createElement(Button, { type: 'link', size: 'small', style: { color: '#52c41a' }, onClick: function() { handleView(row.id, true); } }, '收费')
);
}}
];
}, [listEditableCosts, editingListCell, editingInputValue, handleView]);
var rowSelection = useMemo(function() {
return {
selectedRowKeys: selectedRowKeys,
onChange: function(keys) { setSelectedRowKeys(keys || []); }
};
}, [selectedRowKeys]);
var tablePagination = useMemo(function() {
return {
current: currentPage,
pageSize: pageSize,
total: totalCount,
showSizeChanger: true,
showTotal: function(t) { return '共 ' + t + ' 条'; },
pageSizeOptions: ['10', '20', '50'],
onChange: function(page, size) {
setCurrentPage(page);
if (size !== pageSize) setPageSize(size);
}
};
}, [currentPage, pageSize, totalCount]);
// —————— 详情视图 ——————
if (currentView === 'detail') {
var detailBreadcrumbItems = [
{ title: '运维管理' },
{ title: '业务管理' },
{ title: '租赁账单' },
{ title: detailMode === 'payment' ? '收费' : '查看' }
];
var billInfoCols = [
{ label: '账单开始日期', value: mockBillDetail.startDate },
{ label: '账单结束日期', value: mockBillDetail.endDate },
{ label: '合同编码', value: mockBillDetail.contractCode },
{ label: '项目名称', value: mockBillDetail.projectName },
{ label: '客户名称', value: mockBillDetail.customerName },
{ label: '业务部门', value: mockBillDetail.department },
{ label: '业务负责人', value: mockBillDetail.responsible },
{ label: '付款周期', value: mockBillDetail.paymentCycle }
];
return React.createElement('div', { style: { padding: 24, background: '#f5f5f5', minHeight: '100vh' } },
React.createElement(Breadcrumb, { items: detailBreadcrumbItems, style: { marginBottom: 16 } }),
React.createElement(Card, { style: { marginBottom: 16 } },
React.createElement('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 16, paddingBottom: 12, borderBottom: '1px solid #f0f0f0' } }, '账单信息'),
React.createElement(Row, { gutter: [24, 24] },
billInfoCols.map(function(item, i) {
return React.createElement(Col, { key: i, span: 8 },
React.createElement('div', { style: { marginBottom: 8 } },
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, item.label + ''),
React.createElement('span', null, item.value)
)
);
}),
React.createElement(Col, { span: 8 },
React.createElement('div', { style: { marginBottom: 8 } },
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '应付款总额:'),
React.createElement('span', { style: { color: '#1890ff', fontWeight: 600 } }, vehicleTotals.payableTotal + ' 元')
)
),
React.createElement(Col, { span: 8 },
React.createElement('div', { style: { marginBottom: 8 } },
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '实付款总额:'),
React.createElement('span', { style: { color: '#52c41a', fontWeight: 600 } }, vehicleTotals.paidTotal + ' 元')
)
),
React.createElement(Col, { span: 8 },
React.createElement('div', { style: { marginBottom: 8 } },
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '减免金额:'),
detailMode === 'payment'
? React.createElement(Input, {
value: paymentForm.discountAmount,
onChange: function(e) { handlePaymentFormChange('bill', -1, 'discountAmount', e.target.value); },
style: { width: 120 },
placeholder: '0.00',
addonAfter: '元'
})
: React.createElement('span', { style: { fontWeight: 600 } }, mockBillDetail.discountTotal + ' 元')
)
),
React.createElement(Col, { span: 8 },
React.createElement('div', { style: { marginBottom: 8 } },
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '未付款总额:'),
React.createElement('span', { style: { color: '#ff4d4f', fontWeight: 600 } }, vehicleTotals.unpaidTotal + ' 元')
)
),
React.createElement(Col, { span: 24 },
React.createElement('div', { style: { marginBottom: 8 } },
React.createElement('span', { style: { color: '#666', marginRight: 8 } }, '减免原因:'),
detailMode === 'payment'
? React.createElement(Input.TextArea, {
value: paymentForm.discountReason,
onChange: function(e) { handlePaymentFormChange('bill', -1, 'discountReason', e.target.value); },
placeholder: '请输入减免原因',
rows: 3,
style: { maxWidth: 400 }
})
: React.createElement('span', null, mockBillDetail.discountReason || '-')
)
)
)
),
// 车辆账单
React.createElement(Card, { title: '车辆账单', style: { marginBottom: 16 } },
React.createElement(Table, {
dataSource: mockVehicleList,
rowKey: 'plateNo',
pagination: false,
scroll: { x: 1200 },
columns: [
{ title: '品牌', dataIndex: 'brand', width: 80 },
{ title: '型号', dataIndex: 'model', width: 90 },
{ title: '车牌号', dataIndex: 'plateNo', width: 100 },
{ title: '计划交车日期', dataIndex: 'planDelivery', width: 120 },
{ title: '实际交车日期', dataIndex: 'actualDelivery', width: 120 },
{ title: '账单开始日期', dataIndex: 'billStart', width: 120 },
{ title: '计费结束日期', dataIndex: 'billEnd', width: 120 },
{ title: '车辆月租金', dataIndex: 'monthlyRent', width: 110 },
{ title: '实付月租金', dataIndex: 'paidMonthlyRent', width: 110, render: function(_, record, idx) {
if (detailMode !== 'payment') return (parseFloat(record.paidMonthlyRent || 0)).toFixed(2);
var pf = paymentForm.vehicleList[idx] || {};
return React.createElement(Input, { value: pf.paidMonthlyRent, onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidMonthlyRent', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
}},
{ title: '服务费', dataIndex: 'serviceFee', width: 90, render: function(val, record) {
if (detailMode === 'payment') return val;
return React.createElement('a', { onClick: function() { setPopover({ type: 'service', data: record.serviceItems }); } }, val);
}},
{ title: '实付服务费', dataIndex: 'paidServiceFee', width: 110, render: function(_, record, idx) {
if (detailMode !== 'payment') return (parseFloat(record.paidServiceFee || 0)).toFixed(2);
var pf = paymentForm.vehicleList[idx] || {};
return React.createElement(Input, { value: pf.paidServiceFee, onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidServiceFee', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
}},
{ title: '保证金', dataIndex: 'deposit', width: 90 },
{ title: '实付保证金', dataIndex: 'paidDeposit', width: 110, render: function(_, record, idx) {
if (detailMode !== 'payment') return (parseFloat(record.paidDeposit || 0)).toFixed(2);
var pf = paymentForm.vehicleList[idx] || {};
return React.createElement(Input, { value: pf.paidDeposit, onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidDeposit', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
}}
],
summary: function() {
return React.createElement(Table.Summary, null,
React.createElement(Table.Summary.Row, null,
React.createElement(Table.Summary.Cell, { index: 0, colSpan: 7 }, '总计'),
React.createElement(Table.Summary.Cell, { index: 7 }, vehicleBillTotals.monthlyRentTotal),
React.createElement(Table.Summary.Cell, { index: 8 }, vehicleBillTotals.paidRentTotal),
React.createElement(Table.Summary.Cell, { index: 9 }, vehicleBillTotals.serviceFeeTotal),
React.createElement(Table.Summary.Cell, { index: 10 }, vehicleBillTotals.paidServiceFeeTotal),
React.createElement(Table.Summary.Cell, { index: 11 }, vehicleBillTotals.depositTotal),
React.createElement(Table.Summary.Cell, { index: 12 }, vehicleBillTotals.paidDepositTotal)
)
);
}
})
),
// 氢费账单
React.createElement(Card, { title: '氢费账单', style: { marginBottom: 16 } },
React.createElement('div', { style: { marginBottom: 16 } },
React.createElement('span', { style: { marginRight: 24 } }, '加氢次数:', React.createElement('span', { style: { fontWeight: 600 } }, mockHydrogenData.refuelCount + ' 次')),
React.createElement('span', null, '当前氢费余额:', React.createElement('span', { style: { fontWeight: 600, color: '#1890ff' } }, mockHydrogenData.balance + ' 元'))
),
React.createElement(Table, {
dataSource: mockHydrogenData.list,
rowKey: function(item, i) { return i; },
pagination: false,
columns: [
{ title: '加氢时间', dataIndex: 'refuelTime', width: 180 },
{ title: '加氢站名称', dataIndex: 'stationName', width: 140 },
{ title: '车牌号', dataIndex: 'plateNo', width: 100 },
{ title: '加氢量', dataIndex: 'amount', width: 90 },
{ title: '成本单价(元/KG', dataIndex: 'costPrice', width: 120 },
{ title: '氢费价格(元)', dataIndex: 'feePrice', width: 120 },
{ title: '实付氢费金额(元)', dataIndex: 'paidFeePrice', width: 140, render: function(_, record, i) {
if (detailMode !== 'payment') return (parseFloat(record.paidFeePrice || 0)).toFixed(2);
var pf = paymentForm.hydrogenList[i] || {};
return React.createElement(Input, { value: pf.paidFeePrice, onChange: function(e) { handlePaymentFormChange('hydrogen', i, 'paidFeePrice', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
}}
],
summary: function() {
var totalFee = mockHydrogenData.list.reduce(function(s, item) { return s + parseFloat(item.feePrice || 0); }, 0).toFixed(2);
var totalPaid = mockHydrogenData.list.reduce(function(s, item) { return s + parseFloat(item.paidFeePrice || 0); }, 0).toFixed(2);
return React.createElement(Table.Summary, null,
React.createElement(Table.Summary.Row, null,
React.createElement(Table.Summary.Cell, { index: 0, colSpan: 5 }, '总计'),
React.createElement(Table.Summary.Cell, { index: 5 }, totalFee),
React.createElement(Table.Summary.Cell, { index: 6 }, totalPaid)
)
);
}
})
),
// 违章费用
React.createElement(Card, { title: '违章费用', style: { marginBottom: 16 } },
React.createElement('div', { style: { marginBottom: 16 } },
React.createElement('span', null, '违章次数:', React.createElement('span', { style: { fontWeight: 600 } }, mockViolationData.violationCount + ' 次'))
),
React.createElement(Table, {
dataSource: mockViolationData.list,
rowKey: function(item, i) { return i; },
pagination: false,
columns: [
{ title: '违章时间', dataIndex: 'violationTime', width: 180 },
{ title: '车牌号', dataIndex: 'plateNo', width: 100 },
{ title: '违章类型', dataIndex: 'violationType', width: 100 },
{ title: '违章地点', dataIndex: 'location', width: 200 },
{ title: '罚款金额', dataIndex: 'fineAmount', width: 100 },
{ title: '实付罚款金额(元)', dataIndex: 'paidFineAmount', width: 140, render: function(_, record, i) {
if (detailMode !== 'payment') return (parseFloat(record.paidFineAmount || 0)).toFixed(2);
var pf = paymentForm.violationList[i] || {};
return React.createElement(Input, { value: pf.paidFineAmount, onChange: function(e) { handlePaymentFormChange('violation', i, 'paidFineAmount', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
}}
],
summary: function() {
var totalPaid = mockViolationData.list.reduce(function(s, item) { return s + parseFloat(item.paidFineAmount || 0); }, 0).toFixed(2);
return React.createElement(Table.Summary, null,
React.createElement(Table.Summary.Row, null,
React.createElement(Table.Summary.Cell, { index: 0, colSpan: 4 }, '总计'),
React.createElement(Table.Summary.Cell, { index: 4 }, mockViolationData.totalAmount),
React.createElement(Table.Summary.Cell, { index: 5 }, totalPaid)
)
);
}
})
),
// 还车费用
React.createElement(Card, { title: '还车费用', style: { marginBottom: 16 } },
React.createElement(Table, {
dataSource: mockReturnFeeData.list,
rowKey: function(item, i) { return i; },
pagination: false,
columns: [
{ title: '费用名称', dataIndex: 'feeName', width: 140 },
{ title: '金额', dataIndex: 'amount', width: 100 },
{ title: '实付金额(元)', dataIndex: 'paidAmount', width: 120, render: function(_, record, i) {
if (detailMode !== 'payment') return (parseFloat(record.paidAmount || 0)).toFixed(2);
var pf = paymentForm.returnFeeList[i] || {};
return React.createElement(Input, { value: pf.paidAmount, onChange: function(e) { handlePaymentFormChange('returnFee', i, 'paidAmount', e.target.value); }, style: { width: 100 }, addonAfter: '元' });
}},
{ title: '照片', dataIndex: 'photos', width: 200, render: function(photos) {
if (!photos || photos.length === 0) return '-';
return React.createElement(Space, null, photos.slice(0, 5).map(function(url, idx) {
return React.createElement('img', {
key: idx,
src: url,
alt: '',
style: { width: 40, height: 40, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' },
onClick: function() { setPhotoViewer({ visible: true, photos: photos, currentIndex: idx }); }
});
}));
}},
{ title: '附件', dataIndex: 'attachments', render: function(attachments) {
if (!attachments || attachments.length === 0) return '-';
return React.createElement(Space, { wrap: true }, attachments.map(function(att, idx) {
return React.createElement('a', { key: idx, onClick: function() { message.info('下载附件:' + att.name); } }, att.name);
}));
}}
],
summary: function() {
var totalPaid = mockReturnFeeData.list.reduce(function(s, item) { return s + parseFloat(item.paidAmount || 0); }, 0).toFixed(2);
return React.createElement(Table.Summary, null,
React.createElement(Table.Summary.Row, null,
React.createElement(Table.Summary.Cell, { index: 0 }, '总计'),
React.createElement(Table.Summary.Cell, { index: 1 }, mockReturnFeeData.totalAmount),
React.createElement(Table.Summary.Cell, { index: 2 }, totalPaid),
React.createElement(Table.Summary.Cell, { index: 3 }, ''),
React.createElement(Table.Summary.Cell, { index: 4 }, '')
)
);
}
})
),
React.createElement('div', { style: { marginTop: 24, textAlign: 'center' } },
React.createElement(Space, null,
detailMode === 'payment' && React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交审核'),
detailMode === 'payment' && React.createElement(Button, { onClick: handleSave }, '保存'),
detailMode === 'payment' && React.createElement(Button, { onClick: handleBackToList }, '取消'),
detailMode === 'view' && React.createElement(Button, { onClick: handleBackToList }, '返回')
)
),
// 照片查看器 Modal
React.createElement(Modal, {
title: '照片查看',
open: photoViewer.visible && photoViewer.photos.length > 0,
onCancel: function() { setPhotoViewer({ visible: false, photos: [], currentIndex: 0 }); },
footer: null,
width: '90vw',
centered: true
}, photoViewer.photos.length > 0 ? React.createElement('div', { style: { textAlign: 'center' } },
React.createElement('img', {
src: photoViewer.photos[photoViewer.currentIndex].replace('80/80', '600/600'),
alt: '',
style: { maxWidth: '100%', maxHeight: '70vh', objectFit: 'contain' }
}),
React.createElement('div', { style: { marginTop: 16 } },
React.createElement(Button, {
disabled: photoViewer.currentIndex <= 0,
onClick: function() { setPhotoViewer({ visible: true, photos: photoViewer.photos, currentIndex: photoViewer.currentIndex - 1 }); }
}, '上一张'),
React.createElement('span', { style: { margin: '0 16px' } }, (photoViewer.currentIndex + 1) + ' / ' + photoViewer.photos.length),
React.createElement(Button, {
disabled: photoViewer.currentIndex >= photoViewer.photos.length - 1,
onClick: function() { setPhotoViewer({ visible: true, photos: photoViewer.photos, currentIndex: photoViewer.currentIndex + 1 }); }
}, '下一张')
)
) : null),
// 服务费明细 Modal
popover.type === 'service' && React.createElement(Modal, {
title: '服务费明细',
open: true,
onCancel: function() { setPopover({ type: null, data: null }); },
footer: React.createElement(Button, { onClick: function() { setPopover({ type: null, data: null }); } }, '关闭')
}, React.createElement(Table, {
dataSource: popover.data,
rowKey: function(item, i) { return i; },
pagination: false,
columns: [
{ title: '服务项', dataIndex: 'name' },
{ title: '价格', dataIndex: 'price' },
{ title: '服务生效日期', dataIndex: 'effectiveDate' }
]
}))
);
}
// —————— 列表视图 ——————
var listBreadcrumbItems = [
{ title: '运维管理' },
{ title: '业务管理' },
{ title: '租赁账单' }
];
return React.createElement('div', { style: { padding: 24, background: '#f5f5f5', minHeight: '100vh' } },
React.createElement(Breadcrumb, { items: listBreadcrumbItems, style: { marginBottom: 16 } }),
React.createElement(Card, null,
React.createElement(Row, { gutter: [16, 16], style: { marginBottom: 16 }, align: 'middle' },
React.createElement(Col, null,
React.createElement('span', { style: { marginRight: 8 } }, '合同编码:'),
React.createElement(Select, {
placeholder: '请选择合同编码',
allowClear: true,
showSearch: true,
optionFilterProp: 'label',
value: contractFilter,
onChange: setContractFilter,
style: { width: 200 },
options: contractOptions
})
),
React.createElement(Col, null,
React.createElement('span', { style: { marginRight: 8 } }, '项目名称:'),
React.createElement(Select, {
placeholder: '请选择项目名称',
allowClear: true,
showSearch: true,
optionFilterProp: 'label',
value: projectFilter,
onChange: setProjectFilter,
style: { width: 200 },
options: projectOptions
})
),
React.createElement(Col, null,
React.createElement('span', { style: { marginRight: 8 } }, '客户名称:'),
React.createElement(Select, {
placeholder: '请选择客户名称',
allowClear: true,
showSearch: true,
optionFilterProp: 'label',
value: customerFilter,
onChange: setCustomerFilter,
style: { width: 200 },
options: customerOptions
})
),
React.createElement(Col, null,
React.createElement('span', { style: { marginRight: 8 } }, '付款状态:'),
React.createElement(Select, {
mode: 'multiple',
placeholder: '全部',
allowClear: true,
value: statusFilter,
onChange: setStatusFilter,
style: { width: 200 },
options: statusOptions,
maxTagCount: 'responsive'
})
),
React.createElement(Col, null,
React.createElement(Space, null,
React.createElement(Button, { type: 'primary', onClick: handleSearch }, '查询'),
React.createElement(Button, { onClick: handleReset }, '重置')
)
)
),
React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'flex-end' } },
React.createElement(Button, { type: 'primary', onClick: handleExport }, '导出')
),
React.createElement(Table, {
rowSelection: rowSelection,
columns: listColumns,
dataSource: paginatedList,
rowKey: 'id',
pagination: tablePagination,
scroll: { x: 1600 },
size: 'middle'
})
),
// 应付金额明细 Modal列表按内容一行显示、宽度随内容调整
React.createElement(Modal, {
title: '应付金额明细',
open: popover.type === 'payable',
onCancel: function() { setPopover({ type: null, data: null }); },
footer: React.createElement(Button, { onClick: function() { setPopover({ type: null, data: null }); } }, '关闭'),
width: 'fit-content',
style: { maxWidth: '90vw' },
styles: { body: { paddingBottom: 24 } }
}, popover.type === 'payable' && popover.data ? React.createElement(Table, {
dataSource: popover.data,
rowKey: function(item, i) { return i; },
pagination: false,
tableLayout: 'auto',
style: { minWidth: 0 },
columns: [
{ title: '账单开始日期', dataIndex: 'startDate', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } },
{ title: '账单结束日期', dataIndex: 'endDate', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } },
{ title: '车牌号', dataIndex: 'plateNo', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } },
{ title: '车辆租金', dataIndex: 'rent', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } },
{ title: '服务费', dataIndex: 'serviceFee', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } },
{ title: '保证金', dataIndex: 'deposit', onCell: function() { return { style: { whiteSpace: 'nowrap' } }; } }
]
}) : null)
);
};
if (typeof window !== 'undefined') {
window.Component = Component;
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
var rootEl = document.getElementById('root');
if (rootEl && window.ReactDOM && window.React) {
var root = ReactDOM.createRoot(rootEl);
root.render(React.createElement(Component));
}
});
} else {
var rootEl = document.getElementById('root');
if (rootEl && window.ReactDOM && window.React) {
var root = ReactDOM.createRoot(rootEl);
root.render(React.createElement(Component));
}
}
}