Files
ONE-OS/web端/财务管理/还车应结款-费用明细.jsx
王冕 92d3b97bca 运维/财务:完善交车单编辑/查看与还车应结款页面
- 交车单编辑页:布局对齐、检查单合并、照片必填与需求说明
- 新增交车单查看页:只读展示与样例数据
- 还车应结款相关页面与需求说明补齐

Made-with: Cursor
2026-03-18 22:10:05 +08:00

1335 lines
69 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 作为组件变量名
// 财务管理 - 还车应结款
const Component = function () {
var useState = React.useState;
var useMemo = React.useMemo;
var useCallback = React.useCallback;
var useEffect = React.useEffect;
var useRef = React.useRef;
var antd = window.antd;
var Breadcrumb = antd.Breadcrumb;
var Card = antd.Card;
var Table = antd.Table;
var Button = antd.Button;
var Input = antd.Input;
var Modal = antd.Modal;
var Tooltip = antd.Tooltip;
var Popover = antd.Popover;
var message = antd.message;
var TextArea = Input.TextArea;
function RequiredLabel(text) {
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 4 } },
React.createElement('span', { style: { color: '#f5222d', fontWeight: 600 } }, '*'),
React.createElement('span', null, text)
);
}
function formatDateTimeNow() {
var d = new Date();
var pad = function (n) { return n < 10 ? '0' + n : '' + n; };
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes());
}
function toFixed2(v) {
if (v === null || v === undefined || v === '') return '';
var n = typeof v === 'number' ? v : parseFloat(v);
return isNaN(n) ? '' : n.toFixed(2);
}
// 页面样式
var layoutStyle = { padding: '16px 24px 88px', background: '#f5f5f5', minHeight: '100vh' };
var cardStyle = { marginBottom: 16 };
var footerStyle = { position: 'fixed', left: 0, right: 0, bottom: 0, background: '#fff', borderTop: '1px solid #f0f0f0', padding: '12px 24px', display: 'flex', justifyContent: 'flex-start', gap: 12, zIndex: 10 };
// 还车车辆明细(单车示例)
var vehicleDetail = useMemo(function () {
return [{
key: 'v1',
plateNo: '粤AGP5621',
contractCode: 'LNZLHT20251106001',
projectName: '嘉兴腾4.5T租赁',
customerName: '嘉兴某某物流有限公司',
deliveryTime: '2026-02-01',
returnTime: '2026-02-27',
fragileInsurance: '是',
tireInsurance: '否',
maintenanceInsurance: '是'
}];
}, []);
var insuranceFlags = useMemo(function () {
var v = (vehicleDetail && vehicleDetail[0]) || {};
return {
hasFragileInsurance: String(v.fragileInsurance || '') === '是',
hasTireInsurance: String(v.tireInsurance || '') === '是',
hasMaintenanceInsurance: String(v.maintenanceInsurance || '') === '是'
};
}, [vehicleDetail]);
// 费用统计
var statsState = useState({
depositAmount: '5000.00',
pendingSettleAmount: '0.00',
shouldRefundAmount: '800.00',
shouldPayAmount: '400.00'
});
var stats = statsState[0];
// 通用折叠区块(展开/收起图标)
var CollapseSection = function (props) {
var collapsed = props.collapsed;
var setCollapsed = props.setCollapsed;
var title = props.title;
var extra = props.extra;
var headerActions = props.headerActions;
return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, marginBottom: 12, overflow: 'hidden' } },
React.createElement('div', {
style: { padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer', background: '#fff' },
onClick: function () { setCollapsed(!collapsed); }
},
React.createElement('div', { style: { fontWeight: 600, fontSize: 14 } }, title),
React.createElement('div', { style: { flex: 1 } }),
extra ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 12, color: '#666', fontSize: 13 } }, extra) : null,
headerActions ? React.createElement('div', { onClick: function (e) { e.stopPropagation(); }, style: { display: 'inline-flex', alignItems: 'center', gap: 8 } }, headerActions) : null,
collapsed
? React.createElement('svg', { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, style: { display: 'block', color: '#666' } }, React.createElement('path', { d: 'M6 9l6 6 6-6' }))
: React.createElement('svg', { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, style: { display: 'block', color: '#666' } }, React.createElement('path', { d: 'M18 15l-6-6-6 6' }))
),
collapsed ? null : React.createElement('div', { style: { padding: '12px 16px', borderTop: '1px solid #f0f0f0' } }, props.children)
);
};
// 业务服务组
var businessServiceFixedItems = ['违章处理违约金', '保险上浮', 'ETC-客户未缴费用', 'ETC卡缺损费', 'ETC设备缺损费'];
var businessServiceRowsState = useState(
businessServiceFixedItems.map(function (name, i) {
return { key: 'bs-' + i, seq: i + 1, feeItem: name, amount: '', remark: '', lastUpdateTime: '', photos: [], attachments: [], fixed: true };
})
);
var businessServiceRows = businessServiceRowsState[0];
var setBusinessServiceRows = businessServiceRowsState[1];
var businessServiceMetaState = useState({ submitBy: '', status: '待提交' });
var businessServiceMeta = businessServiceMetaState[0];
var setBusinessServiceMeta = businessServiceMetaState[1];
var businessServiceCollapsedState = useState(false);
function recalcBusinessServiceTotal(list) {
var sum = 0;
(list || []).forEach(function (r) { sum += (parseFloat(r.amount) || 0); });
return sum.toFixed(2);
}
var businessServiceTotalComputed = useMemo(function () {
return recalcBusinessServiceTotal(businessServiceRows || []);
}, [businessServiceRows]);
function calcVehicleRent(monthRent, billStartDateStr, returnDateStr) {
var m = parseFloat(monthRent) || 0;
if (!billStartDateStr || !returnDateStr) return '0.00';
var s = new Date(String(billStartDateStr).replace(/-/g, '/'));
var e = new Date(String(returnDateStr).replace(/-/g, '/'));
if (isNaN(s.getTime()) || isNaN(e.getTime())) return '0.00';
var msDay = 24 * 60 * 60 * 1000;
var days = Math.floor((e.getTime() - s.getTime()) / msDay);
if (days < 0) days = 0;
return ((m / 30) * days).toFixed(2);
}
var billInfoState = useState({
billStartDate: '2026-02-01',
vehicleMonthRent: '9000.00',
receivedRent: '0.00',
actualRent: '',
actualRentManual: false,
shouldRefundRent: '',
shouldRefundManual: false
});
var billInfo = billInfoState[0];
var setBillInfo = billInfoState[1];
useEffect(function () {
if (billInfo.actualRentManual && String(billInfo.actualRent || '').trim() !== '') return;
var returnDateStr = (vehicleDetail && vehicleDetail[0] && vehicleDetail[0].returnTime) || '';
var calc = calcVehicleRent(billInfo.vehicleMonthRent, billInfo.billStartDate, returnDateStr);
if (billInfo.actualRent !== calc) {
setBillInfo(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.actualRent = calc; return n; });
}
}, [billInfo.vehicleMonthRent, billInfo.billStartDate, billInfo.actualRentManual, vehicleDetail]);
useEffect(function () {
if (billInfo.shouldRefundManual && String(billInfo.shouldRefundRent || '').trim() !== '') return;
var received = parseFloat(billInfo.receivedRent) || 0;
var actual = parseFloat(billInfo.actualRent) || 0;
var calc = (received - actual).toFixed(2);
if (billInfo.shouldRefundRent !== calc) {
setBillInfo(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.shouldRefundRent = calc; return n; });
}
}, [billInfo.receivedRent, billInfo.actualRent, billInfo.shouldRefundManual]);
var addBusinessServiceRow = useCallback(function () {
setBusinessServiceRows(function (p) {
var next = p.slice();
var i = next.length + 1;
next.push({ key: 'bs-new-' + Date.now(), seq: i, feeItem: '', amount: '', remark: '', lastUpdateTime: '', photos: [], attachments: [], fixed: false });
return next.map(function (r, idx) { var o = {}; for (var k in r) o[k] = r[k]; o.seq = idx + 1; return o; });
});
}, []);
var removeBusinessServiceRow = useCallback(function (key) {
setBusinessServiceRows(function (p) {
var next = p.filter(function (r) { return r.key !== key; });
return next.map(function (r, idx) { var o = {}; for (var k in r) o[k] = r[k]; o.seq = idx + 1; return o; });
});
}, []);
var updateBusinessServiceRow = useCallback(function (key, field, value) {
setBusinessServiceRows(function (p) {
var next = p.map(function (r) {
if (r.key !== key) return r;
var n = {}; for (var k in r) n[k] = r[k];
n[field] = value;
return n;
});
return next;
});
}, []);
var handleBusinessServiceSave = useCallback(function () {
setBusinessServiceMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.status = '待提交'; return nm; });
message.success('已保存');
}, []);
var handleBusinessServiceSubmit = useCallback(function () {
Modal.confirm({
title: '提交确认',
content: '请确认业务服务组金额填写无误,点击确认完成提交。',
okText: '确认',
cancelText: '取消',
onOk: function () {
setBusinessServiceRows(function (p) {
var now = formatDateTimeNow();
return p.map(function (r) { var n = {}; for (var k in r) n[k] = r[k]; n.lastUpdateTime = now; return n; });
});
setBusinessServiceMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.status = '已提交'; nm.submitBy = '业务服务组-张三'; return nm; });
message.success('已提交');
}
});
}, []);
var handleBusinessServiceRevoke = useCallback(function () {
Modal.confirm({
title: '撤回确认',
content: '是否确认撤回?',
okText: '确认',
cancelText: '取消',
onOk: function () {
setBusinessServiceMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.status = '待提交'; return nm; });
message.success('已撤回,可重新编辑并保存/提交');
}
});
}, []);
// 能源采购组
var energyState = useState({
deliveryHydrogen: '85.00',
returnHydrogen: '72.00',
hydrogenUnitPrice: '35.00',
hydrogenSupplement: '455.00',
hydrogenFee: '',
electricFee: '',
prepayRefund: '',
userBalance: '1200.00'
});
var energy = energyState[0];
var setEnergy = energyState[1];
var energyMetaState = useState({ submitBy: '', status: '待提交' });
var energyMeta = energyMetaState[0];
var setEnergyMeta = energyMetaState[1];
var energyCollapsedState = useState(false);
function recalcEnergyTotal(e) {
var a = parseFloat((e && e.hydrogenSupplement) || '') || 0;
var b = parseFloat((e && e.hydrogenFee) || '') || 0;
var c = parseFloat((e && e.electricFee) || '') || 0;
return (a + b + c).toFixed(2);
}
var energyTotalComputed = useMemo(function () { return recalcEnergyTotal(energy); }, [energy.hydrogenSupplement, energy.hydrogenFee, energy.electricFee]);
useEffect(function () {
var d = parseFloat(energy.deliveryHydrogen) || 0;
var r = parseFloat(energy.returnHydrogen) || 0;
var u = parseFloat(energy.hydrogenUnitPrice) || 0;
if (d > r) {
var calc = ((d - r) * u).toFixed(2);
if (energy.hydrogenSupplement !== calc) {
setEnergy(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.hydrogenSupplement = calc; return n; });
}
}
}, [energy.deliveryHydrogen, energy.returnHydrogen, energy.hydrogenUnitPrice]);
// energy 总金额使用 energyTotalComputed 派生,避免输入时额外 setState 造成丢焦
var handleEnergySave = useCallback(function () {
setEnergyMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.status = '待提交'; return nm; });
message.success('能源采购组已保存');
}, [energy]);
var handleEnergySubmit = useCallback(function () {
Modal.confirm({
title: '提交确认',
content: '请确认能源采购组金额填写无误,点击确认完成提交。',
okText: '确认',
cancelText: '取消',
onOk: function () {
setEnergyMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.submitBy = '能源采购组-李四'; nm.status = '已提交'; return nm; });
message.success('能源采购组已提交');
}
});
}, [energy]);
var handleEnergyRevoke = useCallback(function () {
Modal.confirm({
title: '撤回确认',
content: '是否确认撤回?',
okText: '确认',
cancelText: '取消',
onOk: function () {
setEnergyMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.status = '待提交'; return nm; });
message.success('能源采购组已撤回,可重新编辑并保存/提交');
}
});
}, []);
// 运维部
var operationFixedItems = ['清洗费', '未结算保养', '未结算维修', '车损费用', '工具损坏丢失费用', '证件丢失费用', '广告损坏丢失费用', '送车服务费', '接车服务费', '轮胎磨损费用'];
var operationRowsState = useState(
operationFixedItems.map(function (name, i) {
return { key: 'op-' + i, seq: i + 1, feeItem: name, amount: '', worryFreeDiscount: '', remark: '', lastUpdateTime: '', photos: [], attachments: [], fixed: true };
})
);
var operationRows = operationRowsState[0];
var setOperationRows = operationRowsState[1];
var operationMetaState = useState({ submitBy: '', status: '待提交' });
var operationMeta = operationMetaState[0];
var setOperationMeta = operationMetaState[1];
var operationCollapsedState = useState(false);
function recalcOperationTotal(list) {
var sum = 0;
(list || []).forEach(function (r) { sum += (parseFloat(r.amount) || 0); });
return sum.toFixed(2);
}
var operationTotalComputed = useMemo(function () {
return recalcOperationTotal(operationRows || []);
}, [operationRows]);
// 轮胎胎纹明细(原型 mock10个轮胎 + 1个备胎共11条
var tireTreadList = useMemo(function () {
var unit = 25; // 元/mm
var rows = [
{ key: 't1', name: '左前轮', deliveryDepth: '6.8', returnDepth: '6.2' },
{ key: 't2', name: '左后轮(内)', deliveryDepth: '6.7', returnDepth: '6.0' },
{ key: 't3', name: '左后轮(外)', deliveryDepth: '6.6', returnDepth: '6.1' },
{ key: 't4', name: '右前轮', deliveryDepth: '6.9', returnDepth: '6.4' },
{ key: 't5', name: '右后轮(内)', deliveryDepth: '6.5', returnDepth: '6.0' },
{ key: 't6', name: '右后轮(外)', deliveryDepth: '6.8', returnDepth: '6.3' },
{ key: 't7', name: '左中轮', deliveryDepth: '6.7', returnDepth: '6.2' },
{ key: 't8', name: '右中轮', deliveryDepth: '6.6', returnDepth: '6.1' },
{ key: 't9', name: '左后备位轮', deliveryDepth: '6.9', returnDepth: '6.5' },
{ key: 't10', name: '右后备位轮', deliveryDepth: '6.4', returnDepth: '6.0' },
{ key: 'spare', name: '备胎', deliveryDepth: '7.0', returnDepth: '7.0' }
];
var deliverySum = 0;
var returnSum = 0;
rows.forEach(function (r) { deliverySum += (parseFloat(r.deliveryDepth) || 0); returnSum += (parseFloat(r.returnDepth) || 0); });
var shouldCalc = returnSum <= deliverySum;
return rows.map(function (r) {
var d = parseFloat(r.deliveryDepth) || 0;
var rr = parseFloat(r.returnDepth) || 0;
var diff = shouldCalc ? Math.max(0, d - rr) : 0;
var total = shouldCalc ? (diff * unit) : 0;
return {
key: r.key,
name: r.name,
deliveryDepth: d.toFixed(2),
returnDepth: rr.toFixed(2),
diff: diff.toFixed(2),
unitPrice: unit.toFixed(2),
totalAmount: total.toFixed(2)
};
});
}, []);
var tireWearTotal = useMemo(function () {
var sum = 0;
(tireTreadList || []).forEach(function (r) { sum += (parseFloat(r.totalAmount) || 0); });
return sum.toFixed(2);
}, [tireTreadList]);
// 运维部自动反写/计算(原型 mock
var operationAuto = useMemo(function () {
// 证件丢失费用行驶证200、牌照300、营运证300、加氢证300
var missingDocs = ['行驶证', '牌照']; // mock交车有还车无
var map = { '行驶证': 200, '牌照': 300, '营运证': 300, '加氢证': 300 };
var docFee = missingDocs.reduce(function (s, k) { return s + (map[k] || 0); }, 0);
// 送车/接车服务费 mock
var deliveryFee = 0;
var receiveFee = 0;
// 轮胎磨损费用:按胎纹明细汇总
return { docLossFee: docFee.toFixed(2), deliveryServiceFee: deliveryFee.toFixed(2), receiveServiceFee: receiveFee.toFixed(2), tireWearFee: String(tireWearTotal || '0.00') };
}, [tireWearTotal]);
useEffect(function () {
setOperationRows(function (p) {
var next = p.map(function (r) {
var n = {}; for (var k in r) n[k] = r[k];
if (n.feeItem === '证件丢失费用') n.amount = operationAuto.docLossFee;
if (n.feeItem === '送车服务费') n.amount = operationAuto.deliveryServiceFee;
if (n.feeItem === '接车服务费') n.amount = operationAuto.receiveServiceFee;
if (n.feeItem === '轮胎磨损费用' && !String(n.amount || '').trim()) n.amount = operationAuto.tireWearFee;
return n;
});
return next;
});
}, [operationAuto.docLossFee, operationAuto.deliveryServiceFee, operationAuto.receiveServiceFee, operationAuto.tireWearFee]);
var handleOperationSave = useCallback(function () {
setOperationMeta(function (m) { var nm = {}; for (var k2 in m) nm[k2] = m[k2]; nm.status = '待提交'; return nm; });
message.success('运维部已保存');
}, []);
var handleOperationSubmit = useCallback(function () {
Modal.confirm({
title: '提交确认',
content: '请确认运维部金额填写无误,点击确认完成提交。',
okText: '确认',
cancelText: '取消',
onOk: function () {
setOperationRows(function (p) {
var now = formatDateTimeNow();
return p.map(function (r) { var n = {}; for (var k in r) n[k] = r[k]; n.lastUpdateTime = now; return n; });
});
setOperationMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.submitBy = '运维部-王五'; nm.status = '已提交'; return nm; });
message.success('运维部已提交');
}
});
}, [operationRows]);
var handleOperationRevoke = useCallback(function () {
Modal.confirm({
title: '撤回确认',
content: '是否确认撤回?',
okText: '确认',
cancelText: '取消',
onOk: function () {
setOperationMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.status = '待提交'; return nm; });
message.success('运维部已撤回,可重新编辑并保存/提交');
}
});
}, []);
var addOperationRow = useCallback(function () {
setOperationRows(function (p) {
var next = p.slice();
next.push({ key: 'op-new-' + Date.now(), seq: next.length + 1, feeItem: '', amount: '', worryFreeDiscount: '', remark: '', lastUpdateTime: '', photos: [], attachments: [], fixed: false });
return next.map(function (r, idx) { var o = {}; for (var k in r) o[k] = r[k]; o.seq = idx + 1; return o; });
});
}, []);
var removeOperationRow = useCallback(function (key) {
setOperationRows(function (p) {
var next = p.filter(function (r) { return r.key !== key; });
return next.map(function (r, idx) { var o = {}; for (var k in r) o[k] = r[k]; o.seq = idx + 1; return o; });
});
}, []);
var updateOperationRow = useCallback(function (key, field, value) {
setOperationRows(function (p) {
var next = p.map(function (r) {
if (r.key !== key) return r;
var n = {}; for (var k in r) n[k] = r[k];
n[field] = value;
return n;
});
return next;
});
}, []);
// 安全组(示例数据)
var violationList = useMemo(function () {
return [{
key: 'w1',
code: 'WZ202602010001',
plateNo: '浙F03218F',
violationBehavior: '测试',
violationTime: '2026-02-01',
penaltyAmount: '100.00',
paymentStatus: '未缴费',
score: '6',
handleStatus: '未处理',
violationCustomer: '上海馨想事…',
violationPhoto: '',
remark: ''
}];
}, []);
var accidentList = useMemo(function () {
return [{
key: 'a1',
accidentCode: 'SG202508250001',
plateNo: '京A29256F',
accidentTime: '2025-08-25',
accidentPlace: '北京市大…',
accidentType: '撞固定物',
customerName: '北京海龙…',
ourClaimAmount: '',
theirClaimAmount: '',
responsibility: '全责',
accidentStatus: '未结案',
closeTime: '',
otherFee: '',
remark: ''
}];
}, []);
var safetyCollapsedState = useState(false);
var safetyMetaState = useState({ submitBy: '', status: '待提交' });
var safetyMeta = safetyMetaState[0];
var setSafetyMeta = safetyMetaState[1];
var handleSafetySave = useCallback(function () {
setSafetyMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.submitBy = nm.submitBy || '安全组-赵六'; nm.status = '待提交'; return nm; });
message.success('安全组已保存');
}, []);
var handleSafetySubmit = useCallback(function () {
Modal.confirm({
title: '提交确认',
content: '请确认安全组信息无误,点击确认完成提交。',
okText: '确认',
cancelText: '取消',
onOk: function () {
setSafetyMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.submitBy = nm.submitBy || '安全组-赵六'; nm.status = '已提交'; return nm; });
message.success('安全组已提交');
}
});
}, []);
var handleSafetyRevoke = useCallback(function () {
Modal.confirm({
title: '撤回确认',
content: '是否确认撤回?',
okText: '确认',
cancelText: '取消',
onOk: function () {
setSafetyMeta(function (m) { var nm = {}; for (var k in m) nm[k] = m[k]; nm.status = '待提交'; return nm; });
message.success('安全组已撤回,可重新编辑并保存/提交');
}
});
}, []);
var pendingSettleComputed = useMemo(function () {
var bs = parseFloat(businessServiceTotalComputed) || 0;
var rentRefund = parseFloat((billInfo && billInfo.shouldRefundRent) || '') || 0;
var op = parseFloat(operationTotalComputed) || 0;
var en = (parseFloat(energy.hydrogenSupplement) || 0) + (parseFloat(energy.hydrogenFee) || 0) + (parseFloat(energy.electricFee) || 0) - (parseFloat(energy.prepayRefund) || 0);
return (bs + rentRefund + en + op).toFixed(2);
}, [businessServiceTotalComputed, billInfo.shouldRefundRent, operationTotalComputed, energy.hydrogenSupplement, energy.hydrogenFee, energy.electricFee, energy.prepayRefund]);
var refundTotalComputed = useMemo(function () {
var deposit = parseFloat(stats.depositAmount) || 0;
var settle = parseFloat(pendingSettleComputed) || 0;
var diff = deposit - settle;
return diff > 0 ? diff.toFixed(2) : '0.00';
}, [stats.depositAmount, pendingSettleComputed]);
var payTotalComputed = useMemo(function () {
var deposit = parseFloat(stats.depositAmount) || 0;
var settle = parseFloat(pendingSettleComputed) || 0;
var diff = deposit - settle;
return diff < 0 ? Math.abs(diff).toFixed(2) : '0.00';
}, [stats.depositAmount, pendingSettleComputed]);
function buildBreakdownContent(list) {
var headStyle = { padding: '6px 10px', textAlign: 'left', borderBottom: '1px solid #f0f0f0', background: '#fafafa', fontWeight: 600, fontSize: 12 };
var tdStyle = { padding: '6px 10px', borderBottom: '1px solid #f0f0f0', fontSize: 12 };
return React.createElement('div', { style: { padding: 0, minWidth: 320 } },
React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse' } },
React.createElement('thead', null,
React.createElement('tr', null,
React.createElement('th', { style: headStyle }, '费用项'),
React.createElement('th', { style: Object.assign({}, headStyle, { textAlign: 'right' }) }, '金额(元)')
)
),
React.createElement('tbody', null,
(list || []).map(function (r) {
return React.createElement('tr', { key: r.key },
React.createElement('td', { style: tdStyle }, r.item),
React.createElement('td', { style: Object.assign({}, tdStyle, { fontWeight: r.strong ? 600 : 400, textAlign: 'right' }) }, r.amount)
);
})
)
)
);
}
var settleBreakdown = useMemo(function () {
return [
{ key: 'bs', item: '业务服务组费用项金额总和', amount: businessServiceTotalComputed || '0.00' },
{ key: 'rent', item: '业务服务组-车辆应退租金', amount: (toFixed2(billInfo && billInfo.shouldRefundRent) || '0.00') },
{ key: 'hDiff', item: '能源采购组-氢量差补缴金额', amount: (toFixed2(energy.hydrogenSupplement) || '0.00') },
{ key: 'hFee', item: '能源采购组-氢费补缴金额', amount: (toFixed2(energy.hydrogenFee) || '0.00') },
{ key: 'eFee', item: '能源采购组-电费补缴金额', amount: (toFixed2(energy.electricFee) || '0.00') },
{ key: 'prepay', item: '能源采购组-预付款退费金额(减)', amount: '-' + (toFixed2(energy.prepayRefund) || '0.00') },
{ key: 'op', item: '运维部费用项金额总额', amount: operationTotalComputed || '0.00' },
{ key: 'total', item: '待结算总额', amount: pendingSettleComputed || '0.00', strong: true }
];
}, [businessServiceTotalComputed, billInfo.shouldRefundRent, energy.hydrogenSupplement, energy.hydrogenFee, energy.electricFee, energy.prepayRefund, operationTotalComputed, pendingSettleComputed]);
var refundBreakdown = useMemo(function () {
return [
{ key: 'd', item: '保证金总额', amount: (toFixed2(stats.depositAmount) || '0.00') },
{ key: 's', item: '待结算总额', amount: (pendingSettleComputed || '0.00') },
{ key: 'r', item: '应退还总额', amount: (refundTotalComputed || '0.00'), strong: true }
];
}, [stats.depositAmount, pendingSettleComputed, refundTotalComputed]);
var payBreakdown = useMemo(function () {
return [
{ key: 'd', item: '保证金总额', amount: (toFixed2(stats.depositAmount) || '0.00') },
{ key: 's', item: '待结算总额', amount: (pendingSettleComputed || '0.00') },
{ key: 'p', item: '应补缴总额', amount: (payTotalComputed || '0.00'), strong: true }
];
}, [stats.depositAmount, pendingSettleComputed, payTotalComputed]);
var requirementModalOpenState = useState(false);
var requirementDocContent = useMemo(function () {
return (`还车应结款
一个「数字化资产ONEOS运管平台」中的「还车应结款」「费用明细」模块
#面包屑:
1.财务管理-还车应结款-费用明细
每个模块为一个单独卡片:
2.还车车辆明细;
2.1.车牌号:显示还车车牌号;
2.2.合同编码:显示还车车辆合同编码;
2.3.项目名称:显示还车车辆对应项目名称;
2.4.客户名称:显示还车车辆对应客户名称;
2.5.交车时间:显示还车车辆完成交车时间;
2.6.还车时间:显示还车车辆完成还车时间;
2.7.易损保:显示还车车辆是否购买易损保,显示为是或否,后方为提示图标,文案为:提供刹车片、灯泡、蓄电池、雨刮等易损件租期内免费更换服务(不含轮胎,只限自行到服务站更换);
2.8.轮胎保显示还车车辆是否购买轮胎保显示为是或否后方为提示图标文案为每个租赁年度内提供1次车辆轮胎因自然磨损产生的替换服务
2.9.养护保:显示还车车辆是否购买养护保,显示为是或否,后方为提示图标,文案为:按厂家保养要求提供定期保养服务;
3.还车费用明细:
#上方为还车费用统计数据,包括保证金总额、待结算总额、应退还总额、应补缴总额等相关信息,该部分只有业务管理组能查看;
3.1.保证金总额显示该车辆保证金金额格式为xx.xx元
3.2.待结算总额显示该车辆待结算金额格式为xx.xx元点击以气泡卡片列表显示费用项、金额。计算方式为「业务服务部所有费用项-金额总和」+「业务服务部-车辆应退租金」+「能源采购组-氢量差补缴金额」+「能源采购组-氢费补缴金额」+「能源采购组-电费补缴金额」-「能源采购组-预付款退费金额」+「运维部所有费用项-金额总额」;
3.3.应退还总额显示该车辆应退还金额格式为xx.xx元点击以气泡卡片列表显示费用项、金额。计算方式为「保证金金额」-「待结算金额」如果是正数,则显示在应退还总额中;
3.4.应补缴总额显示该车辆应补缴金额格式为xx.xx元点击以气泡卡片列表显示费用项、金额。计算方式为「保证金金额」-「待结算金额」如果是负数,则显示在应补缴总额中;
#下方为业务服务组、能源采购组、运维部、安全组4部分支持展开/收起功能;
4.业务服务组:仅由业务服务组人员进行填写;标题栏标题为业务服务组,后方为:总金额、提交人、状态(待提交、已提交),该部分只有业务管理组能查看;
4.1.总金额:显示所有费用总金额;
4.2.提交人:显示提交人;
4.3.状态:分为待提交(点击保存按钮)、已提交(点击提交按钮);
4.4.保存:点击保存已填写内容,如果已提交,则隐藏保存按钮;
4.5.提交:点击进行二次确认,提示信息:请确认业务服务组金额填写无误,点击确认完成提交。如果已提交,隐藏提交按钮;
4.6.撤回:已提交后,显示撤回按钮。点击进行二次确认,提示信息:是否确认撤回,点击确认,重新显示保存、提交按钮;
下方为列表,列表字段为:序号、费用项、金额、备注、照片、附件、操作;
4.7.序号1、2、3....依次类推;
4.8.费用项固定显示违章处理违约金、保险上浮、ETC-客户未缴费用、ETC卡缺损费、ETC设备缺损费该部分不能删除可通过点击下方新增一行添加新的条目该部分可删除
4.8.1.违章处理违约金:
4.8.2.保险上浮:
4.8.3.ETC-客户未缴费用:
4.8.4.ETC卡缺损费
4.8.5.ETC设备缺损费
4.9.金额输入框支持2位小数后缀为元
4.10.备注:文本域,支持自定义输入;
4.11.最后更新时间显示最后更新时间格式为YYYY-MM-DD HH:MM
4.12.照片:照片上传按钮,支持照片上传,支持多个附件;
4.13.附件:附件上传按钮,文案为:上传附件,支持多个附件;
4.14.操作:除固定费用项外,其余操作中为删除;
列表下方为车辆租金:包含本期账单已收租金、车辆实际租金、车辆应退租金;
4.15.本期账单已收租金输入框禁用反写本期账单实际到账租金从租赁账单中首期由于未和用友YS打通先显示为0对接完成后显示财务实际到账金额
4.16.车辆实际租金:输入框(可编辑),自动计算车辆实际租金,按照:(车辆月租金/30*(账单开始日期-还车日期总天数,取整)
4.17.车辆应退租金:输入框(可编辑),根据:本期账单已收租金-车辆实际租金计算并反写;
5.能源采购组:仅由能源采购组人员进行填写;标题栏标题为能源采购组,后方为总金额、提交人、状态(待提交、已提交),该部分只有能源采购组能查看;
5.1.总金额:显示所有费用总金额;
5.2.提交人:显示提交人;
5.3.状态:分为待提交(点击保存按钮)、已提交(点击提交按钮);
5.4.保存:点击保存已填写内容,如果已提交,则隐藏保存按钮;
5.5.提交:点击进行二次确认,提示信息:请确认业务服务组金额填写无误,点击确认完成提交。如果已提交,隐藏提交按钮;
5.6.撤回:已提交后,显示撤回按钮。点击进行二次确认,提示信息:是否确认撤回,点击确认,重新显示保存、提交按钮;
下方为填写表单:
5.7.氢量差补缴金额输入框支持2位小数后缀为元如果交车氢量还车氢量时根据“差额*退还车氢气单价”自动计算。
5.8.交车氢量格式为xx.xxMPa保留2位小数从该车辆交车在该合同交车时间氢量获取
5.9.还车氢量格式为xx.xxMPa保留2位小数从该车辆交车在该合同还车时间氢量获取
5.10.退还车氢气单价xx.xx元从车辆租赁合同中获取
5.11.能源费补缴金额右侧为2个输入框分别为氢费补缴金额、电费补缴金额支持2位小数后缀为元
5.12.预付款退费金额输入框支持2位小数后缀为元输入框下方显示项目预充值余额
6.运维部:仅由运维部人员进行填写;标题栏标题为运维部,后方为总金额、提交人、状态(待提交、已提交),该部分只有运维部能查看;
6.1.总金额:显示运维部所有费用项费用金额总额;
6.2.提交人:显示提交人;
6.3.状态:分为待提交(点击保存按钮)、已提交(点击提交按钮);
6.4.保存:点击保存已填写内容,如果已提交,则隐藏保存按钮;
6.5.提交:点击进行二次确认,提示信息:请确认业务服务组金额填写无误,点击确认完成提交。如果已提交,隐藏提交按钮;
6.6.撤回:已提交后,显示撤回按钮。点击进行二次确认,提示信息:是否确认撤回,点击确认,重新显示保存、提交按钮;
下方为列表:
6.7.序号1、2、3....依次类推;
6.8.费用项:固定显示项为:
6.8.1.清洗费:输入框手动填写;
6.8.2.未结算保养:输入框手动填写;
6.8.3.未结算维修:输入框手动填写;
6.8.4.车损费用:输入框手动填写;
6.8.5.工具损坏丢失费用:输入框手动填写;
6.8.6.证件丢失费用输入框自动计算金额交车时做备车检查时有还车时无得证件自动计算费用计算标准为行驶证200元牌照300元营运证300元加氢证300元如所有证件无丢失或交车时就为无则不进行计算
6.8.7.广告损坏丢失费用:输入框手动填写;
6.8.8.送车服务费输入框禁用反写交车时送车服务费如无则显示为0
6.8.9.接车服务费输入框禁用反写还车时接车服务费如无则显示为0
6.8.10.轮胎磨损费用:输入框(可编辑),轮胎磨损费用(元/mm*(交车时所有轮胎及备胎胎纹深度-还车时所有轮胎及备胎胎纹深度)自动计算,此外如还车胎纹总额大于交车胎纹总额则不进行计算,此外轮胎磨损费用标题后增加说明图标,悬浮图标显示气泡卡片:卡片内为列表,标题为:轮胎名称、交车胎纹深度、还车胎纹深度、胎纹差、单价、总金额,每一行为一个单独的轮胎(包括备胎);
该部分不能删除;可通过点击下方新增一行,添加新的条目(该部分可删除);
6.9.金额输入框支持2位小数后缀为元
6.10.无忧包减免输入框支持2位小数后缀为元。仅有三个费用项后有输入框用户未购买易损保时未结算维修费用后该输入框禁用用户未购买轮胎保时间轮胎磨损费用后该输入框为禁用用户未购买养护保时未结算保养后该输入框为禁用
6.11.备注:文本域,支持自定义输入;
6.12.照片:照片上传按钮,支持照片上传;
6.13.附件:附件上传按钮,文案为:上传附件;
6.14.操作:除固定费用项外,其余操作中为删除;
7.安全组:仅由安全组人员进行确认提交;标题栏标题为安全组,后方为提交人、状态(待提交、已提交),该部分只有安全组能查看;
7.1.提交人:显示提交人;
7.2.状态:分为待提交(点击保存按钮)、已提交(点击提交按钮);
7.3.保存:点击保存已填写内容,如果已提交,则隐藏保存按钮;
7.4.提交:点击进行二次确认,提示信息:请确认业务服务组金额填写无误,点击确认完成提交。如果已提交,隐藏提交按钮;
7.5.撤回:已提交后,显示撤回按钮。点击进行二次确认,提示信息:是否确认撤回,点击确认,重新显示保存、提交按钮;
7.6.违章清单:违章编码、车牌号、违法行为、违法时间、罚款金额、缴费状态、计分值、是否处理、违章客户、违章照片、备注;
7.7.事故清单:列表显示:事故编码、车牌号、事故时间、事故地点、事故类型、客户名称、我方定损金额、对方定损金额、责任划分、事故状态、结案时间、其他费用、备注;
8.底部为提交、取消按钮;
8.1.提交审核还车应结款生成后有15天时间倒计时倒计时结束并且业务服务组、能源采购组、运维部、安全部均提交后才启用平时禁用倒计时显示在按钮上格式为x天x小时后可提交审核
8.2.取消:点击返回还车应结款列表页;
`);
}, []);
// 车辆明细表列
var vehicleColumns = useMemo(function () {
return [
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 110 },
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 160, ellipsis: true },
{ title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 140, ellipsis: true },
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 140, ellipsis: true },
{ title: '交车时间', dataIndex: 'deliveryTime', key: 'deliveryTime', width: 110 },
{ title: '还车时间', dataIndex: 'returnTime', key: 'returnTime', width: 110 },
{
title: '易损保', dataIndex: 'fragileInsurance', key: 'fragileInsurance', width: 110,
render: function (v) {
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6 } },
(v || '-'),
React.createElement(Tooltip, { title: '提供刹车片、灯泡、蓄电池、雨刮等易损件租期内免费更换服务(不含轮胎,只限自行到服务站更换)' },
React.createElement('span', {
style: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: 16,
height: 16,
borderRadius: 999,
border: '1px solid #d9d9d9',
color: '#999',
fontSize: 12,
lineHeight: '16px',
cursor: 'help',
userSelect: 'none'
}
}, 'i')
)
);
}
},
{
title: '轮胎保', dataIndex: 'tireInsurance', key: 'tireInsurance', width: 110,
render: function (v) {
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6 } },
(v || '-'),
React.createElement(Tooltip, { title: '每个租赁年度内提供1次车辆轮胎因自然磨损产生的替换服务' },
React.createElement('span', {
style: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: 16,
height: 16,
borderRadius: 999,
border: '1px solid #d9d9d9',
color: '#999',
fontSize: 12,
lineHeight: '16px',
cursor: 'help',
userSelect: 'none'
}
}, 'i')
)
);
}
},
{
title: '养护保', dataIndex: 'maintenanceInsurance', key: 'maintenanceInsurance', width: 110,
render: function (v) {
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6 } },
(v || '-'),
React.createElement(Tooltip, { title: '按厂家保养要求提供定期保养服务' },
React.createElement('span', {
style: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: 16,
height: 16,
borderRadius: 999,
border: '1px solid #d9d9d9',
color: '#999',
fontSize: 12,
lineHeight: '16px',
cursor: 'help',
userSelect: 'none'
}
}, 'i')
)
);
}
}
];
}, []);
var latestRefs = useRef({});
latestRefs.current = {
businessServiceMeta: businessServiceMeta,
operationMeta: operationMeta,
insuranceFlags: insuranceFlags,
tireTreadList: tireTreadList,
updateBusinessServiceRow: updateBusinessServiceRow,
removeBusinessServiceRow: removeBusinessServiceRow,
updateOperationRow: updateOperationRow,
removeOperationRow: removeOperationRow
};
var businessServiceColumns = useMemo(function () {
return [
{ title: '序号', dataIndex: 'seq', key: 'seq', width: 60 },
{
title: '费用项', dataIndex: 'feeItem', key: 'feeItem', width: 200,
render: function (v, r) {
var ref = latestRefs.current || {};
var meta = ref.businessServiceMeta || {};
var readOnly = meta.status === '已提交';
return r.fixed ? (v || '-') : React.createElement(Input, { value: v, onChange: function (e) { ref.updateBusinessServiceRow(r.key, 'feeItem', e.target.value); }, placeholder: '费用项', disabled: readOnly });
}
},
{
title: RequiredLabel('金额'), dataIndex: 'amount', key: 'amount', width: 140,
render: function (v, r) {
var ref = latestRefs.current || {};
var meta = ref.businessServiceMeta || {};
var readOnly = meta.status === '已提交';
return React.createElement(Input, { value: v, onChange: function (e) { ref.updateBusinessServiceRow(r.key, 'amount', e.target.value); }, placeholder: '0.00', addonAfter: '元', disabled: readOnly });
}
},
{
title: '备注', dataIndex: 'remark', key: 'remark',
render: function (v, r) {
var ref = latestRefs.current || {};
var meta = ref.businessServiceMeta || {};
var readOnly = meta.status === '已提交';
return React.createElement(TextArea, { value: v, onChange: function (e) { ref.updateBusinessServiceRow(r.key, 'remark', e.target.value); }, placeholder: '备注', rows: 1, disabled: readOnly });
}
},
{ title: '最后更新时间', dataIndex: 'lastUpdateTime', key: 'lastUpdateTime', width: 150, render: function (v) { return React.createElement('span', { style: { fontSize: 12, color: '#666' } }, v || '-'); } },
{ title: '照片', key: 'photo', width: 100, render: function () { var ref = latestRefs.current || {}; var meta = ref.businessServiceMeta || {}; var readOnly = meta.status === '已提交'; return React.createElement(Button, { size: 'small', disabled: readOnly, onClick: function () { message.info('照片上传(原型)'); } }, '上传'); } },
{ title: '附件', key: 'attachment', width: 110, render: function () { var ref = latestRefs.current || {}; var meta = ref.businessServiceMeta || {}; var readOnly = meta.status === '已提交'; return React.createElement(Button, { size: 'small', disabled: readOnly, onClick: function () { message.info('上传附件(原型)'); } }, '上传附件'); } },
{
title: '操作', key: 'action', width: 80,
render: function (_, r) {
var ref = latestRefs.current || {};
var meta = ref.businessServiceMeta || {};
if (r.fixed || meta.status === '已提交') return null;
return React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function () { ref.removeBusinessServiceRow(r.key); } }, '删除');
}
}
];
}, []);
var operationColumns = useMemo(function () {
return [
{ title: '序号', dataIndex: 'seq', key: 'seq', width: 60 },
{
title: '费用项', dataIndex: 'feeItem', key: 'feeItem', width: 200,
render: function (v, r) {
var ref = latestRefs.current || {};
var meta = ref.operationMeta || {};
var readOnly = meta.status === '已提交';
if (r.fixed) {
if (v === '轮胎磨损费用') {
var popContent = React.createElement('div', { style: { width: 760, maxWidth: '80vw' } },
React.createElement('div', { style: { fontWeight: 600, marginBottom: 8 } }, '轮胎磨损费用明细'),
React.createElement(Table, {
rowKey: 'key',
size: 'small',
bordered: true,
pagination: false,
dataSource: ref.tireTreadList,
columns: [
{ title: '轮胎名称', dataIndex: 'name', key: 'name', width: 120, ellipsis: true },
{ title: '交车胎纹深度', dataIndex: 'deliveryDepth', key: 'deliveryDepth', width: 110, render: function (vv) { return (vv || '0.00') + 'mm'; } },
{ title: '还车胎纹深度', dataIndex: 'returnDepth', key: 'returnDepth', width: 110, render: function (vv) { return (vv || '0.00') + 'mm'; } },
{ title: '胎纹差', dataIndex: 'diff', key: 'diff', width: 80, render: function (vv) { return (vv || '0.00') + 'mm'; } },
{ title: '单价', dataIndex: 'unitPrice', key: 'unitPrice', width: 90, render: function (vv) { return (vv || '0.00') + '元/mm'; } },
{ title: '总金额', dataIndex: 'totalAmount', key: 'totalAmount', width: 90, render: function (vv) { return (vv || '0.00') + '元'; } }
]
})
);
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6 } },
(v || '-'),
React.createElement(Popover, { content: popContent, trigger: 'hover', placement: 'topLeft' },
React.createElement('span', {
style: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: 16,
height: 16,
borderRadius: 999,
border: '1px solid #d9d9d9',
color: '#999',
fontSize: 12,
lineHeight: '16px',
cursor: 'help',
userSelect: 'none'
}
}, 'i')
)
);
}
return (v || '-');
}
return React.createElement(Input, { value: v, onChange: function (e) { ref.updateOperationRow(r.key, 'feeItem', e.target.value); }, placeholder: '费用项', disabled: readOnly });
}
},
{
title: RequiredLabel('金额'), dataIndex: 'amount', key: 'amount', width: 140,
render: function (v, r) {
var ref = latestRefs.current || {};
var meta = ref.operationMeta || {};
var readOnly = meta.status === '已提交';
var fee = r && r.feeItem;
var disabled = readOnly;
if (fee === '送车服务费' || fee === '接车服务费' || fee === '证件丢失费用') disabled = true;
return React.createElement(Input, { value: v, onChange: function (e) { ref.updateOperationRow(r.key, 'amount', e.target.value); }, placeholder: '0.00', addonAfter: '元', disabled: disabled });
}
},
{
title: React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6 } },
'无忧包减免',
React.createElement(Tooltip, { title: '无忧包减免不会列入运维成本,而是计入业务成本' },
React.createElement('span', {
style: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: 16,
height: 16,
borderRadius: 999,
border: '1px solid #d9d9d9',
color: '#999',
fontSize: 12,
lineHeight: '16px',
cursor: 'help',
userSelect: 'none'
}
}, 'i')
)
),
dataIndex: 'worryFreeDiscount',
key: 'worryFreeDiscount',
width: 140,
render: function (v, r) {
var ref = latestRefs.current || {};
var meta = ref.operationMeta || {};
var fee = r && r.feeItem;
var enableFor = (fee === '未结算维修' || fee === '轮胎磨损费用' || fee === '未结算保养');
if (!enableFor) return React.createElement('span', { style: { color: '#999' } }, '-');
var readOnly = meta.status === '已提交';
var disabled = false;
var ins = ref.insuranceFlags || {};
if (fee === '未结算维修' && !ins.hasFragileInsurance) disabled = true;
if (fee === '轮胎磨损费用' && !ins.hasTireInsurance) disabled = true;
if (fee === '未结算保养' && !ins.hasMaintenanceInsurance) disabled = true;
return React.createElement(Input, { value: v, onChange: function (e) { ref.updateOperationRow(r.key, 'worryFreeDiscount', e.target.value); }, placeholder: '0.00', addonAfter: '元', disabled: disabled || readOnly });
}
},
{
title: '备注', dataIndex: 'remark', key: 'remark',
render: function (v, r) {
var ref = latestRefs.current || {};
var meta = ref.operationMeta || {};
var readOnly = meta.status === '已提交';
return React.createElement(TextArea, { value: v, onChange: function (e) { ref.updateOperationRow(r.key, 'remark', e.target.value); }, placeholder: '备注', rows: 1, disabled: readOnly });
}
},
{ title: '最后更新时间', dataIndex: 'lastUpdateTime', key: 'lastUpdateTime', width: 150, render: function (v) { return React.createElement('span', { style: { fontSize: 12, color: '#666' } }, v || '-'); } },
{ title: '照片', key: 'photo', width: 100, render: function () { return React.createElement(Button, { size: 'small', onClick: function () { message.info('照片上传(原型)'); } }, '上传'); } },
{ title: '附件', key: 'attachment', width: 110, render: function () { return React.createElement(Button, { size: 'small', onClick: function () { message.info('上传附件(原型)'); } }, '上传附件'); } },
{
title: '操作', key: 'action', width: 80,
render: function (_, r) {
var ref = latestRefs.current || {};
var meta = ref.operationMeta || {};
if (r.fixed || meta.status === '已提交') return null;
return React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function () { ref.removeOperationRow(r.key); } }, '删除');
}
}
];
}, []);
// 提交审核倒计时(原型:生成后 15 天)
var generatedAt = useMemo(function () { return new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); }, []);
var nowTickState = useState(Date.now());
useEffect(function () {
var t = setInterval(function () { nowTickState[1](Date.now()); }, 60 * 1000);
return function () { clearInterval(t); };
}, []);
function remainingText() {
var end = generatedAt.getTime() + 15 * 24 * 60 * 60 * 1000;
var left = end - nowTickState[0];
if (left <= 0) return '0天0小时后可提交审核';
var d = Math.floor(left / (24 * 60 * 60 * 1000));
var h = Math.floor((left % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));
return d + '天' + h + '小时后可提交审核';
}
function canSubmitReview() {
var end = generatedAt.getTime() + 15 * 24 * 60 * 60 * 1000;
var timeOk = (nowTickState[0] >= end);
var groupOk = businessServiceMeta.status === '已提交' && energyMeta.status === '已提交' && operationMeta.status === '已提交' && safetyMeta.status === '已提交';
return timeOk && groupOk;
}
function handleSubmit() {
function isValidMoney(s) {
if (s === null || s === undefined) return false;
var v = String(s).trim();
if (!v) return false;
return !isNaN(parseFloat(v));
}
// 业务服务组:每行“金额”必填
for (var i = 0; i < (businessServiceRows || []).length; i++) {
var r1 = businessServiceRows[i];
if (!isValidMoney(r1.amount)) {
message.error('请填写业务服务组第' + (r1.seq || (i + 1)) + '行金额');
return;
}
}
if (!isValidMoney(billInfo.actualRent)) { message.error('请填写车辆实际租金'); return; }
if (!isValidMoney(billInfo.shouldRefundRent)) { message.error('请填写车辆应退租金'); return; }
// 能源采购组:氢量差/氢费/电费/预付款退费 必填
if (!isValidMoney(energy.hydrogenSupplement)) { message.error('请填写氢量差补缴金额'); return; }
if (!isValidMoney(energy.hydrogenFee)) { message.error('请填写氢费补缴金额'); return; }
if (!isValidMoney(energy.electricFee)) { message.error('请填写电费补缴金额'); return; }
if (!isValidMoney(energy.prepayRefund)) { message.error('请填写预付款退费金额'); return; }
// 运维部:每行“金额”必填;无忧包减免仅对 3 个费用项必填(即显示输入框的行)
for (var j = 0; j < (operationRows || []).length; j++) {
var r2 = operationRows[j];
if (!isValidMoney(r2.amount)) {
message.error('请填写运维部第' + (r2.seq || (j + 1)) + '行金额');
return;
}
var fee = r2 && r2.feeItem;
var hasDiscountInput = (fee === '未结算维修' || fee === '轮胎磨损费用' || fee === '未结算保养');
if (hasDiscountInput && !isValidMoney(r2.worryFreeDiscount)) {
message.error('请填写运维部“' + fee + '”的无忧包减免金额');
return;
}
}
if (!canSubmitReview()) {
message.error('未满足提交审核条件(倒计时结束且四组均已提交)');
return;
}
message.success('提交审核成功(原型)');
}
function handleCancel() {
message.info('返回还车应结款列表页(原型)');
}
return React.createElement('div', { style: layoutStyle },
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } },
React.createElement(Breadcrumb, { items: [{ title: '财务管理' }, { title: '还车应结款' }] }),
React.createElement(Button, { type: 'link', onClick: function () { requirementModalOpenState[1](true); } }, '查看需求说明')
),
React.createElement(Card, { title: '还车车辆明细', style: cardStyle },
React.createElement(Table, { rowKey: 'key', columns: vehicleColumns, dataSource: vehicleDetail, pagination: false, bordered: true, size: 'middle', scroll: { x: 980 } })
),
React.createElement(Card, { title: '还车费用明细', style: cardStyle },
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 16, marginBottom: 16 } },
React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: '18px 16px', boxShadow: '0 1px 2px rgba(0,0,0,0.04)' } },
React.createElement('div', { style: { fontSize: 13, color: '#666', marginBottom: 12 } }, '保证金总额'),
React.createElement('div', { style: { fontSize: 22, fontWeight: 600, color: '#333' } }, (stats.depositAmount || '0.00'), React.createElement('span', { style: { fontSize: 14, fontWeight: 500, marginLeft: 4 } }, '元'))
),
React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: '18px 16px', boxShadow: '0 1px 2px rgba(0,0,0,0.04)' } },
React.createElement('div', { style: { fontSize: 13, color: '#666', marginBottom: 12 } }, '待结算总额'),
React.createElement(Popover, { trigger: 'click', placement: 'bottomLeft', content: buildBreakdownContent(settleBreakdown) },
React.createElement('div', { style: { fontSize: 22, fontWeight: 600, color: '#fa8c16', cursor: 'pointer' } },
(pendingSettleComputed || '0.00'),
React.createElement('span', { style: { fontSize: 14, fontWeight: 500, marginLeft: 4, color: '#333' } }, '元')
)
)
),
React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: '18px 16px', boxShadow: '0 1px 2px rgba(0,0,0,0.04)' } },
React.createElement('div', { style: { fontSize: 13, color: '#666', marginBottom: 12 } }, '应退还总额'),
React.createElement(Popover, { trigger: 'click', placement: 'bottomLeft', content: buildBreakdownContent(refundBreakdown) },
React.createElement('div', { style: { fontSize: 22, fontWeight: 600, color: '#52c41a', cursor: 'pointer' } },
(refundTotalComputed || '0.00'),
React.createElement('span', { style: { fontSize: 14, fontWeight: 500, marginLeft: 4, color: '#333' } }, '元')
)
)
),
React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: '18px 16px', boxShadow: '0 1px 2px rgba(0,0,0,0.04)' } },
React.createElement('div', { style: { fontSize: 13, color: '#666', marginBottom: 12 } }, '应补缴总额'),
React.createElement(Popover, { trigger: 'click', placement: 'bottomLeft', content: buildBreakdownContent(payBreakdown) },
React.createElement('div', { style: { fontSize: 22, fontWeight: 600, color: '#f5222d', cursor: 'pointer' } },
(payTotalComputed || '0.00'),
React.createElement('span', { style: { fontSize: 14, fontWeight: 500, marginLeft: 4, color: '#333' } }, '元')
)
)
)
),
React.createElement(CollapseSection, {
collapsed: businessServiceCollapsedState[0],
setCollapsed: businessServiceCollapsedState[1],
title: '业务服务组',
extra: React.createElement(React.Fragment, null,
React.createElement('span', null, '总金额:', businessServiceTotalComputed, ' 元'),
React.createElement('span', null, '提交人:', businessServiceMeta.submitBy || '-'),
React.createElement('span', null, '状态:', businessServiceMeta.status)
),
headerActions: businessServiceMeta.status === '已提交'
? React.createElement(Button, { size: 'small', onClick: handleBusinessServiceRevoke }, '撤回')
: React.createElement(React.Fragment, null,
React.createElement(Button, { size: 'small', onClick: handleBusinessServiceSave }, '保存'),
React.createElement(Button, { size: 'small', type: 'primary', onClick: handleBusinessServiceSubmit }, '提交')
)
},
React.createElement(Table, { rowKey: 'key', columns: businessServiceColumns, dataSource: businessServiceRows, pagination: false, bordered: true, size: 'small' }),
React.createElement('div', { style: { marginTop: 12, paddingTop: 12, borderTop: '1px dashed #f0f0f0' } },
React.createElement('div', { style: { fontWeight: 600, marginBottom: 10 } }, '车辆租金'),
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 12 } },
React.createElement('div', null,
React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, '本期账单已收租金'),
React.createElement(Input, { value: billInfo.receivedRent, addonAfter: '元', disabled: true })
),
React.createElement('div', null,
React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, RequiredLabel('车辆实际租金')),
React.createElement(Input, {
value: billInfo.actualRent,
onChange: function (e) { var v = e.target.value; setBillInfo(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.actualRent = v; n.actualRentManual = true; return n; }); },
placeholder: '0.00',
addonAfter: '元',
disabled: businessServiceMeta.status === '已提交'
})
),
React.createElement('div', null,
React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, RequiredLabel('车辆应退租金')),
React.createElement(Input, {
value: billInfo.shouldRefundRent,
onChange: function (e) { var v = e.target.value; setBillInfo(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.shouldRefundRent = v; n.shouldRefundManual = true; return n; }); },
placeholder: '0.00',
addonAfter: '元',
disabled: businessServiceMeta.status === '已提交'
})
)
)
),
businessServiceMeta.status === '待提交'
? React.createElement('div', { style: { marginTop: 8, width: '100%' } },
React.createElement(Button, { type: 'dashed', size: 'small', onClick: addBusinessServiceRow, block: true, style: { width: '100%' } }, '新增一行')
)
: null
),
React.createElement(CollapseSection, {
collapsed: energyCollapsedState[0],
setCollapsed: energyCollapsedState[1],
title: '能源采购组',
extra: React.createElement(React.Fragment, null,
React.createElement('span', null, '总金额:', energyTotalComputed, ' 元'),
React.createElement('span', null, '提交人:', energyMeta.submitBy || '-'),
React.createElement('span', null, '状态:', energyMeta.status)
),
headerActions: energyMeta.status === '已提交'
? React.createElement(Button, { size: 'small', onClick: handleEnergyRevoke }, '撤回')
: React.createElement(React.Fragment, null,
React.createElement(Button, { size: 'small', onClick: handleEnergySave }, '保存'),
React.createElement(Button, { size: 'small', type: 'primary', onClick: handleEnergySubmit }, '提交')
)
},
React.createElement('div', null,
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', gap: 12, marginBottom: 16 } },
React.createElement('div', null,
React.createElement('div', { style: { marginBottom: 8, fontWeight: 500 } }, RequiredLabel('氢量差补缴金额')),
React.createElement(Input, {
value: energy.hydrogenSupplement,
onChange: function (e) { setEnergy(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.hydrogenSupplement = e.target.value; return n; }); },
placeholder: '0.00',
addonAfter: '元',
style: { width: '100%' },
readOnly: (parseFloat(energy.deliveryHydrogen) || 0) > (parseFloat(energy.returnHydrogen) || 0),
disabled: energyMeta.status === '已提交'
})
),
React.createElement('div', null,
React.createElement('div', { style: { marginBottom: 8, fontWeight: 500 } }, '交车氢量'),
React.createElement(Input, { value: (toFixed2(energy.deliveryHydrogen) || '0.00'), addonAfter: 'MPa', disabled: true })
),
React.createElement('div', null,
React.createElement('div', { style: { marginBottom: 8, fontWeight: 500 } }, '还车氢量'),
React.createElement(Input, { value: (toFixed2(energy.returnHydrogen) || '0.00'), addonAfter: 'MPa', disabled: true })
),
React.createElement('div', null,
React.createElement('div', { style: { marginBottom: 8, fontWeight: 500 } }, '退还车氢气单价'),
React.createElement(Input, { value: (toFixed2(energy.hydrogenUnitPrice) || '0.00'), addonAfter: '元', disabled: true })
)
),
React.createElement('div', null,
React.createElement('div', { style: { marginBottom: 8, fontWeight: 500 } }, RequiredLabel('能源费补缴金额')),
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', gap: 12, marginBottom: 12 } },
React.createElement('div', null, React.createElement(Input, { value: energy.hydrogenFee, onChange: function (e) { setEnergy(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.hydrogenFee = e.target.value; return n; }); }, placeholder: '氢费补缴金额', addonAfter: '元', style: { width: '100%' }, disabled: energyMeta.status === '已提交' })),
React.createElement('div', null, React.createElement(Input, { value: energy.electricFee, onChange: function (e) { setEnergy(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.electricFee = e.target.value; return n; }); }, placeholder: '电费补缴金额', addonAfter: '元', style: { width: '100%' }, disabled: energyMeta.status === '已提交' })),
React.createElement('div', null),
React.createElement('div', null)
)
),
React.createElement('div', { style: { gridColumn: '1 / -1', borderTop: '1px solid #f0f0f0', paddingTop: 12, marginTop: 4 } },
React.createElement('div', { style: { marginBottom: 8, fontWeight: 500 } }, RequiredLabel('预付款退费金额')),
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', gap: 12 } },
React.createElement('div', null,
React.createElement(Input, { value: energy.prepayRefund, onChange: function (e) { setEnergy(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.prepayRefund = e.target.value; return n; }); }, placeholder: '0.00', addonAfter: '元', style: { width: '100%' }, disabled: energyMeta.status === '已提交' })
),
React.createElement('div', null),
React.createElement('div', null),
React.createElement('div', null)
),
React.createElement('div', { style: { marginTop: 8, fontSize: 12, color: '#666' } }, '项目预充值余额:', energy.userBalance, ' 元')
)
),
),
React.createElement(CollapseSection, {
collapsed: operationCollapsedState[0],
setCollapsed: operationCollapsedState[1],
title: '运维部',
extra: React.createElement(React.Fragment, null,
React.createElement('span', null, '总金额:', operationTotalComputed, ' 元'),
React.createElement('span', null, '提交人:', operationMeta.submitBy || '-'),
React.createElement('span', null, '状态:', operationMeta.status)
),
headerActions: operationMeta.status === '已提交'
? React.createElement(Button, { size: 'small', onClick: handleOperationRevoke }, '撤回')
: React.createElement(React.Fragment, null,
React.createElement(Button, { size: 'small', onClick: handleOperationSave }, '保存'),
React.createElement(Button, { size: 'small', type: 'primary', onClick: handleOperationSubmit }, '提交')
)
},
React.createElement(Table, { rowKey: 'key', columns: operationColumns, dataSource: operationRows, pagination: false, bordered: true, size: 'small' }),
operationMeta.status === '待提交'
? React.createElement('div', { style: { marginTop: 8, width: '100%' } },
React.createElement(Button, { type: 'dashed', size: 'small', onClick: addOperationRow, block: true, style: { width: '100%' } }, '新增一行')
)
: null
),
React.createElement(CollapseSection, {
collapsed: safetyCollapsedState[0],
setCollapsed: safetyCollapsedState[1],
title: '安全组',
extra: React.createElement(React.Fragment, null,
React.createElement('span', null, '提交人:', safetyMeta.submitBy || '-'),
React.createElement('span', null, '状态:', safetyMeta.status)
),
headerActions: safetyMeta.status === '已提交'
? React.createElement(Button, { size: 'small', onClick: handleSafetyRevoke }, '撤回')
: React.createElement(React.Fragment, null,
React.createElement(Button, { size: 'small', onClick: handleSafetySave }, '保存'),
React.createElement(Button, { size: 'small', type: 'primary', onClick: handleSafetySubmit }, '提交')
)
},
React.createElement('div', { style: { marginBottom: 12, fontWeight: 600 } }, '违章清单'),
React.createElement(Table, {
rowKey: 'key',
size: 'small',
bordered: true,
pagination: false,
dataSource: violationList,
columns: [
{ title: '违章编码', dataIndex: 'code', key: 'code', width: 130, ellipsis: true },
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 110 },
{ title: '违法行为', dataIndex: 'violationBehavior', key: 'violationBehavior', width: 110, ellipsis: true },
{ title: '违法时间', dataIndex: 'violationTime', key: 'violationTime', width: 110 },
{ title: '罚款金额', dataIndex: 'penaltyAmount', key: 'penaltyAmount', width: 90, render: function (v) { return (v || '0.00'); } },
{ title: '缴费状态', dataIndex: 'paymentStatus', key: 'paymentStatus', width: 90 },
{ title: '记分值', dataIndex: 'score', key: 'score', width: 70 },
{ title: '是否处理', dataIndex: 'handleStatus', key: 'handleStatus', width: 90 },
{ title: '违章客户', dataIndex: 'violationCustomer', key: 'violationCustomer', width: 120, ellipsis: true },
{ title: '违章照片', dataIndex: 'violationPhoto', key: 'violationPhoto', width: 90, render: function (v) { return v ? v : ''; } },
{ title: '备注', dataIndex: 'remark', key: 'remark', width: 120, ellipsis: true }
]
}),
React.createElement('div', { style: { margin: '16px 0 12px', fontWeight: 600 } }, '事故清单'),
React.createElement(Table, {
rowKey: 'key',
size: 'small',
bordered: true,
pagination: false,
dataSource: accidentList,
locale: { emptyText: '暂无事故记录' },
columns: [
{ title: '事故编码', dataIndex: 'accidentCode', key: 'accidentCode', width: 130, ellipsis: true },
{ title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 110 },
{ title: '事故时间', dataIndex: 'accidentTime', key: 'accidentTime', width: 110 },
{ title: '事故地点', dataIndex: 'accidentPlace', key: 'accidentPlace', width: 120, ellipsis: true },
{ title: '事故类型', dataIndex: 'accidentType', key: 'accidentType', width: 110, ellipsis: true },
{ title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 120, ellipsis: true },
{ title: '我方定损金额', dataIndex: 'ourClaimAmount', key: 'ourClaimAmount', width: 110, render: function (v) { return v || ''; } },
{ title: '对方定损金额', dataIndex: 'theirClaimAmount', key: 'theirClaimAmount', width: 110, render: function (v) { return v || ''; } },
{ title: '责任划分', dataIndex: 'responsibility', key: 'responsibility', width: 90 },
{ title: '事故状态', dataIndex: 'accidentStatus', key: 'accidentStatus', width: 90 },
{ title: '结案时间', dataIndex: 'closeTime', key: 'closeTime', width: 110 },
{ title: '其它费用', dataIndex: 'otherFee', key: 'otherFee', width: 90, render: function (v) { return v || ''; } },
{ title: '备注', dataIndex: 'remark', key: 'remark', width: 120, ellipsis: true }
]
})
)
),
React.createElement('div', { style: footerStyle },
React.createElement(Button, { type: 'primary', onClick: handleSubmit, disabled: !canSubmitReview() }, canSubmitReview() ? '提交审核' : remainingText()),
React.createElement(Button, { onClick: handleCancel }, '取消')
),
React.createElement(Modal, {
open: requirementModalOpenState[0],
onCancel: function () { requirementModalOpenState[1](false); },
onOk: function () { requirementModalOpenState[1](false); },
title: '需求说明',
width: 860
},
React.createElement('div', { style: { maxHeight: '70vh', overflow: 'auto', whiteSpace: 'pre-wrap', lineHeight: 1.7, color: '#333' } }, requirementDocContent)
)
);
};