- 还车应结款/查看/费用明细:交车/还车时间统一显示到分钟 - 待审批与审批中状态操作列支持撤回 - 费用明细:已收租金可编辑、预付款退费展示最后一辆车提示、证件丢失费用可编辑 - 同步更新相关需求说明文案 Made-with: Cursor
1367 lines
70 KiB
JavaScript
1367 lines
70 KiB
JavaScript
// 【重要】必须使用 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 fmtYMDHM(v) {
|
||
if (v === null || v === undefined) return '-';
|
||
var s = String(v).trim();
|
||
if (!s) return '-';
|
||
if (/^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}/.test(s)) return s.slice(0, 16);
|
||
if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s + ' 00:00';
|
||
try {
|
||
var d = new Date(s.replace(/-/g, '/'));
|
||
if (isNaN(d.getTime())) return s;
|
||
var p2 = function (n) { return n < 10 ? '0' + n : '' + n; };
|
||
return d.getFullYear() + '-' + p2(d.getMonth() + 1) + '-' + p2(d.getDate()) + ' ' + p2(d.getHours()) + ':' + p2(d.getMinutes());
|
||
} catch (e) {
|
||
return s;
|
||
}
|
||
}
|
||
|
||
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 09:30',
|
||
returnTime: '2026-02-27 16:20',
|
||
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]);
|
||
|
||
// 原型判断:当前合同仅展示 1 辆车,则视为“最后一辆车还车”
|
||
var isLastVehicleInContract = useMemo(function () {
|
||
return (vehicleDetail || []).length === 1;
|
||
}, [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]);
|
||
|
||
// 轮胎胎纹明细(原型 mock:10个轮胎 + 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 === '证件丢失费用' && !String(n.amount || '').trim()) 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打通后,显示财务到账金额)
|
||
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: 160, render: function (v) { return fmtYMDHM(v); } },
|
||
{ title: '还车时间', dataIndex: 'returnTime', key: 'returnTime', width: 160, render: function (v) { return fmtYMDHM(v); } },
|
||
{
|
||
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 === '接车服务费') 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,
|
||
onChange: function (e) { var v = e.target.value; setBillInfo(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.receivedRent = v; 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.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('div', { style: { display: 'flex', alignItems: 'center', gap: 12 } },
|
||
React.createElement('div', { style: { flex: 1, minWidth: 0 } },
|
||
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 === '已提交' })
|
||
),
|
||
isLastVehicleInContract ? React.createElement('span', { style: { color: '#f5222d', fontSize: 12, fontWeight: 600, whiteSpace: 'nowrap' } }, '当前车辆为该合同最后一辆车') : null
|
||
)
|
||
),
|
||
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)
|
||
)
|
||
);
|
||
};
|
||
|