1049 lines
60 KiB
JavaScript
1049 lines
60 KiB
JavaScript
// 【重要】必须使用 const Component 作为组件变量名
|
||
// 提车首付款 - 车辆资产管理后台(运维管理-财务管理-提车首付款)
|
||
|
||
const Component = function() {
|
||
var useState = React.useState;
|
||
var useCallback = React.useCallback;
|
||
var useMemo = React.useMemo;
|
||
|
||
// 分页
|
||
var pageState = useState(1);
|
||
var currentPage = pageState[0];
|
||
var setCurrentPage = pageState[1];
|
||
var pageSizeState = useState(10);
|
||
var pageSize = pageSizeState[0];
|
||
var setPageSize = pageSizeState[1];
|
||
|
||
// 筛选:表单输入值(用户填写)
|
||
var contractInputState = useState('');
|
||
var contractInput = contractInputState[0];
|
||
var setContractInput = contractInputState[1];
|
||
var projectInputState = useState('');
|
||
var projectInput = projectInputState[0];
|
||
var setProjectInput = projectInputState[1];
|
||
var customerInputState = useState('');
|
||
var customerInput = customerInputState[0];
|
||
var setCustomerInput = customerInputState[1];
|
||
var statusInputState = useState([]);
|
||
var statusInput = statusInputState[0];
|
||
var setStatusInput = statusInputState[1];
|
||
// 可搜索选择器下拉展开:contract | project | customer | null
|
||
var filterDropdownOpenState = useState(null);
|
||
var filterDropdownOpen = filterDropdownOpenState[0];
|
||
var setFilterDropdownOpen = filterDropdownOpenState[1];
|
||
// 付款状态多选下拉展开
|
||
var statusDropdownOpenState = useState(false);
|
||
var statusDropdownOpen = statusDropdownOpenState[0];
|
||
var setStatusDropdownOpen = statusDropdownOpenState[1];
|
||
// 筛选:已生效条件(点击查询后与列表联动)
|
||
var appliedFilterState = useState({
|
||
contract: '',
|
||
project: '',
|
||
customer: '',
|
||
status: []
|
||
});
|
||
var appliedFilter = appliedFilterState[0];
|
||
var setAppliedFilter = appliedFilterState[1];
|
||
|
||
// 查看页:当前视图 list | detail
|
||
var currentViewState = useState('list');
|
||
var currentView = currentViewState[0];
|
||
var setCurrentView = currentViewState[1];
|
||
// 详情模式:view 查看 | payment 付款
|
||
var detailModeState = useState('view');
|
||
var detailMode = detailModeState[0];
|
||
var setDetailMode = detailModeState[1];
|
||
// 付款表单(付款模式可编辑)
|
||
var paymentFormState = useState({
|
||
discountAmount: '',
|
||
discountReason: '',
|
||
hydrogenPaidAmount: '',
|
||
vehicleList: [],
|
||
hydrogenList: [],
|
||
violationList: [],
|
||
returnFeeList: []
|
||
});
|
||
var paymentForm = paymentFormState[0];
|
||
var setPaymentForm = paymentFormState[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];
|
||
// 当前查看/付款的账单 id
|
||
var viewingBillIdState = useState(null);
|
||
var viewingBillId = viewingBillIdState[0];
|
||
var setViewingBillId = viewingBillIdState[1];
|
||
// 审核状态覆盖(id -> draft|pending|auditing|completed|withdrawn|rejected)待提交|审核中|审核完成|撤回|审核驳回
|
||
var approvalStatusMapState = useState({});
|
||
var approvalStatusMap = approvalStatusMapState[0];
|
||
var setApprovalStatusMap = approvalStatusMapState[1];
|
||
// 撤回二次确认:待撤回的 row id
|
||
var withdrawConfirmIdState = useState(null);
|
||
var withdrawConfirmId = withdrawConfirmIdState[0];
|
||
var setWithdrawConfirmId = withdrawConfirmIdState[1];
|
||
// 查看需求明细弹窗
|
||
var requirementDetailVisibleState = useState(false);
|
||
var requirementDetailVisible = requirementDetailVisibleState[0];
|
||
var setRequirementDetailVisible = requirementDetailVisibleState[1];
|
||
|
||
// 需求明细文案(提车首付款)
|
||
var requirementDetailContent = '提车首付款\n\n1.面包屑:\n1.1.运维管理-财务管理-提车首付款\n\n2.筛选:\n2.1.合同编码:合同编码显示租赁合同编码;选择器,支持从输入框进行内容模糊搜索;\n2.2.项目名称:项目名称显示租赁合同对应项目名称;选择器,支持从输入框输入内容模糊搜索;\n2.3.客户名称:客户名称显示租赁合同对应客户名称,选择器,支持从输入框输入内容模糊搜索;\n2.4.付款状态:显示合同提车首付款付款状态,选择器,支持多选;选项为全部、部分付款、未付款、已付款;\n2.5.右侧为查询和重置按钮,筛选条件以且的模式进行筛选,点击重置清空条件;\n\n3.列表:\n3.1.列表下方为分页器,支持选择单页数据条数;\n3.2.列表字段按照以下排序:付款状态、合同编码、项目名称、客户名称、合同生效日期、首期应付金额、实付金额、减免金额、未付金额、备注、操作;\n3.3.审核状态:分为待提交、审核中、审核完成、审核驳回、撤回;\n 流程流转为:待提交(交车任务生效/完成收费补充保存) → 完成收费补充提交 → 审核中(可撤回,撤回后显示为撤回)→ 审核完成(最终节点审核完成) / 审核驳回(驳回显示为审核驳回);\n a.待提交:交车单交车成功时系统会自动生成提车首付款,状态为「待提交」;业务人员点击收费补充信息点击保存,状态也为「待提交」,同时操作可:查看、收费;\n b.审核中:提车首付款完成收费信息填写,点击提交审核,状态变更为「审核中」,同时操作可:查看、撤回;\n c.审核完成:提车首付款完成审核后,显示为审核完成,同时操作可:查看;\n d.审核驳回:提车首付款在任意审核节点被驳回时,显示为审核驳回,同时操作可:查看、收费;\n e.撤回:提车首付款在审核中时,发起人可进行撤回,撤回后可重新补充收费信息并再次提交;操作可:查看、收费;\n3.4.付款状态:显示付款状态,状态分为:已付款、部分付款、未付款;\n a.已付款:应付金额为0时,显示为已付款;\n b.部分付款:未付金额>0且<应付金额时,显示为部分付款;\n c.未付款:实付金额为0时,显示为未付款;\n3.5.合同编码:显示租赁合同-合同编码;\n3.6.项目名称:显示该项目租赁合同-项目名称;\n3.7.客户名称:显示该项目租赁合同-客户名称;\n3.8.合同生效日期:显示该项目租赁合同-生效日期,格式为YYYY-MM-DD;\n3.9.首期应付金额:显示该笔账单首期应付金额,精确至2位小数,计算方式为:应付款总额=车辆月租金总计+服务费总计+保证金总计+氢气预付款金额;\n3.10.实付金额:显示该笔账单实付金额,精确至2位小数,如未付则显示为0;如已上传付款信息,则显示金额,计算方式为:实付款金额=实付月租金总计+实付服务费总计+实付保证金总计+氢气实付款金额\n3.11.减免金额:显示该笔账单减免金额,精确至2位小数,如无则显示为0;\n3.12.未付金额:显示该笔账单未付金额,精确至2位小数,如无则显示为0;未付款金额=应付款总额-实付金额-减免金额\n3.13.减免原因:显示减免金额添加原因;\n3.14.操作:查看、收费、撤回;\n a.查看:点击查看进入查看页;\n b.收费:点击收费,录入收费信息;\n c.撤回:审核状态为审核中时,点击撤回,进行二次提示,确认后该条数据审核状态显示为撤回,可重新进行收费操作;';
|
||
|
||
// 模拟数据:合同编码、项目、客户选项(用于选择器展示,可选)
|
||
var contractOptions = useMemo(function() {
|
||
return [
|
||
{ value: 'HT-ZL-2025-001', label: 'HT-ZL-2025-001' },
|
||
{ value: 'HT-ZL-2025-002', label: 'HT-ZL-2025-002' },
|
||
{ value: 'HT-ZL-2025-003', label: 'HT-ZL-2025-003' },
|
||
{ value: 'HT-ZL-2024-088', label: 'HT-ZL-2024-088' },
|
||
{ value: 'HT-ZL-2024-099', label: 'HT-ZL-2024-099' }
|
||
];
|
||
}, []);
|
||
var projectOptions = useMemo(function() {
|
||
return [
|
||
{ value: '北京朝阳区租赁项目', label: '北京朝阳区租赁项目' },
|
||
{ value: '上海浦东车辆租赁', label: '上海浦东车辆租赁' },
|
||
{ value: '广州天河运营项目', label: '广州天河运营项目' },
|
||
{ value: '深圳南山首期项目', label: '深圳南山首期项目' }
|
||
];
|
||
}, []);
|
||
var customerOptions = useMemo(function() {
|
||
return [
|
||
{ value: '某某科技有限公司', label: '某某科技有限公司' },
|
||
{ value: '某某物流有限公司', label: '某某物流有限公司' },
|
||
{ value: '某某制造有限公司', label: '某某制造有限公司' },
|
||
{ value: '某某出行服务公司', label: '某某出行服务公司' }
|
||
];
|
||
}, []);
|
||
|
||
// 模拟列表数据
|
||
var mockList = useMemo(function() {
|
||
var list = [];
|
||
var statuses = ['paid', 'partial', 'unpaid'];
|
||
var approvalStatuses = ['draft', 'pending', 'auditing', 'completed', 'withdrawn', 'rejected'];
|
||
var contracts = ['HT-ZL-2025-001', 'HT-ZL-2025-002', 'HT-ZL-2025-003', 'HT-ZL-2024-088', 'HT-ZL-2024-099'];
|
||
var projects = ['北京朝阳区租赁项目', '上海浦东车辆租赁', '广州天河运营项目', '深圳南山首期项目'];
|
||
var customers = ['某某科技有限公司', '某某物流有限公司', '某某制造有限公司', '某某出行服务公司'];
|
||
for (var i = 1; i <= 28; i++) {
|
||
var status = statuses[i % 3];
|
||
var approvalStatus = approvalStatuses[i % 6];
|
||
var firstPayable = 15000 + i * 800;
|
||
var paid = 0;
|
||
var discount = 0;
|
||
if (status === 'paid') {
|
||
paid = firstPayable;
|
||
discount = i % 4 === 0 ? 200 : 0;
|
||
} else if (status === 'partial') {
|
||
paid = Math.floor(firstPayable * 0.5);
|
||
discount = i % 5 === 0 ? 100 : 0;
|
||
}
|
||
var unpaid = Math.max(0, firstPayable - paid - discount);
|
||
var remark = status !== 'unpaid' ? '已上传付款凭证,备注编号' + i : '-';
|
||
var discountReason = discount > 0 ? (i % 4 === 0 ? '长期合作客户优惠' : '首期减免') : '-';
|
||
list.push({
|
||
id: 'FP' + i,
|
||
paymentStatus: status,
|
||
approvalStatus: approvalStatus,
|
||
contractCode: contracts[i % contracts.length],
|
||
projectName: projects[i % projects.length],
|
||
customerName: customers[i % customers.length],
|
||
contractEffectiveDate: '2025-' + (String((i % 12) + 1)).padStart(2, '0') + '-' + (String((i % 28) + 1)).padStart(2, '0'),
|
||
firstPayableAmount: firstPayable,
|
||
paidAmount: paid,
|
||
discountAmount: discount,
|
||
unpaidAmount: unpaid,
|
||
discountReason: discountReason,
|
||
remark: remark
|
||
});
|
||
}
|
||
return list;
|
||
}, []);
|
||
|
||
// 查看页 mock 数据(按 查看页面需求说明 结构)
|
||
var mockBillDetail = useMemo(function() {
|
||
return {
|
||
startDate: '2025-01-01',
|
||
endDate: '2025-01-31',
|
||
contractCode: 'HT-ZL-2025-001',
|
||
projectName: '北京朝阳区租赁项目',
|
||
customerName: '某某科技有限公司',
|
||
department: '运营部',
|
||
responsible: '张三',
|
||
paymentCycle: '先付(付款周期:1个月)',
|
||
discountTotal: '200.00',
|
||
discountReason: '长期合作客户优惠,首期账单减免部分金额'
|
||
};
|
||
}, []);
|
||
var mockVehicleList = useMemo(function() {
|
||
return [
|
||
{ brand: '奔驰', model: 'E300L', plateNo: '京A12345', planDelivery: '2024-12-25', planDeliveryEnd: '2024-12-31', 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', planDeliveryEnd: '2024-12-26', 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 mockHydrogenData = useMemo(function() {
|
||
return {
|
||
paymentMethod: '预付',
|
||
prepaymentAmount: '3580.00',
|
||
paidAmount: '3200.00'
|
||
};
|
||
}, []);
|
||
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 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 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 vehicleTotals = useMemo(function() {
|
||
var monthlyRentTotal = parseFloat(vehicleBillTotals.monthlyRentTotal || 0);
|
||
var serviceFeeTotal = parseFloat(vehicleBillTotals.serviceFeeTotal || 0);
|
||
var depositTotal = parseFloat(vehicleBillTotals.depositTotal || 0);
|
||
var hydrogenTotal = parseFloat(mockHydrogenData.prepaymentAmount || 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 = payableTotal - paidTotal - discountTotal;
|
||
if (unpaidTotal < 0) unpaidTotal = 0;
|
||
return {
|
||
payableTotal: payableTotal.toFixed(2),
|
||
paidTotal: paidTotal.toFixed(2),
|
||
unpaidTotal: unpaidTotal.toFixed(2)
|
||
};
|
||
}, [vehicleBillTotals, mockHydrogenData, mockViolationData, mockReturnFeeData, mockVehicleList, mockBillDetail]);
|
||
|
||
// 筛选后的列表(使用已生效条件,点击查询后才与选择器联动)
|
||
var filteredList = useMemo(function() {
|
||
var list = mockList;
|
||
var af = appliedFilter;
|
||
// 合同编码选择器:与列表 contractCode 精确匹配
|
||
if (af.contract && af.contract.trim()) {
|
||
var cf = af.contract.trim();
|
||
list = list.filter(function(item) {
|
||
return (item.contractCode || '') === cf;
|
||
});
|
||
}
|
||
// 项目名称选择器:与列表 projectName 精确匹配
|
||
if (af.project && af.project.trim()) {
|
||
var pf = af.project.trim();
|
||
list = list.filter(function(item) {
|
||
return (item.projectName || '') === pf;
|
||
});
|
||
}
|
||
// 客户名称选择器:与列表 customerName 精确匹配
|
||
if (af.customer && af.customer.trim()) {
|
||
var cuf = af.customer.trim();
|
||
list = list.filter(function(item) {
|
||
return (item.customerName || '') === cuf;
|
||
});
|
||
}
|
||
if (af.status && af.status.length > 0) {
|
||
list = list.filter(function(item) {
|
||
return af.status.indexOf(item.paymentStatus) >= 0;
|
||
});
|
||
}
|
||
return list;
|
||
}, [mockList, appliedFilter]);
|
||
|
||
var totalCount = filteredList.length;
|
||
var totalPages = Math.ceil(totalCount / pageSize) || 1;
|
||
var paginatedList = useMemo(function() {
|
||
var start = (currentPage - 1) * pageSize;
|
||
return filteredList.slice(start, start + pageSize);
|
||
}, [filteredList, currentPage, pageSize]);
|
||
|
||
var getStatusText = function(status) {
|
||
if (status === 'paid') return '已付款';
|
||
if (status === 'partial') return '部分付款';
|
||
return '未付款';
|
||
};
|
||
var getStatusColor = function(status) {
|
||
if (status === 'paid') return '#52c41a';
|
||
if (status === 'partial') return '#faad14';
|
||
return '#ff4d4f';
|
||
};
|
||
|
||
var formatAmount = function(num) {
|
||
var n = typeof num === 'number' ? num : parseFloat(num);
|
||
if (isNaN(n)) return '0.00';
|
||
return n.toFixed(2);
|
||
};
|
||
|
||
var handleQuery = useCallback(function() {
|
||
setAppliedFilter({
|
||
contract: contractInput,
|
||
project: projectInput,
|
||
customer: customerInput,
|
||
status: statusInput
|
||
});
|
||
setCurrentPage(1);
|
||
}, [contractInput, projectInput, customerInput, statusInput]);
|
||
var handleReset = useCallback(function() {
|
||
setContractInput('');
|
||
setProjectInput('');
|
||
setCustomerInput('');
|
||
setStatusInput([]);
|
||
setAppliedFilter({ contract: '', project: '', customer: '', status: [] });
|
||
setCurrentPage(1);
|
||
}, []);
|
||
var getApprovalStatus = useCallback(function(row) {
|
||
return approvalStatusMap[row.id] != null ? approvalStatusMap[row.id] : row.approvalStatus;
|
||
}, [approvalStatusMap]);
|
||
var getApprovalStatusText = function(s) {
|
||
if (s === 'draft') return '待提交';
|
||
if (s === 'pending' || s === 'auditing') return '审核中';
|
||
if (s === 'completed') return '审核完成';
|
||
if (s === 'withdrawn') return '撤回';
|
||
if (s === 'rejected') return '审核驳回';
|
||
return s || '-';
|
||
};
|
||
var handleView = useCallback(function(id) {
|
||
setViewingBillId(id);
|
||
setDetailMode('view');
|
||
setCurrentView('detail');
|
||
}, []);
|
||
var handlePayment = useCallback(function(id) {
|
||
setViewingBillId(id);
|
||
setDetailMode('payment');
|
||
var vl = mockVehicleList.map(function(v) {
|
||
return { paidMonthlyRent: v.paidMonthlyRent || '', paidServiceFee: v.paidServiceFee || '', paidDeposit: v.paidDeposit || '' };
|
||
});
|
||
var hl = [];
|
||
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 || '',
|
||
hydrogenPaidAmount: mockHydrogenData.paidAmount || '',
|
||
vehicleList: vl,
|
||
hydrogenList: hl,
|
||
violationList: viol,
|
||
returnFeeList: rfl
|
||
});
|
||
setCurrentView('detail');
|
||
}, []);
|
||
var handleBackToList = useCallback(function() {
|
||
setCurrentView('list');
|
||
setDetailMode('view');
|
||
setViewingBillId(null);
|
||
}, []);
|
||
var handleSave = useCallback(function() {
|
||
if (viewingBillId) {
|
||
setApprovalStatusMap(function(prev) {
|
||
var next = {};
|
||
for (var k in prev) { if (prev.hasOwnProperty(k)) next[k] = prev[k]; }
|
||
next[viewingBillId] = 'draft';
|
||
return next;
|
||
});
|
||
}
|
||
alert('保存成功');
|
||
handleBackToList();
|
||
}, [viewingBillId, handleBackToList]);
|
||
var handleWithdraw = useCallback(function(rowId) {
|
||
setWithdrawConfirmId(rowId);
|
||
}, []);
|
||
var handleWithdrawConfirm = useCallback(function() {
|
||
if (withdrawConfirmId) {
|
||
setApprovalStatusMap(function(prev) {
|
||
var next = {};
|
||
for (var k in prev) { if (prev.hasOwnProperty(k)) next[k] = prev[k]; }
|
||
next[withdrawConfirmId] = 'draft';
|
||
return next;
|
||
});
|
||
setWithdrawConfirmId(null);
|
||
alert('已撤回,可重新编辑后提交');
|
||
}
|
||
}, [withdrawConfirmId]);
|
||
var handlePaymentFormChange = useCallback(function(section, index, field, value) {
|
||
setPaymentForm(function(prev) {
|
||
var next = {
|
||
discountAmount: prev.discountAmount,
|
||
discountReason: prev.discountReason,
|
||
hydrogenPaidAmount: prev.hydrogenPaidAmount,
|
||
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;
|
||
if (field === 'hydrogenPaidAmount') next.hydrogenPaidAmount = value;
|
||
} else if (section === 'vehicle' && index >= 0 && index < next.vehicleList.length) {
|
||
next.vehicleList[index] = Object.assign({}, next.vehicleList[index]);
|
||
next.vehicleList[index][field] = value;
|
||
} else if (section === 'hydrogen' && index >= 0 && index < next.hydrogenList.length) {
|
||
next.hydrogenList[index] = Object.assign({}, next.hydrogenList[index]);
|
||
next.hydrogenList[index][field] = value;
|
||
} else if (section === 'violation' && index >= 0 && index < next.violationList.length) {
|
||
next.violationList[index] = Object.assign({}, next.violationList[index]);
|
||
next.violationList[index][field] = value;
|
||
} else if (section === 'returnFee' && index >= 0 && index < next.returnFeeList.length) {
|
||
next.returnFeeList[index] = Object.assign({}, next.returnFeeList[index]);
|
||
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('减免原因');
|
||
if (!paymentForm.hydrogenPaidAmount || String(paymentForm.hydrogenPaidAmount).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.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) {
|
||
alert('请填写必填项:' + errors.join('、'));
|
||
return;
|
||
}
|
||
if (viewingBillId) {
|
||
setApprovalStatusMap(function(prev) {
|
||
var next = {};
|
||
for (var k in prev) { if (prev.hasOwnProperty(k)) next[k] = prev[k]; }
|
||
next[viewingBillId] = 'pending';
|
||
return next;
|
||
});
|
||
}
|
||
alert('提交成功');
|
||
handleBackToList();
|
||
}, [paymentForm, handleBackToList, viewingBillId]);
|
||
|
||
// 样式
|
||
var styles = {
|
||
page: {
|
||
padding: '24px',
|
||
backgroundColor: '#f5f5f5',
|
||
minHeight: '100vh',
|
||
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
|
||
},
|
||
breadcrumb: {
|
||
marginBottom: '16px',
|
||
fontSize: '14px',
|
||
color: '#666'
|
||
},
|
||
breadcrumbSep: { color: '#bfbfbf', margin: '0 8px' },
|
||
content: {
|
||
backgroundColor: '#fff',
|
||
borderRadius: '8px',
|
||
padding: '24px',
|
||
boxShadow: '0 1px 2px rgba(0,0,0,0.03)',
|
||
overflowX: 'auto'
|
||
},
|
||
filterRow: {
|
||
display: 'flex',
|
||
flexWrap: 'nowrap',
|
||
gap: '16px',
|
||
marginBottom: '20px',
|
||
alignItems: 'center',
|
||
overflowX: 'auto'
|
||
},
|
||
filterItem: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: '8px'
|
||
},
|
||
label: { fontSize: '14px', color: '#333', minWidth: '80px' },
|
||
input: {
|
||
padding: '8px 12px',
|
||
border: '1px solid #d9d9d9',
|
||
borderRadius: '4px',
|
||
fontSize: '14px',
|
||
width: '200px'
|
||
},
|
||
select: {
|
||
padding: '8px 12px',
|
||
border: '1px solid #d9d9d9',
|
||
borderRadius: '4px',
|
||
fontSize: '14px',
|
||
width: '200px'
|
||
},
|
||
filterSelectWrap: { position: 'relative', width: '200px' },
|
||
filterDropdown: {
|
||
position: 'absolute',
|
||
top: '100%',
|
||
left: 0,
|
||
right: 0,
|
||
marginTop: '2px',
|
||
maxHeight: '200px',
|
||
overflowY: 'auto',
|
||
backgroundColor: '#fff',
|
||
border: '1px solid #d9d9d9',
|
||
borderRadius: '4px',
|
||
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
||
zIndex: 10
|
||
},
|
||
filterDropdownItem: {
|
||
padding: '8px 12px',
|
||
fontSize: '14px',
|
||
cursor: 'pointer',
|
||
borderBottom: '1px solid #f0f0f0'
|
||
},
|
||
selectMultiple: {
|
||
padding: '8px 12px',
|
||
border: '1px solid #d9d9d9',
|
||
borderRadius: '4px',
|
||
fontSize: '14px',
|
||
minWidth: '200px',
|
||
minHeight: '36px'
|
||
},
|
||
statusMultiWrap: { position: 'relative', display: 'inline-block' },
|
||
statusMultiDisplay: {
|
||
padding: '8px 12px',
|
||
border: '1px solid #d9d9d9',
|
||
borderRadius: '4px',
|
||
fontSize: '14px',
|
||
width: '200px',
|
||
backgroundColor: '#fff',
|
||
cursor: 'pointer',
|
||
minHeight: '36px',
|
||
boxSizing: 'border-box'
|
||
},
|
||
statusDropdownPanel: {
|
||
position: 'absolute',
|
||
left: 0,
|
||
top: '100%',
|
||
marginTop: '2px',
|
||
width: '100%',
|
||
maxHeight: '220px',
|
||
overflowY: 'auto',
|
||
backgroundColor: '#fff',
|
||
border: '1px solid #d9d9d9',
|
||
borderRadius: '4px',
|
||
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
||
zIndex: 10
|
||
},
|
||
statusDropdownOption: {
|
||
padding: '8px 12px',
|
||
fontSize: '14px',
|
||
cursor: 'pointer',
|
||
borderBottom: '1px solid #f0f0f0'
|
||
},
|
||
table: { width: '100%', borderCollapse: 'collapse', fontSize: '14px' },
|
||
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',
|
||
whiteSpace: 'nowrap'
|
||
},
|
||
actionColTh: {
|
||
position: 'sticky',
|
||
right: 0,
|
||
backgroundColor: '#fafafa',
|
||
boxShadow: '-2px 0 4px rgba(0,0,0,0.06)',
|
||
zIndex: 1
|
||
},
|
||
actionColTd: {
|
||
position: 'sticky',
|
||
right: 0,
|
||
backgroundColor: '#fff',
|
||
boxShadow: '-2px 0 4px rgba(0,0,0,0.06)',
|
||
zIndex: 1
|
||
},
|
||
actionBtn: {
|
||
padding: '4px 12px',
|
||
marginRight: '8px',
|
||
borderRadius: '4px',
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
fontSize: '13px'
|
||
},
|
||
viewBtn: { backgroundColor: '#e6f7ff', color: '#1890ff' },
|
||
payBtn: { backgroundColor: '#f6ffed', color: '#52c41a' },
|
||
withdrawBtn: { backgroundColor: '#fff7e6', color: '#fa8c16' },
|
||
pagination: {
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
marginTop: '20px',
|
||
flexWrap: 'wrap',
|
||
gap: '12px'
|
||
},
|
||
pageSizeSelect: {
|
||
padding: '6px 10px',
|
||
border: '1px solid #d9d9d9',
|
||
borderRadius: '4px',
|
||
fontSize: '13px'
|
||
},
|
||
pageInfo: { fontSize: '14px', color: '#666' },
|
||
pageHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '16px', flexWrap: 'wrap', gap: '12px' },
|
||
requirementLink: { color: '#1890ff', cursor: 'pointer', fontSize: '14px', textDecoration: 'none' },
|
||
requirementModalBody: { maxHeight: '70vh', overflowY: 'auto', whiteSpace: 'pre-wrap', fontSize: '14px', lineHeight: 1.6, color: '#333', padding: '0 4px' },
|
||
queryBtn: {
|
||
padding: '8px 20px',
|
||
backgroundColor: '#1890ff',
|
||
color: '#fff',
|
||
border: 'none',
|
||
borderRadius: '4px',
|
||
cursor: 'pointer',
|
||
fontSize: '14px'
|
||
},
|
||
resetBtn: {
|
||
padding: '8px 20px',
|
||
backgroundColor: '#fff',
|
||
color: '#666',
|
||
border: '1px solid #d9d9d9',
|
||
borderRadius: '4px',
|
||
cursor: 'pointer',
|
||
fontSize: '14px'
|
||
},
|
||
// 查看页样式(按 查看页面需求说明)
|
||
detailPage: { padding: '24px', backgroundColor: '#f5f5f5', minHeight: '100vh', width: '100%', boxSizing: 'border-box' },
|
||
backBtn: { marginBottom: '16px', padding: '8px 16px', backgroundColor: '#fff', border: '1px solid #d9d9d9', borderRadius: '4px', cursor: 'pointer', fontSize: '14px' },
|
||
detailContent: { display: 'flex', flexDirection: 'column', gap: '24px', width: '100%' },
|
||
detailCard: { backgroundColor: '#fff', borderRadius: '8px', padding: '24px', boxShadow: '0 1px 2px rgba(0,0,0,0.03)', width: '100%', boxSizing: 'border-box' },
|
||
detailCardTitle: { fontSize: '16px', fontWeight: 600, marginBottom: '16px', paddingBottom: '12px', borderBottom: '1px solid #f0f0f0' },
|
||
detailRow: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '24px', marginBottom: '12px', fontSize: '14px' },
|
||
detailItem: {},
|
||
detailItemInline: { display: 'flex', alignItems: 'center' },
|
||
detailItemInputRight: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' },
|
||
detailLabel: { color: '#666', marginRight: '8px' },
|
||
amountInputWrap: { display: 'flex', alignItems: 'center', gap: '8px' },
|
||
amountInput: { padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: '4px', fontSize: '14px', width: '120px' },
|
||
amountSuffix: { color: '#666', fontSize: '14px' },
|
||
detailTextarea: { padding: '8px 12px', border: '1px solid #d9d9d9', borderRadius: '4px', fontSize: '14px', width: '100%', minWidth: '200px', minHeight: '60px', resize: 'vertical' },
|
||
vehicleTable: { width: '100%', borderCollapse: 'collapse', fontSize: '14px' },
|
||
hydrogenSummary: { display: 'flex', gap: '48px', marginBottom: '16px', fontSize: '14px' },
|
||
hydrogenSummaryItem: { color: '#333' },
|
||
photoThumb: { width: '40px', height: '40px', objectFit: 'cover', borderRadius: '4px', cursor: 'pointer', marginRight: '4px', verticalAlign: 'middle' },
|
||
attachmentLink: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none', marginRight: '8px', fontSize: '14px' },
|
||
amountLink: { color: '#1890ff', cursor: 'pointer', textDecoration: 'none' },
|
||
photoViewerOverlay: { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.85)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1001 },
|
||
photoViewerContent: { position: 'relative', textAlign: 'center' },
|
||
photoViewerImg: { maxWidth: '90vw', maxHeight: '85vh', objectFit: 'contain' },
|
||
photoViewerBtn: { position: 'absolute', top: '50%', marginTop: '-20px', padding: '10px 16px', backgroundColor: 'rgba(255,255,255,0.9)', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '14px' },
|
||
photoViewerPrev: { left: '20px' },
|
||
photoViewerNext: { right: '20px' },
|
||
photoViewerClose: { position: 'absolute', top: '-40px', right: '0', padding: '8px 16px', backgroundColor: 'rgba(255,255,255,0.9)', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '14px' },
|
||
photoViewerCounter: { color: '#fff', marginTop: '16px', fontSize: '14px' },
|
||
popover: { 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 },
|
||
popoverCard: { backgroundColor: '#fff', borderRadius: '8px', padding: '24px', maxWidth: '90%', maxHeight: '80vh', overflow: 'auto' },
|
||
popoverHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '16px', whiteSpace: 'nowrap' },
|
||
popoverTitle: { fontSize: '16px', fontWeight: 600, whiteSpace: 'nowrap' },
|
||
popoverClose: { padding: '4px 12px', backgroundColor: '#f5f5f5', border: 'none', borderRadius: '4px', cursor: 'pointer' },
|
||
popoverTable: { width: '100%', borderCollapse: 'collapse', fontSize: '14px' },
|
||
popoverTh: { textAlign: 'left', padding: '12px 16px', backgroundColor: '#fafafa', borderBottom: '1px solid #f0f0f0', fontWeight: 600, color: '#333', whiteSpace: 'nowrap' },
|
||
popoverTd: { padding: '12px 16px', borderBottom: '1px solid #f0f0f0', color: '#333', whiteSpace: 'nowrap' },
|
||
modalFooter: { marginTop: '24px', textAlign: 'right', display: 'flex', justifyContent: 'center', gap: '12px' }
|
||
};
|
||
|
||
// 查看页:按 查看页面需求说明 渲染(租赁账单详情)
|
||
if (currentView === 'detail') {
|
||
return (
|
||
React.createElement('div', { style: styles.detailPage },
|
||
React.createElement('div', { style: Object.assign({}, styles.breadcrumb, { marginBottom: '16px' }) },
|
||
React.createElement('span', null, '财务管理'),
|
||
React.createElement('span', { style: styles.breadcrumbSep }, '>'),
|
||
React.createElement('span', null, '提车首付款'),
|
||
React.createElement('span', { style: styles.breadcrumbSep }, '>'),
|
||
React.createElement('span', null, detailMode === 'payment' ? '收费' : '查看')
|
||
),
|
||
React.createElement('div', { style: styles.detailContent },
|
||
React.createElement('div', { style: styles.detailCard },
|
||
React.createElement('div', { style: styles.detailCardTitle }, '账单信息'),
|
||
React.createElement('div', { style: styles.detailRow },
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '账单开始日期:'), React.createElement('span', null, mockBillDetail.startDate)),
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '账单结束日期:'), React.createElement('span', null, mockBillDetail.endDate))
|
||
),
|
||
React.createElement('div', { style: styles.detailRow },
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '合同编码:'), React.createElement('span', null, mockBillDetail.contractCode)),
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '项目名称:'), React.createElement('span', null, mockBillDetail.projectName)),
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '客户名称:'), React.createElement('span', null, mockBillDetail.customerName))
|
||
),
|
||
React.createElement('div', { style: styles.detailRow },
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '业务部门:'), React.createElement('span', null, mockBillDetail.department)),
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '业务负责人:'), React.createElement('span', null, mockBillDetail.responsible)),
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '付款周期:'), React.createElement('span', null, mockBillDetail.paymentCycle))
|
||
),
|
||
React.createElement('div', { style: styles.detailRow },
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '首期应付金额:'), React.createElement('span', { style: { color: '#1890ff', fontWeight: 600 } }, vehicleTotals.payableTotal + ' 元')),
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '实付款总额:'), React.createElement('span', { style: { color: '#52c41a', fontWeight: 600 } }, vehicleTotals.paidTotal + ' 元')),
|
||
React.createElement('div', { style: Object.assign({}, styles.detailItem, styles.detailItemInline) }, React.createElement('span', { style: styles.detailLabel }, '减免金额:'), detailMode === 'payment' ? React.createElement('div', { style: styles.amountInputWrap }, React.createElement('input', { type: 'text', style: styles.amountInput, value: paymentForm.discountAmount, onChange: function(e) { handlePaymentFormChange('bill', -1, 'discountAmount', e.target.value); }, placeholder: '0.00' }), React.createElement('span', { style: styles.amountSuffix }, '元')) : React.createElement('span', { style: { fontWeight: 600 } }, mockBillDetail.discountTotal + ' 元')),
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '未付款总额:'), React.createElement('span', { style: { color: '#ff4d4f', fontWeight: 600 } }, vehicleTotals.unpaidTotal + ' 元'))
|
||
),
|
||
React.createElement('div', { style: styles.detailRow },
|
||
React.createElement('div', { style: Object.assign({}, styles.detailItem, { gridColumn: '1 / -1' }) }, React.createElement('span', { style: styles.detailLabel }, '减免原因:'), detailMode === 'payment' ? React.createElement('textarea', { style: styles.detailTextarea, value: paymentForm.discountReason, onChange: function(e) { handlePaymentFormChange('bill', -1, 'discountReason', e.target.value); }, placeholder: '请输入减免原因' }) : React.createElement('span', null, mockBillDetail.discountReason || '-'))
|
||
)
|
||
),
|
||
React.createElement('div', { style: styles.detailCard },
|
||
React.createElement('div', { style: styles.detailCardTitle }, '车辆账单'),
|
||
React.createElement('table', { style: styles.vehicleTable },
|
||
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 }, '服务费'), React.createElement('th', { style: styles.th }, '实付服务费'), React.createElement('th', { style: styles.th }, '保证金'), React.createElement('th', { style: styles.th }, '实付保证金')
|
||
)),
|
||
React.createElement('tbody', null, mockVehicleList.map(function(v, idx) {
|
||
var pf = paymentForm.vehicleList[idx] || {};
|
||
return React.createElement('tr', { key: v.plateNo },
|
||
React.createElement('td', { style: styles.td }, v.brand), React.createElement('td', { style: styles.td }, v.model), React.createElement('td', { style: styles.td }, v.plateNo),
|
||
React.createElement('td', { style: styles.td }, (v.planDelivery && v.planDeliveryEnd) ? (v.planDelivery + '至' + v.planDeliveryEnd) : (v.planDelivery || '未设置')), React.createElement('td', { style: styles.td }, v.actualDelivery || '未交车'), React.createElement('td', { style: styles.td }, v.billStart || '未设置'), React.createElement('td', { style: styles.td }, v.billEnd || '未设置'),
|
||
React.createElement('td', { style: styles.td }, v.monthlyRent),
|
||
React.createElement('td', { style: styles.td }, detailMode === 'payment' ? React.createElement('div', { style: styles.amountInputWrap }, React.createElement('input', { type: 'text', style: styles.amountInput, value: pf.paidMonthlyRent || '', onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidMonthlyRent', e.target.value); }, placeholder: '0.00' }), React.createElement('span', { style: styles.amountSuffix }, '元')) : (parseFloat(v.paidMonthlyRent || 0)).toFixed(2)),
|
||
React.createElement('td', { style: styles.td }, detailMode === 'payment' ? v.serviceFee : React.createElement('span', { style: styles.amountLink, onClick: function() { setPopover({ type: 'service', data: v.serviceItems }); } }, v.serviceFee)),
|
||
React.createElement('td', { style: styles.td }, detailMode === 'payment' ? React.createElement('div', { style: styles.amountInputWrap }, React.createElement('input', { type: 'text', style: styles.amountInput, value: pf.paidServiceFee || '', onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidServiceFee', e.target.value); }, placeholder: '0.00' }), React.createElement('span', { style: styles.amountSuffix }, '元')) : (parseFloat(v.paidServiceFee || 0)).toFixed(2)),
|
||
React.createElement('td', { style: styles.td }, v.deposit),
|
||
React.createElement('td', { style: styles.td }, detailMode === 'payment' ? React.createElement('div', { style: styles.amountInputWrap }, React.createElement('input', { type: 'text', style: styles.amountInput, value: pf.paidDeposit || '', onChange: function(e) { handlePaymentFormChange('vehicle', idx, 'paidDeposit', e.target.value); }, placeholder: '0.00' }), React.createElement('span', { style: styles.amountSuffix }, '元')) : (parseFloat(v.paidDeposit || 0)).toFixed(2))
|
||
);
|
||
})),
|
||
React.createElement('tfoot', null, React.createElement('tr', { style: { backgroundColor: '#fafafa', fontWeight: 600 } }, React.createElement('td', { style: styles.td, colSpan: 7 }, '总计'), React.createElement('td', { style: styles.td }, vehicleBillTotals.monthlyRentTotal), React.createElement('td', { style: styles.td }, vehicleBillTotals.paidRentTotal), React.createElement('td', { style: styles.td }, vehicleBillTotals.serviceFeeTotal), React.createElement('td', { style: styles.td }, vehicleBillTotals.paidServiceFeeTotal), React.createElement('td', { style: styles.td }, vehicleBillTotals.depositTotal), React.createElement('td', { style: styles.td }, vehicleBillTotals.paidDepositTotal)))
|
||
)
|
||
),
|
||
React.createElement('div', { style: styles.detailCard },
|
||
React.createElement('div', { style: styles.detailCardTitle }, '氢费账单'),
|
||
React.createElement('div', { style: styles.detailRow },
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '氢费付款方式:'), React.createElement('span', null, mockHydrogenData.paymentMethod || '-')),
|
||
React.createElement('div', { style: styles.detailItem }, React.createElement('span', { style: styles.detailLabel }, '氢气预付款:'), React.createElement('span', null, (parseFloat(mockHydrogenData.prepaymentAmount || 0)).toFixed(2) + ' 元')),
|
||
React.createElement('div', { style: Object.assign({}, styles.detailItem, styles.detailItemInline) }, React.createElement('span', { style: styles.detailLabel }, '氢气实付款金额:'), detailMode === 'payment' ? React.createElement('div', { style: styles.amountInputWrap }, React.createElement('input', { type: 'text', style: styles.amountInput, value: paymentForm.hydrogenPaidAmount, onChange: function(e) { handlePaymentFormChange('bill', -1, 'hydrogenPaidAmount', e.target.value); }, placeholder: '0.00' }), React.createElement('span', { style: styles.amountSuffix }, '元')) : React.createElement('span', null, (parseFloat(mockHydrogenData.paidAmount || 0)).toFixed(2) + ' 元'))
|
||
)
|
||
)
|
||
),
|
||
React.createElement('div', { style: { marginTop: '24px', textAlign: 'center', display: 'flex', justifyContent: 'center', gap: '16px' } },
|
||
detailMode === 'payment' && React.createElement('button', { type: 'button', style: Object.assign({}, styles.backBtn, { backgroundColor: '#1890ff', color: '#fff', border: 'none' }), onClick: handleSubmit }, '提交审核'),
|
||
detailMode === 'payment' && React.createElement('button', { type: 'button', style: styles.backBtn, onClick: handleSave }, '保存'),
|
||
React.createElement('button', { type: 'button', style: styles.backBtn, onClick: handleBackToList }, '取消')
|
||
),
|
||
photoViewer.visible && photoViewer.photos.length > 0 && React.createElement('div', { style: styles.photoViewerOverlay, onClick: function() { setPhotoViewer({ visible: false, photos: [], currentIndex: 0 }); } },
|
||
React.createElement('div', { style: styles.photoViewerContent, onClick: function(e) { e.stopPropagation(); } },
|
||
React.createElement('button', { style: Object.assign({}, styles.photoViewerBtn, styles.photoViewerClose), onClick: function() { setPhotoViewer({ visible: false, photos: [], currentIndex: 0 }); } }, '关闭'),
|
||
photoViewer.currentIndex > 0 ? React.createElement('button', { style: Object.assign({}, styles.photoViewerBtn, styles.photoViewerPrev), onClick: function() { setPhotoViewer({ visible: true, photos: photoViewer.photos, currentIndex: photoViewer.currentIndex - 1 }); } }, '上一张') : null,
|
||
React.createElement('img', { style: styles.photoViewerImg, src: photoViewer.photos[photoViewer.currentIndex].replace('80/80', '600/600'), alt: '' }),
|
||
photoViewer.currentIndex < photoViewer.photos.length - 1 ? React.createElement('button', { style: Object.assign({}, styles.photoViewerBtn, styles.photoViewerNext), onClick: function() { setPhotoViewer({ visible: true, photos: photoViewer.photos, currentIndex: photoViewer.currentIndex + 1 }); } }, '下一张') : null,
|
||
React.createElement('div', { style: styles.photoViewerCounter }, (photoViewer.currentIndex + 1) + ' / ' + photoViewer.photos.length)
|
||
)
|
||
),
|
||
(popover.type === 'service' && React.createElement('div', { style: styles.popover, onClick: function() { setPopover({ type: null, data: null }); } },
|
||
React.createElement('div', { style: styles.popoverCard, onClick: function(e) { e.stopPropagation(); } },
|
||
React.createElement('div', { style: styles.popoverHeader }, React.createElement('span', { style: styles.popoverTitle }, '服务费明细'), React.createElement('button', { style: styles.popoverClose, onClick: function() { setPopover({ type: null, data: 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, popover.data ? popover.data.map(function(s, i) { return React.createElement('tr', { key: i }, React.createElement('td', { style: styles.td }, s.name), React.createElement('td', { style: styles.td }, s.price), React.createElement('td', { style: styles.td }, s.effectiveDate)); }) : null))
|
||
)
|
||
))
|
||
)
|
||
);
|
||
}
|
||
|
||
|
||
return (
|
||
React.createElement('div', { style: styles.page },
|
||
React.createElement('div', { style: styles.pageHeader },
|
||
React.createElement('div', { style: styles.breadcrumb },
|
||
React.createElement('span', null, '财务管理'),
|
||
React.createElement('span', { style: styles.breadcrumbSep }, '>'),
|
||
React.createElement('span', null, '提车首付款')
|
||
),
|
||
React.createElement('a', { style: styles.requirementLink, onClick: function() { setRequirementDetailVisible(true); }, role: 'button', tabIndex: 0, onKeyDown: function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setRequirementDetailVisible(true); } } }, '查看需求明细')
|
||
),
|
||
React.createElement('div', { style: styles.content },
|
||
// 筛选:合同编码、项目名称、客户名称(选择器+输入搜索)、付款状态(多选);右侧 查询、重置
|
||
React.createElement('div', { style: styles.filterRow },
|
||
React.createElement('div', { style: styles.filterItem },
|
||
React.createElement('span', { style: styles.label }, '合同编码:'),
|
||
React.createElement('div', { style: styles.filterSelectWrap },
|
||
React.createElement('input', {
|
||
style: styles.input,
|
||
placeholder: '请输入合同编码',
|
||
value: contractInput,
|
||
onChange: function(e) { setContractInput(e.target.value); },
|
||
onFocus: function() { setFilterDropdownOpen('contract'); },
|
||
onBlur: function() { setTimeout(function() { setFilterDropdownOpen(null); }, 200); }
|
||
}),
|
||
filterDropdownOpen === 'contract' && React.createElement('div', { style: styles.filterDropdown },
|
||
contractOptions.filter(function(opt) {
|
||
var kw = (contractInput || '').toLowerCase();
|
||
if (!kw) return true;
|
||
return (opt.label || '').toLowerCase().indexOf(kw) >= 0 || (opt.value || '').toLowerCase().indexOf(kw) >= 0;
|
||
}).map(function(opt) {
|
||
return React.createElement('div', {
|
||
key: opt.value,
|
||
style: styles.filterDropdownItem,
|
||
onMouseDown: function(e) { e.preventDefault(); setContractInput(opt.value); setFilterDropdownOpen(null); }
|
||
}, opt.label);
|
||
})
|
||
)
|
||
)
|
||
),
|
||
React.createElement('div', { style: styles.filterItem },
|
||
React.createElement('span', { style: styles.label }, '项目名称:'),
|
||
React.createElement('div', { style: styles.filterSelectWrap },
|
||
React.createElement('input', {
|
||
style: styles.input,
|
||
placeholder: '请输入项目名称',
|
||
value: projectInput,
|
||
onChange: function(e) { setProjectInput(e.target.value); },
|
||
onFocus: function() { setFilterDropdownOpen('project'); },
|
||
onBlur: function() { setTimeout(function() { setFilterDropdownOpen(null); }, 200); }
|
||
}),
|
||
filterDropdownOpen === 'project' && React.createElement('div', { style: styles.filterDropdown },
|
||
projectOptions.filter(function(opt) {
|
||
var kw = (projectInput || '').toLowerCase();
|
||
if (!kw) return true;
|
||
return (opt.label || '').toLowerCase().indexOf(kw) >= 0 || (opt.value || '').toLowerCase().indexOf(kw) >= 0;
|
||
}).map(function(opt) {
|
||
return React.createElement('div', {
|
||
key: opt.value,
|
||
style: styles.filterDropdownItem,
|
||
onMouseDown: function(e) { e.preventDefault(); setProjectInput(opt.value); setFilterDropdownOpen(null); }
|
||
}, opt.label);
|
||
})
|
||
)
|
||
)
|
||
),
|
||
React.createElement('div', { style: styles.filterItem },
|
||
React.createElement('span', { style: styles.label }, '客户名称:'),
|
||
React.createElement('div', { style: styles.filterSelectWrap },
|
||
React.createElement('input', {
|
||
style: styles.input,
|
||
placeholder: '请输入客户名称',
|
||
value: customerInput,
|
||
onChange: function(e) { setCustomerInput(e.target.value); },
|
||
onFocus: function() { setFilterDropdownOpen('customer'); },
|
||
onBlur: function() { setTimeout(function() { setFilterDropdownOpen(null); }, 200); }
|
||
}),
|
||
filterDropdownOpen === 'customer' && React.createElement('div', { style: styles.filterDropdown },
|
||
customerOptions.filter(function(opt) {
|
||
var kw = (customerInput || '').toLowerCase();
|
||
if (!kw) return true;
|
||
return (opt.label || '').toLowerCase().indexOf(kw) >= 0 || (opt.value || '').toLowerCase().indexOf(kw) >= 0;
|
||
}).map(function(opt) {
|
||
return React.createElement('div', {
|
||
key: opt.value,
|
||
style: styles.filterDropdownItem,
|
||
onMouseDown: function(e) { e.preventDefault(); setCustomerInput(opt.value); setFilterDropdownOpen(null); }
|
||
}, opt.label);
|
||
})
|
||
)
|
||
)
|
||
),
|
||
React.createElement('div', { style: styles.filterItem },
|
||
React.createElement('span', { style: styles.label }, '付款状态:'),
|
||
React.createElement('div', {
|
||
style: styles.statusMultiWrap,
|
||
tabIndex: 0,
|
||
onBlur: function() { setTimeout(function() { setStatusDropdownOpen(false); }, 200); }
|
||
},
|
||
React.createElement('div', {
|
||
style: styles.statusMultiDisplay,
|
||
onClick: function() { setStatusDropdownOpen(!statusDropdownOpen); }
|
||
}, statusInput.length === 0 ? '全部' : statusInput.map(function(s) { return getStatusText(s); }).join('、')),
|
||
statusDropdownOpen ? React.createElement('div', { style: styles.statusDropdownPanel },
|
||
['partial', 'unpaid', 'paid'].map(function(s) {
|
||
var checked = statusInput.indexOf(s) >= 0;
|
||
return React.createElement('div', {
|
||
key: s,
|
||
style: styles.statusDropdownOption,
|
||
onMouseDown: function(e) {
|
||
e.preventDefault();
|
||
var idx = statusInput.indexOf(s);
|
||
var next = statusInput.slice();
|
||
if (idx >= 0) next.splice(idx, 1);
|
||
else next.push(s);
|
||
setStatusInput(next);
|
||
}
|
||
}, React.createElement('label', { style: { cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '6px' } },
|
||
React.createElement('input', { type: 'checkbox', checked: checked, readOnly: true }),
|
||
getStatusText(s)
|
||
));
|
||
})
|
||
) : null
|
||
)
|
||
),
|
||
React.createElement('div', { style: { display: 'flex', gap: '8px', marginLeft: 'auto' } },
|
||
React.createElement('button', { type: 'button', style: styles.queryBtn, onClick: handleQuery }, '查询'),
|
||
React.createElement('button', { type: 'button', style: styles.resetBtn, onClick: handleReset }, '重置')
|
||
)
|
||
),
|
||
// 列表:审核状态、付款状态、合同编码、项目名称、客户名称、合同生效日期、首期应付金额、实付款总额、减免金额、未付款总额、减免原因、操作(固定右侧)
|
||
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('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: Object.assign({}, styles.th, styles.actionColTh) }, '操作')
|
||
)
|
||
),
|
||
React.createElement('tbody', null,
|
||
paginatedList.map(function(row) {
|
||
var approvalStatus = getApprovalStatus(row);
|
||
var showPay = approvalStatus === 'draft' || approvalStatus === 'withdrawn' || approvalStatus === 'rejected';
|
||
var showWithdraw = approvalStatus === 'auditing' || approvalStatus === 'pending';
|
||
return React.createElement('tr', { key: row.id },
|
||
React.createElement('td', { style: styles.td }, getApprovalStatusText(approvalStatus)),
|
||
React.createElement('td', { style: styles.td },
|
||
React.createElement('span', { style: { color: getStatusColor(row.paymentStatus) } }, getStatusText(row.paymentStatus))
|
||
),
|
||
React.createElement('td', { style: styles.td }, row.contractCode),
|
||
React.createElement('td', { style: styles.td }, row.projectName),
|
||
React.createElement('td', { style: styles.td }, row.customerName),
|
||
React.createElement('td', { style: styles.td }, row.contractEffectiveDate),
|
||
React.createElement('td', { style: styles.td }, formatAmount(row.firstPayableAmount)),
|
||
React.createElement('td', { style: styles.td }, formatAmount(row.paidAmount)),
|
||
React.createElement('td', { style: styles.td }, formatAmount(row.discountAmount)),
|
||
React.createElement('td', { style: styles.td }, formatAmount(row.unpaidAmount)),
|
||
React.createElement('td', { style: styles.td }, row.discountReason || '-'),
|
||
React.createElement('td', { style: Object.assign({}, styles.td, styles.actionColTd) },
|
||
React.createElement('button', {
|
||
type: 'button',
|
||
style: Object.assign({}, styles.actionBtn, styles.viewBtn),
|
||
onClick: function() { handleView(row.id); }
|
||
}, '查看'),
|
||
showPay && React.createElement('button', {
|
||
type: 'button',
|
||
style: Object.assign({}, styles.actionBtn, styles.payBtn),
|
||
onClick: function() { handlePayment(row.id); }
|
||
}, '收费'),
|
||
showWithdraw && React.createElement('button', {
|
||
type: 'button',
|
||
style: Object.assign({}, styles.actionBtn, styles.withdrawBtn),
|
||
onClick: function() { handleWithdraw(row.id); }
|
||
}, '撤回')
|
||
)
|
||
);
|
||
})
|
||
)
|
||
),
|
||
// 分页:支持选择单页数据条数
|
||
React.createElement('div', { style: styles.pagination },
|
||
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
|
||
React.createElement('span', { style: styles.pageInfo }, '共 ' + totalCount + ' 条'),
|
||
React.createElement('select', {
|
||
style: styles.pageSizeSelect,
|
||
value: pageSize,
|
||
onChange: function(e) {
|
||
setPageSize(Number(e.target.value));
|
||
setCurrentPage(1);
|
||
}
|
||
},
|
||
React.createElement('option', { value: 10 }, '10 条/页'),
|
||
React.createElement('option', { value: 20 }, '20 条/页'),
|
||
React.createElement('option', { value: 50 }, '50 条/页')
|
||
)
|
||
),
|
||
React.createElement('div', { style: { display: 'flex', gap: '8px', alignItems: 'center' } },
|
||
React.createElement('button', {
|
||
type: 'button',
|
||
style: Object.assign({}, styles.actionBtn, { padding: '6px 12px' }),
|
||
disabled: currentPage <= 1,
|
||
onClick: function() { setCurrentPage(currentPage - 1); }
|
||
}, '上一页'),
|
||
React.createElement('span', { style: styles.pageInfo }, currentPage + ' / ' + totalPages),
|
||
React.createElement('button', {
|
||
type: 'button',
|
||
style: Object.assign({}, styles.actionBtn, { padding: '6px 12px' }),
|
||
disabled: currentPage >= totalPages,
|
||
onClick: function() { setCurrentPage(currentPage + 1); }
|
||
}, '下一页')
|
||
)
|
||
),
|
||
withdrawConfirmId && React.createElement('div', { style: styles.popover, onClick: function() { setWithdrawConfirmId(null); } },
|
||
React.createElement('div', { style: styles.popoverCard, onClick: function(e) { e.stopPropagation(); } },
|
||
React.createElement('div', { style: styles.popoverTitle }, '确认撤回?'),
|
||
React.createElement('p', { style: { margin: '16px 0', fontSize: '14px', color: '#666' } }, '确认后将撤回该数据,审核状态变为撤回。'),
|
||
React.createElement('div', { style: styles.modalFooter },
|
||
React.createElement('button', { type: 'button', style: styles.backBtn, onClick: function() { setWithdrawConfirmId(null); } }, '取消'),
|
||
React.createElement('button', { type: 'button', style: Object.assign({}, styles.backBtn, { backgroundColor: '#fa8c16', color: '#fff', border: 'none' }), onClick: handleWithdrawConfirm }, '确认')
|
||
)
|
||
)
|
||
),
|
||
requirementDetailVisible && React.createElement('div', { style: styles.popover, onClick: function() { setRequirementDetailVisible(false); } },
|
||
React.createElement('div', { style: Object.assign({}, styles.popoverCard, { maxWidth: '720px', width: '90%' }), onClick: function(e) { e.stopPropagation(); } },
|
||
React.createElement('div', { style: styles.popoverHeader },
|
||
React.createElement('span', { style: styles.popoverTitle }, '需求明细'),
|
||
React.createElement('button', { style: styles.popoverClose, onClick: function() { setRequirementDetailVisible(false); } }, '关闭')
|
||
),
|
||
React.createElement('div', { style: styles.requirementModalBody }, requirementDetailContent)
|
||
)
|
||
)
|
||
)
|
||
)
|
||
);
|
||
};
|
||
|
||
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));
|
||
}
|
||
}
|
||
}
|