diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4f074fd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.DS_Store
+**/.DS_Store
diff --git a/web端/财务管理/还车应结款-查看.jsx b/web端/财务管理/还车应结款-查看.jsx
new file mode 100644
index 0000000..f89f4d5
--- /dev/null
+++ b/web端/财务管理/还车应结款-查看.jsx
@@ -0,0 +1,589 @@
+// 【重要】必须使用 const Component 作为组件变量名
+// 财务管理 - 还车应结款 - 查看(只读)
+
+const Component = function () {
+ var useMemo = React.useMemo;
+
+ 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 Tooltip = antd.Tooltip;
+ var Popover = antd.Popover;
+ var message = antd.message;
+
+ var TextArea = Input.TextArea;
+
+ function toFixed2(v) {
+ if (v === null || v === undefined || v === '') return '';
+ var n = typeof v === 'number' ? v : parseFloat(v);
+ return isNaN(n) ? '' : n.toFixed(2);
+ }
+
+ function fmtMoney(v) {
+ var n = typeof v === 'number' ? v : parseFloat(v);
+ if (isNaN(n)) n = 0;
+ return n.toFixed(2);
+ }
+
+ // 页面样式
+ var layoutStyle = { padding: '16px 24px 72px', 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 businessServiceRows = useMemo(function () {
+ var items = ['违章处理违约金', '保险上浮', 'ETC-客户未缴费用', 'ETC卡缺损费', 'ETC设备缺损费'];
+ return items.map(function (name, i) {
+ return {
+ key: 'bs-' + i,
+ seq: i + 1,
+ feeItem: name,
+ amount: i === 2 ? '100.00' : '0.00',
+ remark: '',
+ lastUpdateTime: '2026-02-27 10:20',
+ photos: [],
+ attachments: []
+ };
+ });
+ }, []);
+
+ var billInfo = useMemo(function () {
+ return {
+ receivedRent: '0.00',
+ actualRent: '0.00',
+ shouldRefundRent: '0.00'
+ };
+ }, []);
+
+ var energy = useMemo(function () {
+ return {
+ deliveryHydrogen: '85.00',
+ returnHydrogen: '72.00',
+ hydrogenUnitPrice: '35.00',
+ hydrogenSupplement: '455.00',
+ hydrogenFee: '0.00',
+ electricFee: '0.00',
+ prepayRefund: '0.00',
+ userBalance: '1200.00'
+ };
+ }, []);
+
+ // 运维部明细(只读样例)
+ var tireTreadList = useMemo(function () {
+ var unit = 25; // 元/mm
+ var rows = [
+ { key: 't1', name: '左前轮', deliveryDepth: '6.80', returnDepth: '6.20' },
+ { key: 't2', name: '左后轮(内)', deliveryDepth: '6.70', returnDepth: '6.00' },
+ { key: 't3', name: '左后轮(外)', deliveryDepth: '6.60', returnDepth: '6.10' },
+ { key: 't4', name: '右前轮', deliveryDepth: '6.90', returnDepth: '6.40' },
+ { key: 't5', name: '右后轮(内)', deliveryDepth: '6.50', returnDepth: '6.00' },
+ { key: 't6', name: '右后轮(外)', deliveryDepth: '6.80', returnDepth: '6.30' },
+ { key: 't7', name: '左中轮', deliveryDepth: '6.70', returnDepth: '6.20' },
+ { key: 't8', name: '右中轮', deliveryDepth: '6.60', returnDepth: '6.10' },
+ { key: 't9', name: '左后备位轮', deliveryDepth: '6.90', returnDepth: '6.50' },
+ { key: 't10', name: '右后备位轮', deliveryDepth: '6.40', returnDepth: '6.00' },
+ { key: 'spare', name: '备胎', deliveryDepth: '7.00', returnDepth: '7.00' }
+ ];
+ return rows.map(function (r) {
+ var d = parseFloat(r.deliveryDepth) || 0;
+ var rr = parseFloat(r.returnDepth) || 0;
+ var diff = Math.max(0, d - rr);
+ var total = diff * unit;
+ 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 operationRows = useMemo(function () {
+ var fixed = [
+ { name: '清洗费', amount: '0.00' },
+ { name: '未结算保养', amount: '372.50' },
+ { name: '未结算维修', amount: '0.00' },
+ { name: '车损费用', amount: '0.00' },
+ { name: '工具损坏丢失费用', amount: '0.00' },
+ { name: '证件丢失费用', amount: '0.00' },
+ { name: '广告损坏丢失费用', amount: '0.00' },
+ { name: '送车服务费', amount: '0.00' },
+ { name: '接车服务费', amount: '0.00' },
+ { name: '轮胎磨损费用', amount: '0.00' }
+ ];
+ return fixed.map(function (x, i) {
+ return { key: 'op-' + i, seq: i + 1, feeItem: x.name, amount: x.amount, worryFreeDiscount: '0.00', remark: '', lastUpdateTime: '2026-02-27 10:20' };
+ });
+ }, []);
+
+ // 安全组(只读示例)
+ 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 businessServiceTotal = useMemo(function () {
+ var sum = 0;
+ (businessServiceRows || []).forEach(function (r) { sum += (parseFloat(r.amount) || 0); });
+ return sum.toFixed(2);
+ }, [businessServiceRows]);
+ var operationTotal = useMemo(function () {
+ var sum = 0;
+ (operationRows || []).forEach(function (r) { sum += (parseFloat(r.amount) || 0); });
+ return sum.toFixed(2);
+ }, [operationRows]);
+ var energyTotal = useMemo(function () {
+ var a = parseFloat(energy.hydrogenSupplement) || 0;
+ var b = parseFloat(energy.hydrogenFee) || 0;
+ var c = parseFloat(energy.electricFee) || 0;
+ return (a + b + c).toFixed(2);
+ }, [energy.hydrogenSupplement, energy.hydrogenFee, energy.electricFee]);
+
+ var pendingSettle = useMemo(function () {
+ var bs = parseFloat(businessServiceTotal) || 0;
+ var rentRefund = parseFloat(billInfo.shouldRefundRent) || 0;
+ var op = parseFloat(operationTotal) || 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);
+ }, [businessServiceTotal, billInfo.shouldRefundRent, operationTotal, energy.hydrogenSupplement, energy.hydrogenFee, energy.electricFee, energy.prepayRefund]);
+
+ var depositAmount = '5000.00';
+ var refundTotal = useMemo(function () {
+ var diff = (parseFloat(depositAmount) || 0) - (parseFloat(pendingSettle) || 0);
+ return diff > 0 ? diff.toFixed(2) : '0.00';
+ }, [depositAmount, pendingSettle]);
+ var payTotal = useMemo(function () {
+ var diff = (parseFloat(depositAmount) || 0) - (parseFloat(pendingSettle) || 0);
+ return diff < 0 ? Math.abs(diff).toFixed(2) : '0.00';
+ }, [depositAmount, pendingSettle]);
+
+ 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: businessServiceTotal || '0.00' },
+ { key: 'rent', item: '业务服务组-车辆应退租金', amount: (toFixed2(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: operationTotal || '0.00' },
+ { key: 'total', item: '待结算总额', amount: pendingSettle || '0.00', strong: true }
+ ];
+ }, [businessServiceTotal, billInfo.shouldRefundRent, energy.hydrogenSupplement, energy.hydrogenFee, energy.electricFee, energy.prepayRefund, operationTotal, pendingSettle]);
+
+ var refundBreakdown = useMemo(function () {
+ return [
+ { key: 'd', item: '保证金总额', amount: (toFixed2(depositAmount) || '0.00') },
+ { key: 's', item: '待结算总额', amount: (pendingSettle || '0.00') },
+ { key: 'r', item: '应退还总额', amount: (refundTotal || '0.00'), strong: true }
+ ];
+ }, [depositAmount, pendingSettle, refundTotal]);
+
+ var payBreakdown = useMemo(function () {
+ return [
+ { key: 'd', item: '保证金总额', amount: (toFixed2(depositAmount) || '0.00') },
+ { key: 's', item: '待结算总额', amount: (pendingSettle || '0.00') },
+ { key: 'p', item: '应补缴总额', amount: (payTotal || '0.00'), strong: true }
+ ];
+ }, [depositAmount, pendingSettle, payTotal]);
+
+ 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 businessServiceColumns = useMemo(function () {
+ return [
+ { title: '序号', dataIndex: 'seq', key: 'seq', width: 60 },
+ { title: '费用项', dataIndex: 'feeItem', key: 'feeItem', width: 200, ellipsis: true },
+ { title: '金额', dataIndex: 'amount', key: 'amount', width: 140, render: function (v) { return React.createElement(Input, { value: v, addonAfter: '元', disabled: true }); } },
+ { title: '备注', dataIndex: 'remark', key: 'remark', render: function (v) { return React.createElement(TextArea, { value: v, rows: 1, disabled: true }); } },
+ { 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', disabled: true }, '上传'); } },
+ { title: '附件', key: 'attachment', width: 110, render: function () { return React.createElement(Button, { size: 'small', disabled: true }, '上传附件'); } }
+ ];
+ }, []);
+
+ var operationColumns = useMemo(function () {
+ return [
+ { title: '序号', dataIndex: 'seq', key: 'seq', width: 60 },
+ {
+ title: '费用项', dataIndex: 'feeItem', key: 'feeItem', width: 200,
+ render: function (v) {
+ 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: 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 || '-';
+ }
+ },
+ { title: '金额', dataIndex: 'amount', key: 'amount', width: 140, render: function (v) { return React.createElement(Input, { value: v, addonAfter: '元', disabled: true }); } },
+ {
+ 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) { return React.createElement(Input, { value: v, addonAfter: '元', disabled: true }); }
+ },
+ { title: '备注', dataIndex: 'remark', key: 'remark', render: function (v) { return React.createElement(TextArea, { value: v, rows: 1, disabled: true }); } },
+ { 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', disabled: true }, '上传'); } },
+ { title: '附件', key: 'attachment', width: 110, render: function () { return React.createElement(Button, { size: 'small', disabled: true }, '上传附件'); } }
+ ];
+ }, [tireTreadList]);
+
+ function goBack() {
+ message.info('返回还车应结款列表页(原型)');
+ try { if (window.history && window.history.length > 1) window.history.back(); } catch (e) {}
+ }
+
+ 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: '还车应结款' }, { title: '查看' }] })
+ ),
+
+ 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' } }, (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' } },
+ (pendingSettle || '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' } },
+ (refundTotal || '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' } },
+ (payTotal || '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, marginBottom: 12 } },
+ React.createElement('div', { style: { padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 12 } },
+ React.createElement('div', { style: { fontWeight: 600, fontSize: 14 } }, '业务服务组'),
+ React.createElement('div', { style: { flex: 1 } }),
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, color: '#666', fontSize: 13 } },
+ React.createElement('span', null, '总金额:', businessServiceTotal, ' 元'),
+ React.createElement('span', null, '提交人:业务服务组-张三'),
+ React.createElement('span', null, '状态:已提交')
+ )
+ ),
+ React.createElement('div', { style: { padding: '12px 16px', borderTop: '1px solid #f0f0f0' } },
+ 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 } }, '车辆实际租金'),
+ React.createElement(Input, { value: billInfo.actualRent, addonAfter: '元', disabled: true })
+ ),
+ React.createElement('div', null,
+ React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, '车辆应退租金'),
+ React.createElement(Input, { value: billInfo.shouldRefundRent, addonAfter: '元', disabled: true })
+ )
+ )
+ )
+ )
+ ),
+
+ React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, marginBottom: 12 } },
+ React.createElement('div', { style: { padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 12 } },
+ React.createElement('div', { style: { fontWeight: 600, fontSize: 14 } }, '能源采购组'),
+ React.createElement('div', { style: { flex: 1 } }),
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, color: '#666', fontSize: 13 } },
+ React.createElement('span', null, '总金额:', energyTotal, ' 元'),
+ React.createElement('span', null, '提交人:能源采购组-李四'),
+ React.createElement('span', null, '状态:已提交')
+ )
+ ),
+ React.createElement('div', { style: { padding: '12px 16px', borderTop: '1px solid #f0f0f0' } },
+ 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 } }, '氢量差补缴金额'),
+ React.createElement(Input, { value: energy.hydrogenSupplement, addonAfter: '元', disabled: true })
+ ),
+ 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 } }, '能源费补缴金额'),
+ 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, placeholder: '氢费补缴金额', addonAfter: '元', style: { width: '100%' }, disabled: true })),
+ React.createElement('div', null, React.createElement(Input, { value: energy.electricFee, placeholder: '电费补缴金额', addonAfter: '元', style: { width: '100%' }, disabled: true })),
+ 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 } }, '预付款退费金额'),
+ React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', gap: 12 } },
+ React.createElement('div', null,
+ React.createElement(Input, { value: energy.prepayRefund, placeholder: '0.00', addonAfter: '元', style: { width: '100%' }, disabled: true })
+ ),
+ 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('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, marginBottom: 12 } },
+ React.createElement('div', { style: { padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 12 } },
+ React.createElement('div', { style: { fontWeight: 600, fontSize: 14 } }, '运维部'),
+ React.createElement('div', { style: { flex: 1 } }),
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, color: '#666', fontSize: 13 } },
+ React.createElement('span', null, '总金额:', operationTotal, ' 元'),
+ React.createElement('span', null, '提交人:运维部-王五'),
+ React.createElement('span', null, '状态:已提交')
+ )
+ ),
+ React.createElement('div', { style: { padding: '12px 16px', borderTop: '1px solid #f0f0f0' } },
+ React.createElement(Table, { rowKey: 'key', columns: operationColumns, dataSource: operationRows, pagination: false, bordered: true, size: 'small' })
+ )
+ ),
+
+ React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, marginBottom: 12 } },
+ React.createElement('div', { style: { padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 12 } },
+ React.createElement('div', { style: { fontWeight: 600, fontSize: 14 } }, '安全组'),
+ React.createElement('div', { style: { flex: 1 } }),
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, color: '#666', fontSize: 13 } },
+ React.createElement('span', null, '提交人:安全组-赵六'),
+ React.createElement('span', null, '状态:已提交')
+ )
+ ),
+ React.createElement('div', { style: { padding: '12px 16px', borderTop: '1px solid #f0f0f0' } },
+ 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: 160, 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: 140, ellipsis: true },
+ { title: '事故类型', dataIndex: 'accidentType', key: 'accidentType', width: 110, ellipsis: true },
+ { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 140, 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, { onClick: goBack }, '返回')
+ )
+ );
+};
+
diff --git a/web端/财务管理/还车应结款-费用明细.jsx b/web端/财务管理/还车应结款-费用明细.jsx
new file mode 100644
index 0000000..65beb42
--- /dev/null
+++ b/web端/财务管理/还车应结款-费用明细.jsx
@@ -0,0 +1,1334 @@
+// 【重要】必须使用 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]);
+
+ // 轮胎胎纹明细(原型 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 === '证件丢失费用') 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)
+ )
+ );
+};
+
diff --git a/web端/财务管理/还车应结款.jsx b/web端/财务管理/还车应结款.jsx
new file mode 100644
index 0000000..1e75acf
--- /dev/null
+++ b/web端/财务管理/还车应结款.jsx
@@ -0,0 +1,2054 @@
+// 【重要】必须使用 const Component 作为组件变量名
+// 财务管理 - 还车应结款(列表页)
+
+const Component = function () {
+ var useState = React.useState;
+ var useMemo = React.useMemo;
+
+ var antd = window.antd;
+ var Breadcrumb = antd.Breadcrumb;
+ var Card = antd.Card;
+var Table = antd.Table;
+var Button = antd.Button;
+var Modal = antd.Modal;
+var Tooltip = antd.Tooltip;
+var Select = antd.Select;
+var DatePicker = antd.DatePicker;
+var message = antd.message;
+
+ var RangePicker = DatePicker.RangePicker;
+
+ var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' };
+ var cardStyle = { marginBottom: 16 };
+
+ function fmtYMD(d) {
+ var dd = d instanceof Date ? d : new Date();
+ var p2 = function (n) { return n < 10 ? '0' + n : '' + n; };
+ return dd.getFullYear() + '-' + p2(dd.getMonth() + 1) + '-' + p2(dd.getDate());
+ }
+
+ function fmtMoney(v) {
+ var n = typeof v === 'number' ? v : parseFloat(v);
+ if (isNaN(n)) n = 0;
+ return n.toFixed(2);
+ }
+
+ function filterOption(input, option) {
+ var label = (option && (option.label || option.children)) || '';
+ return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0;
+ }
+
+ function IconSubmitted(ok) {
+ return React.createElement('span', {
+ style: {
+ display: 'inline-block',
+ width: 10,
+ height: 10,
+ borderRadius: 999,
+ background: ok ? '#52c41a' : '#d9d9d9',
+ border: ok ? '1px solid #52c41a' : '1px solid #d9d9d9'
+ }
+ });
+ }
+
+ function SubmitCell(ok, name) {
+ return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6 } },
+ IconSubmitted(ok),
+ React.createElement('span', { style: { color: ok ? '#333' : '#999' } }, name || (ok ? '-' : '-'))
+ );
+ }
+
+ var approvalStatusOptions = useMemo(function () {
+ return [
+ { value: '待提交', label: '待提交' },
+ { value: '待审批', label: '待审批' },
+ { value: '审批中', label: '审批中' },
+ { value: '审批完成', label: '审批完成' },
+ { value: '审批驳回', label: '审批驳回' },
+ { value: '撤回', label: '撤回' }
+ ];
+ }, []);
+
+ var tableDataState = useState([
+ {
+ key: 'r1',
+ contractCode: 'LNZLHT20251106001',
+ submitSafety: { ok: true, name: '赵六' },
+ submitBusiness: { ok: true, name: '张三' },
+ submitOperation: { ok: false, name: '王五' },
+ submitEnergy: { ok: true, name: '李四' },
+ approvalStatus: '待提交',
+ customerName: '嘉兴某某物流有限公司',
+ projectName: '嘉兴腾4.5T租赁',
+ plateNo: '粤AGP5621',
+ businessDept: '业务一部',
+ businessOwner: '张经理',
+ deliveryTime: '2026-02-01',
+ returnTime: '2026-02-27',
+ returnPerson: '陈还车'
+ },
+ {
+ key: 'r2',
+ contractCode: 'LNZLHT20251201002',
+ submitSafety: { ok: true, name: '赵六' },
+ submitBusiness: { ok: true, name: '张三' },
+ submitOperation: { ok: true, name: '王五' },
+ submitEnergy: { ok: true, name: '李四' },
+ approvalStatus: '待审批',
+ customerName: '上海馨想事成物流有限公司',
+ projectName: '上海干线运输项目',
+ plateNo: '浙F03218F',
+ businessDept: '业务二部',
+ businessOwner: '李主管',
+ deliveryTime: '2026-01-10',
+ returnTime: '2026-02-20',
+ returnPerson: '周还车'
+ },
+ {
+ key: 'r3',
+ contractCode: 'ZYYGHT20260105003',
+ submitSafety: { ok: false, name: '' },
+ submitBusiness: { ok: false, name: '' },
+ submitOperation: { ok: false, name: '' },
+ submitEnergy: { ok: false, name: '' },
+ approvalStatus: '审批驳回',
+ customerName: '北京海龙运输有限公司',
+ projectName: '京津冀冷链项目',
+ plateNo: '京A29256F',
+ businessDept: '业务三部',
+ businessOwner: '王总',
+ deliveryTime: '2025-08-01',
+ returnTime: '2026-02-02',
+ returnPerson: '刘还车'
+ },
+ {
+ key: 'r4',
+ contractCode: 'LNZLHT20251106004',
+ submitSafety: { ok: true, name: '赵六' },
+ submitBusiness: { ok: false, name: '' },
+ submitOperation: { ok: true, name: '王五' },
+ submitEnergy: { ok: false, name: '' },
+ approvalStatus: '审批中',
+ customerName: '苏州某某快运有限公司',
+ projectName: '苏州城配项目',
+ plateNo: '苏E8K2P1',
+ businessDept: '业务一部',
+ businessOwner: '张经理',
+ deliveryTime: '2026-02-15',
+ returnTime: '2026-02-26',
+ returnPerson: '孙还车'
+ },
+ {
+ key: 'r5',
+ contractCode: 'LNZLHT20251106005',
+ submitSafety: { ok: true, name: '赵六' },
+ submitBusiness: { ok: true, name: '张三' },
+ submitOperation: { ok: true, name: '王五' },
+ submitEnergy: { ok: false, name: '' },
+ approvalStatus: '撤回',
+ customerName: '杭州某某物流有限公司',
+ projectName: '杭州港区项目',
+ plateNo: '浙A3M8Q7',
+ businessDept: '业务二部',
+ businessOwner: '李主管',
+ deliveryTime: '2026-02-05',
+ returnTime: '2026-02-25',
+ returnPerson: '吴还车'
+ },
+ {
+ key: 'r6',
+ contractCode: 'LNZLHT20251215006',
+ submitSafety: { ok: true, name: '赵六' },
+ submitBusiness: { ok: true, name: '张三' },
+ submitOperation: { ok: true, name: '王五' },
+ submitEnergy: { ok: true, name: '李四' },
+ approvalStatus: '审批完成',
+ customerName: '宁波某某供应链有限公司',
+ projectName: '宁波港区支线项目',
+ plateNo: '浙B6H9Q2',
+ businessDept: '业务三部',
+ businessOwner: '周经理',
+ deliveryTime: '2026-01-05',
+ returnTime: '2026-02-18',
+ returnPerson: '郑还车'
+ },
+ {
+ key: 'r7',
+ contractCode: 'LNZLHT20260108007',
+ submitSafety: { ok: true, name: '赵六' },
+ submitBusiness: { ok: false, name: '' },
+ submitOperation: { ok: false, name: '' },
+ submitEnergy: { ok: false, name: '' },
+ approvalStatus: '待提交',
+ customerName: '合肥某某物流有限公司',
+ projectName: '合肥城配项目',
+ plateNo: '皖A7K3P8',
+ businessDept: '业务一部',
+ businessOwner: '张经理',
+ deliveryTime: '2026-02-10',
+ returnTime: '2026-02-28',
+ returnPerson: '许还车'
+ },
+ {
+ key: 'r8',
+ contractCode: 'ZYYGHT20260112008',
+ submitSafety: { ok: true, name: '赵六' },
+ submitBusiness: { ok: true, name: '张三' },
+ submitOperation: { ok: true, name: '王五' },
+ submitEnergy: { ok: true, name: '李四' },
+ approvalStatus: '待审批',
+ customerName: '深圳某某快运有限公司',
+ projectName: '深莞干线项目',
+ plateNo: '粤B2P6M9',
+ businessDept: '业务二部',
+ businessOwner: '李主管',
+ deliveryTime: '2026-01-20',
+ returnTime: '2026-02-22',
+ returnPerson: '彭还车'
+ },
+ {
+ key: 'r9',
+ contractCode: 'LNZLHT20251130009',
+ submitSafety: { ok: true, name: '赵六' },
+ submitBusiness: { ok: true, name: '张三' },
+ submitOperation: { ok: true, name: '王五' },
+ submitEnergy: { ok: true, name: '李四' },
+ approvalStatus: '审批中',
+ customerName: '天津某某运输有限公司',
+ projectName: '津冀干线项目',
+ plateNo: '津A5R8L1',
+ businessDept: '业务三部',
+ businessOwner: '王总',
+ deliveryTime: '2026-02-02',
+ returnTime: '2026-02-27',
+ returnPerson: '韩还车'
+ },
+ {
+ key: 'r10',
+ contractCode: 'LNZLHT20251118010',
+ submitSafety: { ok: true, name: '赵六' },
+ submitBusiness: { ok: true, name: '张三' },
+ submitOperation: { ok: true, name: '王五' },
+ submitEnergy: { ok: false, name: '' },
+ approvalStatus: '审批驳回',
+ customerName: '武汉某某物流有限公司',
+ projectName: '武汉园区项目',
+ plateNo: '鄂A8D1Q6',
+ businessDept: '业务一部',
+ businessOwner: '周经理',
+ deliveryTime: '2026-01-15',
+ returnTime: '2026-02-16',
+ returnPerson: '蒋还车'
+ }
+ ]);
+ var tableDataAll = tableDataState[0];
+ var setTableDataAll = tableDataState[1];
+
+ var contractOptions = useMemo(function () {
+ var map = {};
+ tableDataAll.forEach(function (r) { map[r.contractCode] = true; });
+ return Object.keys(map).map(function (v) { return { value: v, label: v }; });
+ }, [tableDataAll]);
+ var customerOptions = useMemo(function () {
+ var map = {};
+ tableDataAll.forEach(function (r) { map[r.customerName] = true; });
+ return Object.keys(map).map(function (v) { return { value: v, label: v }; });
+ }, [tableDataAll]);
+ var projectOptions = useMemo(function () {
+ var map = {};
+ tableDataAll.forEach(function (r) { map[r.projectName] = true; });
+ return Object.keys(map).map(function (v) { return { value: v, label: v }; });
+ }, [tableDataAll]);
+ var plateOptions = useMemo(function () {
+ var map = {};
+ tableDataAll.forEach(function (r) { map[r.plateNo] = true; });
+ return Object.keys(map).map(function (v) { return { value: v, label: v }; });
+ }, [tableDataAll]);
+
+ var filtersState = useState({
+ contractCode: undefined,
+ customerName: undefined,
+ projectName: undefined,
+ plateNo: undefined,
+ returnDateRange: null,
+ approvalStatus: undefined
+ });
+ var filters = filtersState[0];
+ var setFilters = filtersState[1];
+var filterMoreOpenState = useState(false);
+
+ function resetFilters() {
+ setFilters({
+ contractCode: undefined,
+ customerName: undefined,
+ projectName: undefined,
+ plateNo: undefined,
+ returnDateRange: null,
+ approvalStatus: undefined
+ });
+ }
+
+ function inReturnRange(returnTimeStr, range) {
+ if (!range || !range[0] || !range[1]) return true;
+ var s = new Date(String(range[0]).replace(/-/g, '/'));
+ var e = new Date(String(range[1]).replace(/-/g, '/'));
+ var d = new Date(String(returnTimeStr || '').replace(/-/g, '/'));
+ if (isNaN(s.getTime()) || isNaN(e.getTime()) || isNaN(d.getTime())) return true;
+ var ms = d.getTime();
+ return ms >= s.getTime() && ms <= (e.getTime() + 24 * 60 * 60 * 1000 - 1);
+ }
+
+ var filteredData = useMemo(function () {
+ return (tableDataAll || []).filter(function (r) {
+ if (filters.contractCode && r.contractCode !== filters.contractCode) return false;
+ if (filters.customerName && r.customerName !== filters.customerName) return false;
+ if (filters.projectName && r.projectName !== filters.projectName) return false;
+ if (filters.plateNo && r.plateNo !== filters.plateNo) return false;
+ if (filters.approvalStatus && r.approvalStatus !== filters.approvalStatus) return false;
+ if (!inReturnRange(r.returnTime, filters.returnDateRange)) return false;
+ return true;
+ });
+ }, [tableDataAll, filters]);
+
+var billModalOpenState = useState(false);
+var billModalModeState = useState('export'); // generate | export
+var billRowState = useState(null);
+
+var requirementModalOpenState = useState(false);
+var requirementDocContent = useMemo(function () {
+ return (`一个「数字化资产ONEOS运管平台」中的「还车应结款」模块
+#面包屑:财务管理-还车应结款
+
+1.筛选;
+1.1.合同编号:选择器,支持输入框内输入合同编号模糊搜索下拉显示匹配项;
+1.2.客户名称:选择器,支持输入框内输入客户名称模糊搜索下拉显示匹配项;
+1.3.项目名称:选择器,支持输入框内输入项目名称模糊搜索下拉显示匹配项;
+1.4.车牌号:选择器,支持输入框内输入车牌号模糊搜索下拉显示匹配项;
+1.5.还车时间:日期选择器,支持单输入框双日历选择开始-结束日期,精确至日;
+1.6.审批状态:选择器,分为待提交、待审批、审批中、审批完成、审批驳回、撤回;
+
+2.还车应结款列表:右侧为导出;
+2.1.合同编号:显示合同编号;
+2.2.提交情况(合并表头):分为安全组、业务服务组、运维组、能源组,内容为各组提交人姓名,前方为已提交/未提交图标;
+2.3.审批状态:待提交、待审批、审批中、审批完成、审批驳回、撤回;
+ 2.3.1.待提交:未完成4部门提交也未提交审批;
+ 2.3.2.待审批:已提交审批,但没有任意节点进行审批;
+ 2.3.3.审批中:已提交审批,已有节点完成审批,但未完成最终节点审批;
+ 2.3.4.审批完成:已提交审批,并完成最终节点审批;
+ 2.3.5.审批驳回:已提交审批,但在任意节点被驳回;
+ 2.3.6.撤回:已提交审批,但在最终节点审批完成前主动撤回;
+2.4.客户名称:显示合同对应客户名称;
+2.5.项目名称:显示合同对应项目名称;
+2.6.车牌号:显示合同对应车牌号;
+2.7.业务部门:显示合同对应业务部门;
+2.8.业务负责人:显示合同对应业务负责人;
+2.9.交车时间:显示该车辆交车时间;
+2.10.还车时间:显示该车辆还车时间;
+2.11.还车人:显示该车辆还车人姓名;
+2.12.操作:查看、生成账单、费用明细、撤回;
+ 2.12.1.查看:点击跳转还车应结款-查看页面;
+ 2.12.2.生成账单:点击生成账单,弹框显示账单界面,审批状态为待审批的记录才可生成账单,其他状态生成账单隐藏;
+ 2.12.3.费用明细:点击跳转还车应结款-费用明细页面,审批状态为待审批、审批中、审批完成时,不显示费用明细;
+ 2.12.4.撤回:审批状态为审批中时,不显示费用明细点击二次确认,提示:是否确认撤回,点击确定后,提示:撤回成功,同时审批状态修改为撤回;
+2.13.右下角为分页符,支持单页查看数据条数;
+`);
+}, []);
+
+ var billDetail = useMemo(function () {
+ var row = billRowState[0] || (filteredData && filteredData[0]) || (tableDataAll && tableDataAll[0]) || {};
+ var customerName = row.customerName || '广州毅斌物流有限公司';
+ var plateNo = row.plateNo || '粤AGP6579';
+ var deliveryTime = row.deliveryTime || '2025-07-21';
+ var returnTime = row.returnTime || '2025-10-01';
+ var billDate = fmtYMD(new Date());
+
+ var feeRows = [
+ { item: '违章处理违约金', amount: '0.00' },
+ { item: '保险上浮', amount: '0.00' },
+ { item: 'ETC-客', amount: '100.00' },
+ { item: 'ETC卡缺损费', amount: '0.00' },
+ { item: 'ETC设备缺损费', amount: '0.00' },
+ { item: '租金', amount: '0.00' },
+ { item: '电费—客', amount: '0.00' },
+ { item: '氢气费—客', amount: '0.00' },
+ { item: '退还车氢量差', amount: '284.54' },
+ { item: '能源费补缴', amount: '0.00' },
+ { item: '能源费退款', amount: '0.00' },
+ { item: '清洗费', amount: '0.00' },
+ { item: '未结算保养费', amount: '372.50' },
+ { item: '未结算维修费', amount: '0.00' },
+ { item: '车损费', amount: '0.00' },
+ { item: '工具损坏或丢失费', amount: '0.00' },
+ { item: '证件费', amount: '0.00' },
+ { item: '广告损坏费', amount: '0.00' },
+ { item: '轮胎磨损费', amount: '0.00' }
+ ];
+ var total = feeRows.reduce(function (s, r) { return s + (parseFloat(r.amount) || 0); }, 0);
+ var depositPaid = 2000.00;
+ var refund = Math.max(0, depositPaid - total);
+ var pay = Math.max(0, total - depositPaid);
+
+ return {
+ customerName: customerName,
+ billDate: billDate,
+ vehicle: { seq: 1, plateNo: plateNo, deliveryTime: deliveryTime, returnTime: returnTime },
+ feeRows: feeRows,
+ total: fmtMoney(total),
+ depositPaid: fmtMoney(depositPaid),
+ pendingSettle: fmtMoney(total),
+ refund: fmtMoney(refund),
+ pay: fmtMoney(pay)
+ };
+ }, [billRowState[0], filteredData, tableDataAll]);
+
+ function openBillModal(mode, row) {
+ billModalModeState[1](mode);
+ billRowState[1](row || null);
+ billModalOpenState[1](true);
+ }
+
+ function printBill() {
+ try {
+ var el = document.getElementById('bill-print-area');
+ if (!el) { message.error('未找到可打印区域'); return; }
+ var w = window.open('', '_blank');
+ if (!w) { message.error('浏览器拦截了新窗口,请允许弹窗后重试'); return; }
+ var style = [
+ 'body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;color:#000;padding:24px;}',
+ 'table{width:100%;border-collapse:collapse;}',
+ 'th,td{border:1px solid #d9d9d9;padding:8px 10px;font-size:12px;vertical-align:top;}',
+ 'th{background:#0f6b2d;color:#fff;font-weight:600;}',
+ '.t-center{text-align:center;} .t-right{text-align:right;}',
+ '.muted{color:#666;}'
+ ].join('');
+ w.document.open();
+ w.document.write('
账单明细' + el.innerHTML + '');
+ w.document.close();
+ w.focus();
+ w.print();
+ } catch (e) {
+ message.error('打印失败(原型):' + (e && e.message ? e.message : '未知错误'));
+ }
+ }
+
+ var pageState = useState({ current: 1, pageSize: 20 });
+ var page = pageState[0];
+ var setPage = pageState[1];
+
+ useMemo(function () {
+ if (page.current !== 1) setPage(function (p) { return Object.assign({}, p, { current: 1 }); });
+ return null;
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [filters.contractCode, filters.customerName, filters.projectName, filters.plateNo, filters.approvalStatus, filters.returnDateRange]);
+
+ function handleView(row) {
+ message.info('跳转:还车应结款-查看(原型) 合同编号:' + (row && row.contractCode));
+ }
+ function handleFeeDetail(row) {
+ message.info('跳转:还车应结款-费用明细(原型) 合同编号:' + (row && row.contractCode));
+ }
+ function handleGenerateBill(row) {
+ openBillModal('generate', row);
+ }
+ function handleRevoke(row) {
+ Modal.confirm({
+ title: '撤回确认',
+ content: '是否确认撤回?',
+ okText: '确定',
+ cancelText: '取消',
+ onOk: function () {
+ setTableDataAll(function (p) {
+ return (p || []).map(function (r) {
+ if (!row || r.key !== row.key) return r;
+ var n = {}; for (var k in r) n[k] = r[k];
+ n.approvalStatus = '撤回';
+ return n;
+ });
+ });
+ message.success('撤回成功');
+ }
+ });
+ }
+
+ var columns = useMemo(function () {
+ return [
+ { title: '合同编号', dataIndex: 'contractCode', key: 'contractCode', width: 170, fixed: 'left', ellipsis: true },
+ {
+ title: '提交情况',
+ key: 'submitGroup',
+ children: [
+ {
+ title: '安全组',
+ key: 'submitSafety',
+ width: 140,
+ render: function (_, r) { return SubmitCell(r.submitSafety && r.submitSafety.ok, r.submitSafety && r.submitSafety.name); }
+ },
+ {
+ title: '业务服务组',
+ key: 'submitBusiness',
+ width: 140,
+ render: function (_, r) { return SubmitCell(r.submitBusiness && r.submitBusiness.ok, r.submitBusiness && r.submitBusiness.name); }
+ },
+ {
+ title: '运维组',
+ key: 'submitOperation',
+ width: 140,
+ render: function (_, r) { return SubmitCell(r.submitOperation && r.submitOperation.ok, r.submitOperation && r.submitOperation.name); }
+ },
+ {
+ title: '能源组',
+ key: 'submitEnergy',
+ width: 140,
+ render: function (_, r) { return SubmitCell(r.submitEnergy && r.submitEnergy.ok, r.submitEnergy && r.submitEnergy.name); }
+ }
+ ]
+ },
+ { title: '审批状态', dataIndex: 'approvalStatus', key: 'approvalStatus', width: 110 },
+ { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 180, ellipsis: true },
+ { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 160, ellipsis: true },
+ { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 110 },
+ { title: '业务部门', dataIndex: 'businessDept', key: 'businessDept', width: 110 },
+ { title: '业务负责人', dataIndex: 'businessOwner', key: 'businessOwner', width: 110 },
+ { title: '交车时间', dataIndex: 'deliveryTime', key: 'deliveryTime', width: 110 },
+ { title: '还车时间', dataIndex: 'returnTime', key: 'returnTime', width: 110 },
+ { title: '还车人', dataIndex: 'returnPerson', key: 'returnPerson', width: 110 },
+ {
+ title: '操作',
+ key: 'action',
+ width: 220,
+ fixed: 'right',
+ render: function (_, r) {
+ var st = String(r && r.approvalStatus);
+ var showFeeDetail = !(st === '待审批' || st === '审批中' || st === '审批完成');
+ var showRevoke = String(r && r.approvalStatus) === '审批中';
+ var showGenerateBill = String(r && r.approvalStatus) === '待审批';
+ return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 8 } },
+ React.createElement(Button, { type: 'link', size: 'small', onClick: function () { handleView(r); } }, '查看'),
+ showGenerateBill ? React.createElement(Button, { type: 'link', size: 'small', onClick: function () { handleGenerateBill(r); } }, '生成账单') : null,
+ showFeeDetail ? React.createElement(Button, { type: 'link', size: 'small', onClick: function () { handleFeeDetail(r); } }, '费用明细') : null,
+ showRevoke ? React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function () { handleRevoke(r); } }, '撤回') : null
+ );
+ }
+ }
+ ];
+ }, []);
+
+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('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(Select, {
+ value: filters.contractCode,
+ options: contractOptions,
+ allowClear: true,
+ placeholder: '请输入或选择合同编号',
+ showSearch: true,
+ filterOption: filterOption,
+ style: { width: '100%' },
+ onChange: function (v) { setFilters(function (p) { return Object.assign({}, p, { contractCode: v }); }); }
+ })
+ ),
+ React.createElement('div', null,
+ React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, '客户名称'),
+ React.createElement(Select, {
+ value: filters.customerName,
+ options: customerOptions,
+ allowClear: true,
+ placeholder: '请输入或选择客户名称',
+ showSearch: true,
+ filterOption: filterOption,
+ style: { width: '100%' },
+ onChange: function (v) { setFilters(function (p) { return Object.assign({}, p, { customerName: v }); }); }
+ })
+ ),
+ React.createElement('div', null,
+ React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, '项目名称'),
+ React.createElement(Select, {
+ value: filters.projectName,
+ options: projectOptions,
+ allowClear: true,
+ placeholder: '请输入或选择项目名称',
+ showSearch: true,
+ filterOption: filterOption,
+ style: { width: '100%' },
+ onChange: function (v) { setFilters(function (p) { return Object.assign({}, p, { projectName: v }); }); }
+ })
+ )
+ ,
+ filterMoreOpenState[0] ? React.createElement(React.Fragment, null,
+ React.createElement('div', null,
+ React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, '车牌号'),
+ React.createElement(Select, {
+ value: filters.plateNo,
+ options: plateOptions,
+ allowClear: true,
+ placeholder: '请输入或选择车牌号',
+ showSearch: true,
+ filterOption: filterOption,
+ style: { width: '100%' },
+ onChange: function (v) { setFilters(function (p) { return Object.assign({}, p, { plateNo: v }); }); }
+ })
+ ),
+ React.createElement('div', null,
+ React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, '还车时间'),
+ React.createElement(RangePicker, {
+ value: filters.returnDateRange,
+ allowClear: true,
+ style: { width: '100%' },
+ placeholder: ['请选择开始日期', '请选择结束日期'],
+ onChange: function (v) { setFilters(function (p) { return Object.assign({}, p, { returnDateRange: v }); }); }
+ })
+ ),
+ React.createElement('div', null,
+ React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, '审批状态'),
+ React.createElement(Select, {
+ value: filters.approvalStatus,
+ options: approvalStatusOptions,
+ allowClear: true,
+ placeholder: '请选择审批状态',
+ style: { width: '100%' },
+ onChange: function (v) { setFilters(function (p) { return Object.assign({}, p, { approvalStatus: v }); }); }
+ })
+ )
+ ) : null
+ ),
+ React.createElement('div', { style: { marginTop: 12, display: 'flex', justifyContent: 'flex-end', gap: 8 } },
+ React.createElement(Button, { type: 'link', onClick: function () { filterMoreOpenState[1](!filterMoreOpenState[0]); } }, filterMoreOpenState[0] ? '收起' : '展开'),
+ React.createElement(Button, { onClick: resetFilters }, '重置'),
+ React.createElement(Button, { type: 'primary', onClick: function () { message.success('已查询(原型)'); } }, '查询')
+ )
+ ),
+
+ React.createElement(Card, {
+ title: '还车应结款列表',
+ style: cardStyle,
+ extra: React.createElement('div', { style: { display: 'inline-flex', alignItems: 'center', gap: 8 } },
+ React.createElement(Button, { onClick: function () { openBillModal('export', (filteredData && filteredData[0]) || null); } }, '导出')
+ )
+ },
+ React.createElement(Table, {
+ rowKey: 'key',
+ columns: columns,
+ dataSource: filteredData,
+ bordered: true,
+ size: 'middle',
+ scroll: { x: 1650 },
+ pagination: {
+ current: page.current,
+ pageSize: page.pageSize,
+ showSizeChanger: true,
+ pageSizeOptions: ['20', '50', '100'],
+ showQuickJumper: true,
+ total: (filteredData || []).length,
+ showTotal: function (t) { return '共 ' + t + ' 条'; },
+ onChange: function (c, s) { setPage({ current: c, pageSize: s }); }
+ }
+ })
+ ),
+
+ React.createElement(Modal, {
+ open: billModalOpenState[0],
+ onCancel: function () { billModalOpenState[1](false); },
+ footer: React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } },
+ React.createElement(Button, { type: 'primary', onClick: printBill }, '打印'),
+ React.createElement(Button, { onClick: function () { billModalOpenState[1](false); } }, '返回')
+ ),
+ title: (billModalModeState[0] === 'generate' ? '生成账单' : '导出账单'),
+ width: 980
+ },
+ React.createElement('div', { id: 'bill-print-area' },
+ React.createElement('div', { style: { fontSize: 18, fontWeight: 700, textAlign: 'center', marginBottom: 18 } }, '车辆退车结算明细'),
+ React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', marginBottom: 12, fontSize: 13 } },
+ React.createElement('div', null, React.createElement('span', { style: { fontWeight: 600 } }, '客户名称:'), billDetail.customerName),
+ React.createElement('div', null, React.createElement('span', { style: { fontWeight: 600 } }, '账单生成日期:'), billDetail.billDate)
+ ),
+ React.createElement('table', null,
+ React.createElement('thead', null,
+ React.createElement('tr', null,
+ React.createElement('th', { className: 't-center', style: { width: 60 } }, '序号'),
+ React.createElement('th', { className: 't-center', style: { width: 120 } }, '车牌'),
+ React.createElement('th', { className: 't-center', style: { width: 120 } }, '交车日期'),
+ React.createElement('th', { className: 't-center', style: { width: 120 } }, '退车日期'),
+ React.createElement('th', { className: 't-center' }, '费用科目'),
+ React.createElement('th', { className: 't-center', style: { width: 140 } }, '待结算费用')
+ )
+ ),
+ React.createElement('tbody', null,
+ (billDetail.feeRows || []).map(function (r, idx) {
+ var rowspan = (billDetail.feeRows || []).length + 1;
+ return React.createElement('tr', { key: 'fee-' + idx },
+ idx === 0 ? React.createElement('td', { rowSpan: rowspan, className: 't-center' }, String(billDetail.vehicle.seq || 1)) : null,
+ idx === 0 ? React.createElement('td', { rowSpan: rowspan, className: 't-center' }, billDetail.vehicle.plateNo) : null,
+ idx === 0 ? React.createElement('td', { rowSpan: rowspan, className: 't-center' }, billDetail.vehicle.deliveryTime) : null,
+ idx === 0 ? React.createElement('td', { rowSpan: rowspan, className: 't-center' }, billDetail.vehicle.returnTime) : null,
+ React.createElement('td', null, r.item),
+ React.createElement('td', { className: 't-right' }, fmtMoney(r.amount))
+ );
+ }),
+ React.createElement('tr', { key: 'fee-total', style: { background: '#f6f7f8' } },
+ React.createElement('td', { style: { fontWeight: 700 } }, '合计'),
+ React.createElement('td', { className: 't-right', style: { fontWeight: 700 } }, billDetail.total)
+ )
+ )
+ ),
+ React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', rowGap: 10, marginTop: 18, fontSize: 13 } },
+ React.createElement('div', null, React.createElement('span', { style: { fontWeight: 600 } }, '已支付保证金:'), billDetail.depositPaid),
+ React.createElement('div', null, React.createElement('span', { style: { fontWeight: 600 } }, '待结算费用:'), billDetail.pendingSettle),
+ React.createElement('div', null, React.createElement('span', { style: { fontWeight: 600 } }, '应退款:'), billDetail.refund),
+ React.createElement('div', null, React.createElement('span', { style: { fontWeight: 600 } }, '应缴纳:'), billDetail.pay)
+ )
+ )
+ ),
+ 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)
+ )
+ );
+};
+
+// 【重要】必须使用 const Component 作为组件变量名
+// 财务管理 - 还车应结款
+
+// (历史残留)费用明细页代码误追加到本文件,为避免重复声明 Component,改名保留但不再作为入口组件使用
+const ComponentFeeDetail = 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]);
+
+ // 轮胎胎纹明细(原型 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 === '证件丢失费用') 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)
+ )
+ );
+};
+
diff --git a/web端/车辆管理.jsx b/web端/车辆管理.jsx
index 66df30c..c820ecf 100644
--- a/web端/车辆管理.jsx
+++ b/web端/车辆管理.jsx
@@ -1,676 +1,676 @@
-// 【重要】必须使用 const Component 作为组件变量名
-// 车辆资产管理 - 车辆管理模块(中后台 Ant Design 原型)
-const Component = function () {
- var useState = React.useState;
- var useMemo = React.useMemo;
- var useCallback = React.useCallback;
- var antd = window.antd;
-
- var Breadcrumb = antd.Breadcrumb;
- var Cascader = antd.Cascader;
- var Select = antd.Select;
- var Input = antd.Input;
- var Button = antd.Button;
- var Table = antd.Table;
- var Space = antd.Space;
- var Dropdown = antd.Dropdown;
- var Modal = antd.Modal;
- var Upload = antd.Upload;
- var Card = antd.Card;
- var Tabs = antd.Tabs;
- var message = antd.message;
- var App = antd.App;
-
- // 筛选项状态
- var _region = useState([]);
- var _vehicleType = useState(undefined);
- var _brand = useState(undefined);
- var _model = useState(undefined);
- var _customer = useState(undefined);
- var _department = useState(undefined);
- var _contractNo = useState(undefined);
- var _ownership = useState(undefined);
-
- var _plateFilter = useState('');
- var _selectedRowKeys = useState([]);
- var _uploadModalVisible = useState(false);
- var _confirmModalVisible = useState(false);
- var _ocrLoadingVisible = useState(false);
- var _currentRow = useState(null);
- var _plateForm = useState({ vin: '', plateNo: '' });
- var _plateError = useState('');
- var _detailRecord = useState(null);
- var _detailCardExpanded = useState(false);
- var _detailTab = useState('型号参数');
- var _filterExpanded = useState(false);
- var _requirementModalVisible = useState(false);
- var _inspectImportModalVisible = useState(false);
- var _inspectImportResult = useState(null);
-
- // 省-市 地区数据(示例)
- var regionOptions = [
- { value: 'guangdong', label: '广东省', children: [{ value: 'guangzhou', label: '广州市' }, { value: 'shenzhen', label: '深圳市' }] },
- { value: 'beijing', label: '北京市', children: [{ value: 'beijing', label: '北京市' }] },
- { value: 'shanghai', label: '上海市', children: [{ value: 'shanghai', label: '上海市' }] }
- ];
-
- // 车辆类型、品牌、型号、客户、部门、合同、登记所有权(模拟下拉数据)
- var vehicleTypeOptions = [{ label: '小型轿车', value: 'type1' }, { label: 'SUV', value: 'type2' }, { label: '厢式货车', value: 'type3' }];
- var brandOptions = [{ label: '比亚迪', value: 'byd' }, { label: '特斯拉', value: 'tsl' }, { label: '蔚来', value: 'nio' }];
- var modelOptions = [{ label: '汉EV', value: 'han' }, { label: 'Model 3', value: 'm3' }, { label: 'ET5', value: 'et5' }];
- var customerOptions = [{ label: '无', value: 'none' }, { label: '客户A', value: 'c1' }, { label: '客户B', value: 'c2' }];
- var departmentOptions = [{ label: '无', value: 'none' }, { label: '华南区', value: 'd1' }, { label: '华东区', value: 'd2' }];
- var contractOptions = [{ label: 'HT-2024-001', value: 'HT-2024-001' }, { label: 'HT-2024-002', value: 'HT-2024-002' }];
- var ownershipOptions = [{ label: '某某租赁公司', value: 'o1' }, { label: '某某科技有限公司', value: 'o2' }];
-
- // 表格数据(模拟 20 条,状态按产品枚举)
- // 运营状态: 待运营、库存、租赁、自营、退出运营
- // 库位状态: 新车入库-待验车、新车入库-证照办理、库存车-可交付车、库存车-不可交付车、库存车-呆滞车、已交付车-租赁交车、已交付车-自营交车、已交付车-替换交车、退出运营-报废车、退出运营-三方退租车、退出运营-过户售车
- // 出库状态: 异动出库、调拨出库、展示出库、租赁交车、自营交车、替换交车、过户售车、外租退车、报废出库、无
- // 预占状态: 331 后迭代,暂用「未预占」
- // 整备状态: 待整备、整备中、正常、无
- // 过户状态: 过户中、内部过户完成、销售过户完成、无
- // 维修状态: 待服务站接单、维修中、正常
- // 证照状态: 正常、异常
- // 报废状态: 报废中、已报废、无
- var rawData = [
- { id: '1', region: '广东省/广州市', vin: 'LGWEF4A59NS123456', plateNo: '粤A12345', vehicleNo: 'V001', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '白色', parking: '天河停车场', customer: '客户A', department: '华南区', manager: '张三', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '12580.50', purchaseDate: '2023-06-15', regDate: '2023-07-01', inspectExpire: '2025-07', lastDeliveryTime: '2024-01-10', lastDeliveryMile: '12000.00', lastReturnTime: '2024-02-01', lastReturnMile: '12580.50', scrapDate: '2038-12-31', contractNo: 'HT-2024-001', location: '广东省广州市天河区天河路100号', gpsTime: '2024-02-12 14:30' },
- { id: '2', region: '广东省/深圳市', vin: 'LGWEF4A59NS789012', plateNo: '粤B67890', vehicleNo: '-', vehicleType: 'SUV', brand: '特斯拉', model: 'Model 3', color: '黑色', parking: '-', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '库存', storageStatus: '库存车-可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '待整备', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2022', mileage: '25600.00', purchaseDate: '2022-08-20', regDate: '2022-09-01', inspectExpire: '2024-09', lastDeliveryTime: '2024-01-05', lastDeliveryMile: '25500.00', lastReturnTime: '2024-01-20', lastReturnMile: '25600.00', scrapDate: '2037-09-30', contractNo: '-', location: '广东省深圳市南山区科技园南路', gpsTime: '2024-02-11 09:00' },
- { id: '3', region: '广东省/广州市', vin: 'LSJA24U70PS001234', plateNo: '粤A88K88', vehicleNo: 'V003', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '灰色', parking: '黄埔停车场', customer: '客户A', department: '华南区', manager: '王五', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '8320.00', purchaseDate: '2023-09-01', regDate: '2023-09-20', inspectExpire: '2025-09', lastDeliveryTime: '2024-02-05', lastDeliveryMile: '8100.00', lastReturnTime: '2024-02-10', lastReturnMile: '8320.00', scrapDate: '2039-09-30', contractNo: 'HT-2024-002', location: '广东省广州市黄埔区开泰大道200号', gpsTime: '2024-02-12 10:15' },
- { id: '4', region: '北京市/北京市', vin: 'WVWZZZ3CZWE123456', plateNo: '京C12345', vehicleNo: '-', vehicleType: 'SUV', brand: '特斯拉', model: 'Model Y', color: '蓝色', parking: '朝阳停车场', customer: '无', department: '无', manager: '-', operateStatus: '自营', storageStatus: '已交付车-自营交车', outStatus: '自营交车', preemptStatus: '未预占', prepareStatus: '整备中', transferStatus: '无', repairStatus: '维修中', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2022', mileage: '18900.25', purchaseDate: '2022-11-10', regDate: '2022-12-01', inspectExpire: '2024-12', lastDeliveryTime: '2024-01-15', lastDeliveryMile: '18500.00', lastReturnTime: '2024-01-28', lastReturnMile: '18900.25', scrapDate: '2037-12-31', contractNo: '-', location: '北京市朝阳区望京街88号', gpsTime: '2024-02-10 16:00' },
- { id: '5', region: '上海市/上海市', vin: 'LSVAU2BR3NS567890', plateNo: '沪D66666', vehicleNo: 'V005', vehicleType: '厢式货车', brand: '比亚迪', model: '汉EV', color: '白色', parking: '-', customer: '客户C', department: '华东区', manager: '赵六', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '异常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2021', mileage: '45200.80', purchaseDate: '2021-05-20', regDate: '2021-06-15', inspectExpire: '2024-06', lastDeliveryTime: '2024-02-01', lastDeliveryMile: '44800.00', lastReturnTime: '2024-02-08', lastReturnMile: '45200.80', scrapDate: '2036-06-30', contractNo: 'HT-2024-003', location: '上海市浦东新区张江高科路500号', gpsTime: '2024-02-12 09:45' },
- { id: '6', region: '广东省/深圳市', vin: '5YJ3E1EA1NF123456', plateNo: '粤B12345', vehicleNo: '-', vehicleType: '小型轿车', brand: '特斯拉', model: 'Model 3', color: '红色', parking: '福田停车场', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '5600.00', purchaseDate: '2023-03-08', regDate: '2023-04-01', inspectExpire: '2025-04', lastDeliveryTime: '2024-02-02', lastDeliveryMile: '5200.00', lastReturnTime: '2024-02-11', lastReturnMile: '5600.00', scrapDate: '2038-04-30', contractNo: 'HT-2024-004', location: '广东省深圳市福田区福华路188号', gpsTime: '2024-02-12 11:20' },
- { id: '7', region: '广东省/广州市', vin: 'LGWEF4A59NS234567', plateNo: '粤A99A99', vehicleNo: 'V007', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '黑色', parking: '天河停车场', customer: '客户A', department: '华南区', manager: '张三', operateStatus: '库存', storageStatus: '库存车-不可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2022', mileage: '22100.30', purchaseDate: '2022-07-15', regDate: '2022-08-01', inspectExpire: '2024-08', lastDeliveryTime: '2024-01-20', lastDeliveryMile: '21800.00', lastReturnTime: '2024-02-05', lastReturnMile: '22100.30', scrapDate: '2037-08-31', contractNo: '-', location: '广东省广州市天河区体育西路200号', gpsTime: '2024-02-11 18:30' },
- { id: '8', region: '北京市/北京市', vin: 'WVWZZZ3CZWE789012', plateNo: '京E88888', vehicleNo: '-', vehicleType: 'SUV', brand: '蔚来', model: 'ET5', color: '绿色', parking: '-', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '自营', storageStatus: '已交付车-自营交车', outStatus: '自营交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2023', mileage: '11200.00', purchaseDate: '2023-01-12', regDate: '2023-02-01', inspectExpire: '2025-02', lastDeliveryTime: '2024-02-06', lastDeliveryMile: '11000.00', lastReturnTime: '2024-02-12', lastReturnMile: '11200.00', scrapDate: '2038-02-28', contractNo: 'HT-2024-005', location: '北京市海淀区中关村大街1号', gpsTime: '2024-02-12 08:00' },
- { id: '9', region: '上海市/上海市', vin: 'LSVAU2BR3NS111222', plateNo: '沪A12345', vehicleNo: '-', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '银色', parking: '浦东停车场', customer: '无', department: '无', manager: '-', operateStatus: '库存', storageStatus: '库存车-呆滞车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '待整备', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2021', mileage: '38500.60', purchaseDate: '2021-10-05', regDate: '2021-11-01', inspectExpire: '2024-11', lastDeliveryTime: '2024-01-08', lastDeliveryMile: '38000.00', lastReturnTime: '2024-01-25', lastReturnMile: '38500.60', scrapDate: '2036-11-30', contractNo: '-', location: '上海市徐汇区漕溪路250号', gpsTime: '2024-02-09 14:00' },
- { id: '10', region: '广东省/深圳市', vin: '5YJ3E1EA2NF333444', plateNo: '粤B55B55', vehicleNo: 'V010', vehicleType: 'SUV', brand: '特斯拉', model: 'Model Y', color: '白色', parking: '南山停车场', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2022', mileage: '16800.00', purchaseDate: '2022-04-18', regDate: '2022-05-10', inspectExpire: '2024-05', lastDeliveryTime: '2024-01-30', lastDeliveryMile: '16500.00', lastReturnTime: '2024-02-09', lastReturnMile: '16800.00', scrapDate: '2037-05-31', contractNo: 'HT-2024-006', location: '广东省深圳市南山区后海大道300号', gpsTime: '2024-02-12 13:10' },
- { id: '11', region: '广东省/广州市', vin: 'LSJA24U70PS555666', plateNo: '粤A11B22', vehicleNo: '-', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '蓝色', parking: '番禺停车场', customer: '客户A', department: '华南区', manager: '王五', operateStatus: '租赁', storageStatus: '已交付车-替换交车', outStatus: '替换交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '7200.50', purchaseDate: '2023-07-20', regDate: '2023-08-05', inspectExpire: '2025-08', lastDeliveryTime: '2024-02-03', lastDeliveryMile: '7000.00', lastReturnTime: '2024-02-11', lastReturnMile: '7200.50', scrapDate: '2038-08-31', contractNo: 'HT-2024-007', location: '广东省广州市番禺区市桥街100号', gpsTime: '2024-02-12 12:00' },
- { id: '12', region: '北京市/北京市', vin: 'WVWZZZ3CZWE333555', plateNo: '京F33333', vehicleNo: '-', vehicleType: '厢式货车', brand: '比亚迪', model: '汉EV', color: '灰色', parking: '大兴停车场', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '库存', storageStatus: '库存车-不可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '待整备', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2020', mileage: '52100.00', purchaseDate: '2020-09-01', regDate: '2020-10-01', inspectExpire: '2024-10', lastDeliveryTime: '2024-01-12', lastDeliveryMile: '51800.00', lastReturnTime: '2024-01-30', lastReturnMile: '52100.00', scrapDate: '2035-10-31', contractNo: '-', location: '北京市大兴区亦庄经济开发区', gpsTime: '2024-02-08 11:00' },
- { id: '13', region: '上海市/上海市', vin: 'LSVAU2BR3NS777888', plateNo: '沪B99999', vehicleNo: 'V013', vehicleType: '小型轿车', brand: '特斯拉', model: 'Model 3', color: '黑色', parking: '-', customer: '客户C', department: '华东区', manager: '赵六', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '内部过户完成', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2022', mileage: '19800.00', purchaseDate: '2022-12-01', regDate: '2023-01-05', inspectExpire: '2025-01', lastDeliveryTime: '2024-02-07', lastDeliveryMile: '19500.00', lastReturnTime: '2024-02-12', lastReturnMile: '19800.00', scrapDate: '2038-01-31', contractNo: 'HT-2024-008', location: '上海市闵行区莘庄镇莘松路88号', gpsTime: '2024-02-12 15:45' },
- { id: '14', region: '广东省/深圳市', vin: 'LGWEF4A59NS888999', plateNo: '粤B22C33', vehicleNo: '-', vehicleType: 'SUV', brand: '比亚迪', model: '汉EV', color: '白色', parking: '龙岗停车场', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '4200.00', purchaseDate: '2023-10-10', regDate: '2023-11-01', inspectExpire: '2025-11', lastDeliveryTime: '2024-02-04', lastDeliveryMile: '4000.00', lastReturnTime: '2024-02-10', lastReturnMile: '4200.00', scrapDate: '2038-11-30', contractNo: 'HT-2024-009', location: '广东省深圳市龙岗区龙城大道500号', gpsTime: '2024-02-12 10:30' },
- { id: '15', region: '广东省/广州市', vin: 'LSJA24U70PS000111', plateNo: '粤A66D66', vehicleNo: 'V015', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '红色', parking: '天河停车场', customer: '无', department: '无', manager: '-', operateStatus: '待运营', storageStatus: '新车入库-待验车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '无', transferStatus: '无', repairStatus: '正常', licenseStatus: '异常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2021', mileage: '31200.40', purchaseDate: '2021-03-15', regDate: '2021-04-01', inspectExpire: '2024-04', lastDeliveryTime: '2024-01-18', lastDeliveryMile: '30800.00', lastReturnTime: '2024-02-02', lastReturnMile: '31200.40', scrapDate: '2036-04-30', contractNo: '-', location: '广东省广州市越秀区中山五路66号', gpsTime: '2024-02-07 09:00' },
- { id: '16', region: '北京市/北京市', vin: '5YJ3E1EA3NF222333', plateNo: '京G12345', vehicleNo: '-', vehicleType: '小型轿车', brand: '特斯拉', model: 'Model 3', color: '银色', parking: '西城停车场', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '9800.00', purchaseDate: '2023-05-01', regDate: '2023-05-20', inspectExpire: '2025-05', lastDeliveryTime: '2024-02-01', lastDeliveryMile: '9500.00', lastReturnTime: '2024-02-11', lastReturnMile: '9800.00', scrapDate: '2038-05-31', contractNo: 'HT-2024-010', location: '北京市西城区金融街28号', gpsTime: '2024-02-12 14:00' },
- { id: '17', region: '上海市/上海市', vin: 'LGWEF4A59NS444555', plateNo: '沪C11111', vehicleNo: '-', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '黑色', parking: '虹口停车场', customer: '客户C', department: '华东区', manager: '赵六', operateStatus: '库存', storageStatus: '库存车-可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2022', mileage: '24500.00', purchaseDate: '2022-02-28', regDate: '2022-03-15', inspectExpire: '2024-03', lastDeliveryTime: '2024-01-22', lastDeliveryMile: '24200.00', lastReturnTime: '2024-02-06', lastReturnMile: '24500.00', scrapDate: '2037-03-31', contractNo: '-', location: '上海市虹口区四川北路1688号', gpsTime: '2024-02-11 17:00' },
- { id: '18', region: '广东省/深圳市', vin: 'LSVAU2BR3NS666777', plateNo: '粤B44E44', vehicleNo: 'V018', vehicleType: '厢式货车', brand: '比亚迪', model: '汉EV', color: '灰色', parking: '-', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '待服务站接单', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2020', mileage: '67800.25', purchaseDate: '2020-06-10', regDate: '2020-07-01', inspectExpire: '2024-07', lastDeliveryTime: '2024-01-25', lastDeliveryMile: '67500.00', lastReturnTime: '2024-02-09', lastReturnMile: '67800.25', scrapDate: '2035-07-31', contractNo: 'HT-2024-011', location: '广东省深圳市宝安区新安街道创业路', gpsTime: '2024-02-12 08:20' },
- { id: '19', region: '广东省/广州市', vin: '5YJ3E1EA4NF888999', plateNo: '粤A77F77', vehicleNo: '-', vehicleType: 'SUV', brand: '特斯拉', model: 'Model Y', color: '蓝色', parking: '白云停车场', customer: '客户A', department: '华南区', manager: '张三', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '过户中', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '3500.00', purchaseDate: '2023-11-05', regDate: '2023-11-25', inspectExpire: '2025-11', lastDeliveryTime: '2024-02-08', lastDeliveryMile: '3200.00', lastReturnTime: '2024-02-12', lastReturnMile: '3500.00', scrapDate: '2038-11-30', contractNo: 'HT-2024-012', location: '广东省广州市白云区白云大道南888号', gpsTime: '2024-02-12 16:00' },
- { id: '20', region: '北京市/北京市', vin: 'LSJA24U70PS999000', plateNo: '京H88888', vehicleNo: '-', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '白色', parking: '昌平停车场', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '退出运营', storageStatus: '退出运营-报废车', outStatus: '报废出库', preemptStatus: '未预占', prepareStatus: '无', transferStatus: '过户中', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '报废中', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2022', mileage: '15600.00', purchaseDate: '2022-06-20', regDate: '2022-07-10', inspectExpire: '2024-07', lastDeliveryTime: '2024-01-10', lastDeliveryMile: '15300.00', lastReturnTime: '2024-01-28', lastReturnMile: '15600.00', scrapDate: '2037-07-31', contractNo: '-', location: '北京市昌平区回龙观西大街100号', gpsTime: '2024-02-10 12:30' }
- ];
-
- var dataSource = useMemo(function () {
- var plate = _plateFilter[0];
- if (!plate || plate.trim() === '') return rawData;
- return rawData.filter(function (row) {
- return row.plateNo && row.plateNo.indexOf(plate) !== -1;
- });
- }, [rawData, _plateFilter[0]]);
-
- var onPlateFilterChange = useCallback(function (e) {
- _plateFilter[1](e.target.value);
- }, []);
-
- var onSelectChange = useCallback(function (keys) {
- _selectedRowKeys[1](keys);
- }, []);
-
- var rowSelection = {
- selectedRowKeys: _selectedRowKeys[0],
- onChange: onSelectChange
- };
-
- var handleExport = useCallback(function () {
- if (_selectedRowKeys[0].length === 0) {
- message.warning('请先勾选要导出的记录');
- return;
- }
- message.success('导出功能(联动多选):已选 ' + _selectedRowKeys[0].length + ' 条');
- }, []);
-
- var handleBatchImport = useCallback(function () {
- message.info('批量导入:请上传文件(原型演示)');
- }, []);
-
- var openInspectImportModal = useCallback(function () {
- _inspectImportModalVisible[1](true);
- _inspectImportResult[1](null);
- }, []);
- var closeInspectImportModal = useCallback(function () {
- _inspectImportModalVisible[1](false);
- _inspectImportResult[1](null);
- }, []);
- var downloadInspectTemplate = useCallback(function () {
- var csv = '\uFEFF\u8F66\u724C\u53F7,\u7B49\u8BC4\u65F6\u95F4\n';
- var blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
- var url = URL.createObjectURL(blob);
- var a = document.createElement('a');
- a.href = url;
- a.download = '\u5BFC\u5165\u6A21\u677F_\u7B49\u8BC4\u65F6\u95F4.csv';
- a.click();
- URL.revokeObjectURL(url);
- message.success('模板已下载');
- }, []);
- var handleInspectImportUpload = useCallback(function (file) {
- var reader = new FileReader();
- reader.onload = function (e) {
- var text = e.target && e.target.result ? String(e.target.result) : '';
- var lines = text.split(/\r?\n/).filter(function (line) { return line.trim(); });
- var failedList = [];
- if (lines.length > 0) {
- var header = lines[0];
- for (var i = 1; i < Math.min(lines.length, 6); i++) {
- var parts = lines[i].split(',');
- var plateNo = (parts[0] || '').trim();
- var inspectTime = (parts[1] || '').trim();
- failedList.push({ plateNo: plateNo || '-', inspectTime: inspectTime || '-', reason: '\u8F66\u724C\u53F7\u4E0D\u5B58\u5728\u6216\u683C\u5F0F\u9519\u8BEF' });
- }
- }
- if (failedList.length === 0) failedList.push({ plateNo: '-', inspectTime: '-', reason: '\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF' });
- _inspectImportResult[1]({ hasFailed: true, failedList: failedList });
- };
- reader.readAsText(file, 'UTF-8');
- return false;
- }, []);
- var downloadInspectFailedCsv = useCallback(function () {
- var result = _inspectImportResult[0];
- if (!result || !result.failedList || result.failedList.length === 0) return;
- var escape = function (v) {
- var s = v == null ? '' : String(v);
- if (s.indexOf(',') !== -1 || s.indexOf('"') !== -1 || s.indexOf('\n') !== -1) return '"' + s.replace(/"/g, '""') + '"';
- return s;
- };
- var header = '\u8F66\u724C\u53F7,\u7B49\u8BC4\u65F6\u95F4,\u5931\u8D25\u539F\u56E0';
- var rows = result.failedList.map(function (r) { return escape(r.plateNo) + ',' + escape(r.inspectTime) + ',' + escape(r.reason); });
- var csv = '\uFEFF' + header + '\n' + rows.join('\n');
- var blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
- var url = URL.createObjectURL(blob);
- var a = document.createElement('a');
- a.href = url;
- a.download = '\u7B49\u8BC4\u65F6\u95F4\u5BFC\u5165\u5931\u8D25\u8BB0\u5F55.csv';
- a.click();
- URL.revokeObjectURL(url);
- message.success('失败记录已下载');
- }, [_inspectImportResult[0]]);
-
- var goDetail = useCallback(function (record) {
- _detailRecord[1](record);
- }, []);
-
- var backToList = useCallback(function () {
- _detailRecord[1](null);
- }, []);
-
- var openUploadModal = useCallback(function (record) {
- _currentRow[1](record);
- _uploadModalVisible[1](true);
- _confirmModalVisible[1](false);
- _plateForm[1]({ vin: record.vin || '', plateNo: record.plateNo || '' });
- _plateError[1]('');
- }, []);
-
- var closeUploadModal = useCallback(function () {
- _uploadModalVisible[1](false);
- _currentRow[1](null);
- }, []);
-
- var startOcrThenConfirm = useCallback(function () {
- _uploadModalVisible[1](false);
- _ocrLoadingVisible[1](true);
- setTimeout(function () {
- _ocrLoadingVisible[1](false);
- _confirmModalVisible[1](true);
- }, 1500);
- }, []);
-
- var closeConfirmModal = useCallback(function () {
- _confirmModalVisible[1](false);
- _currentRow[1](null);
- _plateForm[1]({ vin: '', plateNo: '' });
- _plateError[1]('');
- }, []);
-
- var onPlateFormChange = useCallback(function (field, value) {
- _plateError[1]('');
- _plateForm[1](function (prev) {
- var next = {};
- next[field] = value;
- return Object.assign({}, prev, next);
- });
- }, []);
-
- var confirmPlate = useCallback(function () {
- var row = _currentRow[0];
- var form = _plateForm[0];
- if (!row) return;
- var vinMatch = form.vin && row.vin && form.vin.trim() === row.vin.trim();
- if (!form.vin || !form.plateNo) {
- _plateError[1]('请填写车辆识别代号与车牌号');
- return;
- }
- if (!vinMatch) {
- _plateError[1]('车辆识别代号与该车辆不匹配');
- return;
- }
- message.success('上牌信息已更新(原型演示)');
- closeConfirmModal();
- }, []);
-
- var getMoreMenuItems = function (record) {
- return [
- { key: 'plate', label: '车辆上牌', onClick: function () { openUploadModal(record); } },
- { key: 'transfer', label: '车辆过户', onClick: function () { message.info('车辆过户(原型)'); } },
- { key: 'move', label: '车辆异动', onClick: function () { message.info('车辆异动(原型)'); } },
- { key: 'allocate', label: '车辆调拨', onClick: function () { message.info('车辆调拨(原型)'); } },
- { key: 'scrap', label: '车辆报废', onClick: function () { message.info('车辆报废(原型)'); } },
- { key: 'inspect', label: '车辆年审', onClick: function () { message.info('车辆年审(原型)'); } },
- { key: 'sell', label: '车辆出售', onClick: function () { message.info('车辆出售(原型)'); } },
- { key: 'fault', label: '故障提报', onClick: function () { message.info('故障提报(原型)'); } }
- ];
- };
-
- var columns = [
- { title: '运营城市', dataIndex: 'region', key: 'region', width: 140, fixed: 'left' },
- { title: '车辆识别代号', dataIndex: 'vin', key: 'vin', width: 180, fixed: 'left' },
- { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 110, fixed: 'left' },
- { title: '车辆编号', dataIndex: 'vehicleNo', key: 'vehicleNo', width: 100 },
- { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 },
- { title: '品牌', dataIndex: 'brand', key: 'brand', width: 90 },
- { title: '型号', dataIndex: 'model', key: 'model', width: 100 },
- { title: '车身颜色', dataIndex: 'color', key: 'color', width: 90 },
- { title: '归属停车场', dataIndex: 'parking', key: 'parking', width: 120 },
- { title: '客户名称', dataIndex: 'customer', key: 'customer', width: 100 },
- { title: '业务部门', dataIndex: 'department', key: 'department', width: 100 },
- { title: '业务负责人', dataIndex: 'manager', key: 'manager', width: 100 },
- { title: '运营状态', dataIndex: 'operateStatus', key: 'operateStatus', width: 90 },
- { title: '库位状态', dataIndex: 'storageStatus', key: 'storageStatus', width: 180 },
- { title: '出库状态', dataIndex: 'outStatus', key: 'outStatus', width: 90 },
- { title: '整备状态', dataIndex: 'prepareStatus', key: 'prepareStatus', width: 90 },
- { title: '过户状态', dataIndex: 'transferStatus', key: 'transferStatus', width: 90 },
- { title: '维修状态', dataIndex: 'repairStatus', key: 'repairStatus', width: 90 },
- { title: '证照状态', dataIndex: 'licenseStatus', key: 'licenseStatus', width: 90 },
- { title: '报废状态', dataIndex: 'scrapStatus', key: 'scrapStatus', width: 90 },
- { title: '登记所有权', dataIndex: 'ownership', key: 'ownership', width: 160 },
- { title: '在线状态', dataIndex: 'onlineStatus', key: 'onlineStatus', width: 90 },
- { title: '出厂年份', dataIndex: 'year', key: 'year', width: 90 },
- { title: '行驶公里数(KM)', dataIndex: 'mileage', key: 'mileage', width: 130 },
- { title: '采购入库时间', dataIndex: 'purchaseDate', key: 'purchaseDate', width: 120 },
- { title: '行驶证注册日期', dataIndex: 'regDate', key: 'regDate', width: 130 },
- { title: '行驶证检验有效期', dataIndex: 'inspectExpire', key: 'inspectExpire', width: 130 },
- { title: '上次交车时间', dataIndex: 'lastDeliveryTime', key: 'lastDeliveryTime', width: 120 },
- { title: '上次交车里程(KM)', dataIndex: 'lastDeliveryMile', key: 'lastDeliveryMile', width: 140 },
- { title: '上次还车时间', dataIndex: 'lastReturnTime', key: 'lastReturnTime', width: 120 },
- { title: '上次还车里程(KM)', dataIndex: 'lastReturnMile', key: 'lastReturnMile', width: 140 },
- { title: '强制报废日期', dataIndex: 'scrapDate', key: 'scrapDate', width: 120 },
- { title: '合同编号', dataIndex: 'contractNo', key: 'contractNo', width: 120 },
- { title: '当前位置', dataIndex: 'location', key: 'location', width: 300 },
- { title: 'GPS最后上传时间', dataIndex: 'gpsTime', key: 'gpsTime', width: 160 },
- {
- title: '操作',
- key: 'action',
- width: 140,
- fixed: 'right',
- render: function (_, record) {
- return React.createElement(Space, null,
- React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goDetail(record); } }, '查看'),
- React.createElement(Dropdown, { menu: { items: getMoreMenuItems(record) }, trigger: ['click'] },
- React.createElement(Button, { type: 'link', size: 'small' }, '更多')
- )
- );
- }
- }
- ];
-
- var filterControlStyle = { width: '100%' };
- var layoutStyle = { padding: '16px 24px', background: '#fff', minHeight: '100vh' };
- var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' };
- var filterItemStyle = { marginBottom: 12 };
- var toolbarStyle = { marginBottom: 12, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 };
- var tableSingleLineStyle = '.vehicle-mgmt-table .ant-table-thead th,.vehicle-mgmt-table .ant-table-tbody td{white-space:nowrap;}';
-
- // 车辆详情页:卡片标题、默认只显示前三列+收起/展开,下方 Tab+内容卡片
- var detailRecord = _detailRecord[0];
- if (detailRecord) {
- var r = detailRecord;
- var labelStyle = { color: 'rgba(0,0,0,0.45)', fontSize: 14, flexShrink: 0, minWidth: 100, textAlign: 'right' };
- var valueStyle = { color: 'rgba(0,0,0,0.85)', fontSize: 14, textAlign: 'left' };
- var linkStyle = { color: '#1890ff', cursor: 'pointer', fontSize: 14 };
- var cellStyle = { marginBottom: 16, minHeight: 22, display: 'flex', alignItems: 'center', gap: 8 };
- var link = function (text, onClick) { return React.createElement('a', { style: linkStyle, onClick: onClick }, text); };
- var detailCardStyle = { background: '#fff', borderRadius: 8, padding: 24, marginTop: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.03)' };
- var gridColStyle = { flex: '1', minWidth: 180 };
- var field = function (label, val) { return React.createElement('div', { style: cellStyle }, React.createElement('span', { style: labelStyle }, label), React.createElement('span', { style: valueStyle }, val)); };
- var fieldLink = function (label, text, onClick) { return React.createElement('div', { style: cellStyle }, React.createElement('span', { style: labelStyle }, label), React.createElement('span', { style: valueStyle }, link(text, onClick))); };
- var detailFields = [
- { label: '车架号', node: field('车架号', r.vin || '-') },
- { label: '车辆编号', node: field('车辆编号', r.vehicleNo || '-') },
- { label: '实际所有权', node: field('实际所有权', r.ownership || '-') },
- { label: '合同编号', node: r.contractNo && r.contractNo !== '-' ? fieldLink('合同编号', r.contractNo, function () { message.info('合同详情(原型)'); }) : field('合同编号', r.contractNo || '-') },
- { label: '出库状态', node: field('出库状态', r.outStatus || '-') },
- { label: '过户状态', node: field('过户状态', r.transferStatus || '-') },
- { label: '车身颜色', node: field('车身颜色', r.color || '-') },
- { label: '采购交车日期', node: field('采购交车日期', r.purchaseDate || '-') },
- { label: '登记所有权', node: field('登记所有权', r.ownership || '-') },
- { label: '客户名称', node: field('客户名称', r.customer || '-') },
- { label: '整备状态', node: field('整备状态', r.prepareStatus || '-') },
- { label: '证照状态', node: field('证照状态', r.licenseStatus || '-') },
- { label: '资源分类', node: field('资源分类', 'XXXXXXXXX') },
- { label: '出厂年份', node: field('出厂年份', r.year || '-') },
- { label: '库位状态', node: field('库位状态', r.storageStatus || '-') },
- { label: '业务部门', node: field('业务部门', r.department || '-') },
- { label: '上次整备时间', node: field('上次整备时间', r.lastDeliveryTime || '-') },
- { label: '报废状态', node: field('报废状态', r.scrapStatus || '-') },
- { label: '资产评级', node: field('资产评级', 'XXXXXXXXX') },
- { label: '等评时间', node: field('等评时间', r.regDate || '-') },
- { label: '强制报废期', node: field('强制报废期', r.scrapDate || '-') },
- { label: '停车位置', node: field('停车位置', r.parking || '-') },
- { label: '业务负责人', node: r.manager && r.manager !== '-' ? fieldLink('业务负责人', r.manager, function () { message.info('负责人(原型)'); }) : field('业务负责人', r.manager || '-') },
- { label: '维修状态', node: field('维修状态', r.repairStatus || '-') },
- { label: '运营城市', node: field('运营城市', r.region ? r.region.replace(/\//g, '-') : '-') },
- { label: '下次年检时间', node: field('下次年检时间', r.inspectExpire || '-') },
- { label: '上次维修时间', node: field('上次维修时间', r.lastDeliveryTime || '-') },
- { label: 'GPS最后上传时间', node: field('GPS最后上传时间', r.gpsTime || '-') },
- { label: '车辆当前位置', node: fieldLink('车辆当前位置', r.location || '-', function () { message.info('查看位置(原型)'); }) }
- ];
- var rowCount = _detailCardExpanded[0] ? 8 : 3;
- var rows = [];
- for (var i = 0; i < rowCount; i++) {
- var rowCells = [];
- for (var j = 0; j < 4; j++) {
- var idx = i * 4 + j;
- rowCells.push(React.createElement('div', { key: idx, style: gridColStyle }, detailFields[idx] ? detailFields[idx].node : null));
- }
- rows.push(React.createElement('div', { key: i, style: { display: 'flex', gap: 24, marginBottom: i < rowCount - 1 ? 16 : 0 } }, rowCells));
- }
- var tabContentCardStyle = { background: '#fff', borderRadius: 8, padding: 24, marginTop: 0, boxShadow: '0 1px 2px rgba(0,0,0,0.03)' };
- var tabContent = function (text) { return React.createElement('div', { style: { padding: '8px 0', color: 'rgba(0,0,0,0.65)', fontSize: 14 } }, text); };
- var sectionTitleStyle = { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 16, fontSize: 15, fontWeight: 600, color: 'rgba(0,0,0,0.85)' };
- var sectionIconStyle = { width: 6, height: 6, borderRadius: 3, backgroundColor: '#1890ff', flexShrink: 0 };
- var kvLabelStyle = { color: 'rgba(0,0,0,0.45)', fontSize: 14, flexShrink: 0, minWidth: 90, textAlign: 'right' };
- var kvValueStyle = { color: 'rgba(0,0,0,0.85)', fontSize: 14, textAlign: 'left' };
- var kvItem = function (label, value, badge) {
- var valNode = value;
- if (badge) valNode = React.createElement('span', null, value, React.createElement('span', { style: { marginLeft: 6, padding: '0 6px', fontSize: 12, background: '#faad14', color: '#fff', borderRadius: 2 } }, badge));
- return React.createElement('div', { key: label, style: { display: 'flex', alignItems: 'center', gap: 8 } }, React.createElement('span', { style: kvLabelStyle }, label), React.createElement('span', { style: kvValueStyle }, valNode));
- };
- var modelParamRowStyle = { display: 'flex', gap: 24, marginBottom: 16 };
- var fourColRows = function (nodes) {
- var out = [];
- for (var i = 0; i < nodes.length; i += 4) {
- var rowCells = [];
- for (var j = 0; j < 4; j++) {
- var idx = i + j;
- rowCells.push(React.createElement('div', { key: idx, style: gridColStyle }, nodes[idx] || null));
- }
- out.push(React.createElement('div', { key: i, style: modelParamRowStyle }, rowCells));
- }
- return out;
- };
- var maintenanceTableData = [
- { key: '1', no: 1, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '' },
- { key: '2', no: 2, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '' },
- { key: '3', no: 3, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '5000' },
- { key: '4', no: 4, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '5000' },
- { key: '5', no: 5, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '5000' }
- ];
- var maintenanceColumns = [
- { title: '序号', dataIndex: 'no', key: 'no', width: 60 },
- { title: '养护项目', dataIndex: 'item', key: 'item', width: 120 },
- { title: '保养公里周期(km)', dataIndex: 'kmCycle', key: 'kmCycle', width: 140 },
- { title: '保养时间周期(月)', dataIndex: 'monthCycle', key: 'monthCycle', width: 130 },
- { title: '工时费(元)', dataIndex: 'labor', key: 'labor', width: 100 },
- { title: '材料费(元)', dataIndex: 'material', key: 'material', width: 100 },
- { title: '合计', dataIndex: 'total', key: 'total', width: 80 },
- { title: '上次保养公里数(KM)', dataIndex: 'lastKm', key: 'lastKm', width: 140 }
- ];
- var modelParamContent = React.createElement('div', { style: tabContentCardStyle },
- React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '型号参数'),
- React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([
- kvItem('品牌', r.brand || '苏龙'),
- kvItem('型号', r.model || '海格牌KLQ5180XYKFCEV'),
- kvItem('车辆类型', r.vehicleType || '18吨双飞翼货车'),
- kvItem('燃料种类', '氢'),
- kvItem('整车尺寸', '5995mm x 2145mm x 3130mm'),
- kvItem('车牌颜色', '绿牌')
- ])),
- React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '轮胎情况'),
- React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([
- kvItem('轮胎数量', '8'),
- kvItem('轮胎规格', '15/80R22.5')
- ])),
- React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '电气系统'),
- React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([
- kvItem('电池类型', '磷酸铁锂'),
- kvItem('电池厂家', 'XXXXXXXXXXXXX企业名称'),
- kvItem('储电量', '100000 kWh'),
- kvItem('续航里程', '200 KM')
- ])),
- React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '供氢系统'),
- React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([
- kvItem('氢瓶容量', 'xxx L'),
- kvItem('仪表盘模式', 'MPa'),
- kvItem('续航里程', '1000 KM'),
- kvItem('供氢系统厂家', 'XXXXXXXX企业')
- ])),
- React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '其他系统'),
- React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([
- kvItem('冷机生产企业', 'XXXXXXXX企业'),
- kvItem('电堆生产企业', 'XXXXXXXX企业')
- ])),
- React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '保养参数'),
- React.createElement(Table, { size: 'small', rowKey: 'key', columns: maintenanceColumns, dataSource: maintenanceTableData, pagination: false })
- );
- var detailTabItems = [
- { key: '型号参数', label: '型号参数', children: modelParamContent },
- { key: '后装设备', label: '后装设备', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('后装设备列表(原型占位)')) },
- { key: '证照信息', label: '证照信息', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('证照信息(原型占位)')) },
- { key: '租赁记录', label: '租赁记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('租赁记录(原型占位)')) },
- { key: '保险记录', label: '保险记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('保险记录(原型占位)')) },
- { key: '维修记录', label: '维修记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('维修记录(原型占位)')) },
- { key: '事故记录', label: '事故记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('事故记录(原型占位)')) },
- { key: '故障记录', label: '故障记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('故障记录(原型占位)')) },
- { key: '违章记录', label: '违章记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('违章记录(原型占位)')) },
- { key: '异动记录', label: '异动记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('异动记录(原型占位)')) },
- { key: '调拨记录', label: '调拨记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('调拨记录(原型占位)')) },
- { key: '整备记录', label: '整备记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('整备记录(原型占位)')) }
- ];
- return React.createElement(App, null,
- React.createElement('div', { style: layoutStyle },
- React.createElement(Breadcrumb, {
- style: { marginBottom: 16 },
- items: [
- { title: '运维管理' },
- { title: React.createElement('a', { onClick: backToList, style: { color: '#1890ff' } }, '车辆管理') },
- { title: '车辆详情' }
- ]
- }),
- React.createElement('div', { style: detailCardStyle },
- React.createElement('div', { style: { fontSize: 16, fontWeight: 600, color: 'rgba(0,0,0,0.85)', marginBottom: 24 } }, '车辆详情'),
- React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, marginBottom: 24 } },
- React.createElement('span', { style: { width: 8, height: 8, borderRadius: 4, backgroundColor: r.onlineStatus === '在线' ? '#52c41a' : '#d9d9d9', flexShrink: 0 } }),
- React.createElement('span', { style: { fontSize: 16, fontWeight: 600, color: 'rgba(0,0,0,0.85)' } }, r.plateNo || '-'),
- React.createElement(Button, { type: 'primary', size: 'small' }, r.operateStatus || '租赁')
- ),
- React.createElement('div', { style: { marginBottom: 24 } }, rows),
- React.createElement('div', { style: { textAlign: 'center', paddingTop: 16, marginTop: 8, borderTop: '1px solid #f0f0f0' } },
- React.createElement(Button, { type: 'link', size: 'small', onClick: function () { _detailCardExpanded[1](!_detailCardExpanded[0]); } }, _detailCardExpanded[0] ? '收起' : '展开')
- )
- ),
- React.createElement('div', { style: Object.assign({}, detailCardStyle, { marginTop: 24 }) },
- React.createElement(Tabs, {
- activeKey: _detailTab[0],
- onChange: function (key) { _detailTab[1](key); },
- items: detailTabItems
- })
- )
- )
- );
- }
-
- var requirementText = '# 车辆管理\n一个「车辆资产管理」后台的「车辆管理」模块,功能从上到下依次为:\n\n1.面包屑:\n运维管理-车辆管理\n\n2.筛选:\n2.1.运营城市:地区选择器,支持省-市2级地区选择;\n2.2.车辆类型:选择器,根据车辆类型表拉取;\n2.3.品牌:选择器,根据型号参数表拉取品牌字段;\n2.4.型号:选择器,根据型号参数表拉取型号字段;\n2.5.客户名称:选择器,根据租赁合同/自营合同对应客户名称拉取,并包含无;\n2.6.归属业务部门:选择器,根据租赁合同/自营合同对应归属业务部门拉取,并包含无;\n2.7.合同编号:选择器,支持输入框内输入内容模糊匹配选项;\n2.8.登记所有权:选择器,支持输入框内输入内容模糊匹配选项;\n\n3.列表:\n列表左上角为车牌号筛选框,输入车牌号可快速筛选列表内记录;右侧按钮为导出(联动多选)、批量导入,字段依次为运营城市、车辆识别代号、车牌号、车辆编号、车辆类型、品牌、型号、车身颜色、归属停车场、客户名称、业务部门、业务负责人、运营状态、库位状态、出库状态、预占状态、整备状态、过户状态、维修状态、证照状态、报废状态、登记所有权、在线状态、出厂年份、行驶公里数、采购入库时间、行驶证注册日期、行驶证检验有效期、上次交车时间、上次交车里程(KM)、上次还车时间、上次还车里程(KM)、强制报废日期、合同编号、当前位置、GPS最后上传时间、操作;\n3.1.运营城市:列固定在左侧,车辆实际运营城市,通过GPS当前位置获取,只显示至省-市2级;\n3.2.车辆识别代号:列固定在左侧,显示车辆识别代号(VIN码),为唯一值,采购、外部租赁等方式录入的车架号与系统现有车架号不能重复,导入亦然;\n3.3.车牌号:列固定在左侧,显示车牌号,主要通过上牌管理功能录入;\n3.4.车辆编号:显示车辆编号,主要由旧版迁移数据获取(这批车在港口需要单独以编号记录),其他车辆无此需求;\n3.5.车辆类型:显示车辆对应车辆类型,由车辆类型表中获取;\n3.6.品牌:显示车辆对应品牌,由型号参数表-品牌字段获取;\n3.7.型号:显示车辆对应型号,由型号参数表-型号字段获取;\n3.8.车身颜色:显示车辆对应颜色,由用户采购/外部租赁车辆合同时填写车身颜色(旧版迁移数据)字段获取;\n3.9.归属停车场:显示车辆归属停车场,交车出去租赁给客户的车辆显示为-,还车或发生异动时,根据运维记录,记录最后停放的停车场,停车场从停车场表中获取;\n3.10.客户名称:显示车辆租赁/自营合同发生时,该合同对应客户名称;\n3.11.业务部门:显示车辆租赁/自营合同发生时,该合同对应业务部门;\n3.12.业务负责人:显示车辆租赁/自营合同发生时,该合同对应业务负责人;\n3.13.运营状态:分为待运营、库存、租赁、自营、退出运营\n3.14.库位状态:分为新车入库-待验车、新车入库-证照办理、库存车-可交付车、库存车-不可交付车、库存车-呆滞车、已交付车-租赁交车、已交付车-自营交车、已交付车-替换交车、退出运营-报废车、退出运营-三方退租车、退出运营-过户售车;\n3.15.出库状态:分为异动出库、调拨出库、展示出库、租赁交车、自营交车、替换交车、过户售车、外租退车、报废出库、无;\n3.16.预占状态:331前占位,该功能331后迭代;\n3.17.整备状态:分为待整备、整备中、正常、无;\n3.18.过户状态:分为过户中、内部过户完成、销售过户完成、无;\n3.19.维修状态:分为待服务站接单、维修中、正常;\n3.20.证照状态:分为正常、异常;\n3.21.报废状态:分为报废中、已报废、无;\n3.22.登记所有权:行驶证对应所有人;\n3.23.在线状态:分为在线、离线,显示GPS在线状态,根据实际对接设备取状态;\n3.24.出厂年份:格式为YYYY,根据车辆采购合同时记录的出厂年份获取;\n3.25.行驶公里数(KM):精确至2位小数,根据最近一次交车/还车/备车/异动/调拨记录时记录的行驶公里数获取;\n3.26.采购入库时间:格式为YYYY-MM-DD,根据采购合同时,采购完成车辆验车单提交时的时间;\n3.27.行驶证注册日期:格式为YYYY-MM-DD,根据上牌管理中,最后一次上传行驶证时的行驶证注册日期获取;\n3.28.行驶证检验有效期:根据行驶证(副联)中,检验记录中检验有效期获取;\n3.29.上次交车时间:格式为YYYY-MM-DD,根据最近一次交车记录完成时间获取;\n3.30.上次交车里程(KM):精确至2位小数,根据最近一次交车记录中记录的里程获取;\n3.31.上次还车日期:格式为YYYY-MM-DD,根据最近一次还车记录完成时间获取;\n3.32.上次还车里程(KM):精确至2位小数,根据最近一次还车记录中记录的里程获取;\n3.33.强制报废日期:格式为YYYY-MM-DD,根据行驶证(副联)中强制报废期获取;\n3.34.合同编号:显示当前租赁/自营合同合同编号,如无则显示为-;\n3.35.当前位置:根据车辆GPS当前定位获取,格式为xxx省xx市xx区/县xxxxx路xxxxx号xxxxxx\n3.36.GPS最后上传时间:格式为YYYY-MM-DD HH:MM,显示车辆GPS最后一次获取时间\n3.37.操作:操作列固定在右侧,显示查看、更多,悬浮更多时,显示车辆上牌、车辆过户、车辆异动、车辆调拨、车辆报废、车辆年审、车辆出售、故障提报;\n3.37.1.查看,点击跳转车辆详情页;\n3.38.2.车辆上牌,点击后上传照片,OCR识别过程中卡片提示:识别中,请勿关闭页面,之后弹出卡片,标题为确认上牌信息,左侧为行驶证照片,右侧为车辆识别代号(输入框,根据行驶证照片反写,可编辑)与车牌号(输入框,根据行驶证照片反写,可编辑),下侧为取消和确认按钮,点击确认,判断车辆识别代码与车牌号是否一致,如不一致提示车辆识别代号与该车辆不匹配,如一致则更新车牌号和行驶证照片(具体参考车辆业务-上牌管理);\n3.38.3.车辆过户:\n3.38.4.车辆异动:\n3.38.5.车辆调拨:\n3.38.6.车辆报废:\n3.38.7.车辆年审:\n3.38.8.车辆出售;\n3.38.9.故障提报:';
-
- return React.createElement(App, null,
- React.createElement('div', { style: layoutStyle },
- React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } },
- React.createElement(Breadcrumb, { items: [{ title: '运维管理' }, { title: '车辆管理' }] }),
- React.createElement(Button, { type: 'link', onClick: function () { _requirementModalVisible[1](true); } }, '查看需求说明')
- ),
-
- React.createElement('div', { style: { marginBottom: 16, display: 'flex', gap: 24, alignItems: 'flex-start' } },
- React.createElement('div', {
- style: {
- display: 'grid',
- gridTemplateColumns: '1fr 1fr 1fr',
- gap: '16px 24px',
- alignItems: 'start',
- flex: 1,
- minWidth: 0
- }
- }, (function () {
- var filterItems = [
- React.createElement('div', { key: 'region', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '运营城市'), React.createElement(Cascader, { options: regionOptions, placeholder: '请选择', style: filterControlStyle, value: _region[0].length ? _region[0] : undefined, onChange: function (v) { _region[1](v || []); } })),
- React.createElement('div', { key: 'type', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '车辆类型'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: vehicleTypeOptions, value: _vehicleType[0], onChange: _vehicleType[1], allowClear: true })),
- React.createElement('div', { key: 'brand', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '品牌'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: brandOptions, value: _brand[0], onChange: _brand[1], allowClear: true })),
- React.createElement('div', { key: 'model', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '型号'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: modelOptions, value: _model[0], onChange: _model[1], allowClear: true })),
- React.createElement('div', { key: 'customer', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '客户名称'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: customerOptions, value: _customer[0], onChange: _customer[1], allowClear: true })),
- React.createElement('div', { key: 'dept', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '归属业务部门'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: departmentOptions, value: _department[0], onChange: _department[1], allowClear: true })),
- React.createElement('div', { key: 'contract', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '合同编号'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: contractOptions, value: _contractNo[0], onChange: _contractNo[1], allowClear: true, showSearch: true, filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } })),
- React.createElement('div', { key: 'ownership', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '登记所有权'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: ownershipOptions, value: _ownership[0], onChange: _ownership[1], allowClear: true, showSearch: true, filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } }))
- ];
- var limit = _filterExpanded[0] ? 8 : 6;
- var out = [];
- for (var i = 0; i < limit && i < filterItems.length; i++) {
- out.push(filterItems[i]);
- }
- return out;
- })()),
- React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 8, flexShrink: 0 } },
- React.createElement(Button, { type: 'primary', onClick: function () { message.info('查询(原型)'); } }, '查询'),
- React.createElement(Button, { onClick: function () {
- _region[1]([]);
- _vehicleType[1](undefined);
- _brand[1](undefined);
- _model[1](undefined);
- _customer[1](undefined);
- _department[1](undefined);
- _contractNo[1](undefined);
- _ownership[1](undefined);
- } }, '重置'),
- React.createElement(Button, { type: 'link', size: 'small', onClick: function () { _filterExpanded[1](!_filterExpanded[0]); } }, _filterExpanded[0] ? '收起' : '展开')
- )
- ),
-
- React.createElement('div', { style: toolbarStyle },
- React.createElement(Input.Search, {
- placeholder: '输入车牌号筛选',
- allowClear: true,
- value: _plateFilter[0],
- onChange: onPlateFilterChange,
- onSearch: onPlateFilterChange,
- style: { width: 220 }
- }),
- React.createElement(Space, null,
- React.createElement(Button, { onClick: handleExport }, '导出'),
- React.createElement(Button, { onClick: handleBatchImport }, '批量导入'),
- React.createElement(Button, { onClick: openInspectImportModal }, '批量导入等评时间')
- )
- ),
-
- React.createElement(React.Fragment, null,
- React.createElement('style', null, tableSingleLineStyle),
- React.createElement('div', { className: 'vehicle-mgmt-table' },
- React.createElement(Table, {
- rowKey: 'id',
- columns: columns,
- dataSource: dataSource,
- rowSelection: rowSelection,
- scroll: { x: 3800 },
- size: 'small',
- pagination: { showSizeChanger: true, showQuickJumper: true, showTotal: function (t) { return '共 ' + t + ' 条'; } }
- })
- )
- ),
-
- // 车辆上牌:上传/识别中
- React.createElement(Modal, {
- title: '上传行驶证',
- open: _uploadModalVisible[0],
- onCancel: closeUploadModal,
- footer: [
- React.createElement(Button, { key: 'cancel', onClick: closeUploadModal }, '取消'),
- React.createElement(Button, { key: 'ok', type: 'primary', onClick: startOcrThenConfirm }, '开始识别')
- ]
- }, React.createElement(Upload.Dragger, { accept: 'image/*', multiple: false }, React.createElement('p', null, '点击或拖拽行驶证照片到此区域上传'))),
-
- React.createElement(Modal, {
- title: '识别中,请勿关闭页面',
- open: _ocrLoadingVisible[0],
- footer: null,
- closable: false,
- maskClosable: false
- }, React.createElement('div', { style: { textAlign: 'center', padding: '24px 0' } }, '正在识别行驶证信息...')),
-
- React.createElement(Modal, {
- title: '确认上牌信息',
- open: _confirmModalVisible[0],
- onCancel: closeConfirmModal,
- width: 640,
- footer: [
- React.createElement(Button, { key: 'cancel', onClick: closeConfirmModal }, '取消'),
- React.createElement(Button, { key: 'ok', type: 'primary', onClick: confirmPlate }, '确认')
- ]
- }, React.createElement('div', { style: { display: 'flex', gap: 24 } },
- React.createElement('div', { style: { flex: '0 0 240px' } },
- React.createElement('div', { style: { marginBottom: 8, color: '#666' } }, '行驶证照片'),
- React.createElement('img', { src: 'https://picsum.photos/240/160', alt: '行驶证', style: { width: '100%', borderRadius: 8 } })
- ),
- React.createElement('div', { style: { flex: 1 } },
- React.createElement('div', { style: { marginBottom: 12 } },
- React.createElement('span', { style: { marginRight: 8 } }, '车辆识别代号'),
- React.createElement(Input, {
- value: _plateForm[0].vin,
- onChange: function (e) { onPlateFormChange('vin', e.target.value); },
- placeholder: '根据行驶证反写,可编辑',
- style: { width: '100%' }
- })
- ),
- React.createElement('div', { style: { marginBottom: 12 } },
- React.createElement('span', { style: { marginRight: 8 } }, '车牌号'),
- React.createElement(Input, {
- value: _plateForm[0].plateNo,
- onChange: function (e) { onPlateFormChange('plateNo', e.target.value); },
- placeholder: '根据行驶证反写,可编辑',
- style: { width: '100%' }
- })
- ),
- _plateError[0] ? React.createElement('div', { style: { color: '#ff4d4f', fontSize: 12 } }, _plateError[0]) : null
- )
- )),
- React.createElement(Modal, {
- title: '需求说明',
- open: _requirementModalVisible[0],
- onCancel: function () { _requirementModalVisible[1](false); },
- width: 720,
- footer: React.createElement(Button, { onClick: function () { _requirementModalVisible[1](false); } }, '关闭')
- }, React.createElement('div', { style: { maxHeight: 560, overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6, color: 'rgba(0,0,0,0.85)' } }, requirementText)),
-
- React.createElement(Modal, {
- title: '批量导入等评时间',
- open: _inspectImportModalVisible[0],
- onCancel: closeInspectImportModal,
- width: 520,
- footer: React.createElement(Button, { onClick: closeInspectImportModal }, '关闭')
- }, React.createElement('div', null,
- React.createElement('div', { style: { marginBottom: 16 } },
- React.createElement(Upload.Dragger, {
- accept: '.csv',
- showUploadList: false,
- beforeUpload: function (file) { handleInspectImportUpload(file); return false; }
- }, React.createElement('p', { style: { margin: 0, padding: '16px 0' } }, '点击或拖拽 CSV 文件到此区域批量上传')),
- React.createElement(Button, { type: 'link', onClick: downloadInspectTemplate, style: { marginTop: 8, padding: 0 } }, '下载导入模板')
- ),
- _inspectImportResult[0] && _inspectImportResult[0].hasFailed ? React.createElement('div', { style: { marginTop: 16, padding: 12, background: '#fff2f0', border: '1px solid #ffccc7', borderRadius: 4 } },
- React.createElement('span', { style: { color: 'rgba(0,0,0,0.85)' } }, '上传失败,'),
- React.createElement('a', { href: '#', onClick: function (e) { e.preventDefault(); downloadInspectFailedCsv(); }, style: { color: '#1890ff' } }, '查看失败记录')
- ) : null
- ))
- )
- );
-};
+// 【重要】必须使用 const Component 作为组件变量名
+// 车辆资产管理 - 车辆管理模块(中后台 Ant Design 原型)
+const Component = function () {
+ var useState = React.useState;
+ var useMemo = React.useMemo;
+ var useCallback = React.useCallback;
+ var antd = window.antd;
+
+ var Breadcrumb = antd.Breadcrumb;
+ var Cascader = antd.Cascader;
+ var Select = antd.Select;
+ var Input = antd.Input;
+ var Button = antd.Button;
+ var Table = antd.Table;
+ var Space = antd.Space;
+ var Dropdown = antd.Dropdown;
+ var Modal = antd.Modal;
+ var Upload = antd.Upload;
+ var Card = antd.Card;
+ var Tabs = antd.Tabs;
+ var message = antd.message;
+ var App = antd.App;
+
+ // 筛选项状态
+ var _region = useState([]);
+ var _vehicleType = useState(undefined);
+ var _brand = useState(undefined);
+ var _model = useState(undefined);
+ var _customer = useState(undefined);
+ var _department = useState(undefined);
+ var _contractNo = useState(undefined);
+ var _ownership = useState(undefined);
+
+ var _plateFilter = useState('');
+ var _selectedRowKeys = useState([]);
+ var _uploadModalVisible = useState(false);
+ var _confirmModalVisible = useState(false);
+ var _ocrLoadingVisible = useState(false);
+ var _currentRow = useState(null);
+ var _plateForm = useState({ vin: '', plateNo: '' });
+ var _plateError = useState('');
+ var _detailRecord = useState(null);
+ var _detailCardExpanded = useState(false);
+ var _detailTab = useState('型号参数');
+ var _filterExpanded = useState(false);
+ var _requirementModalVisible = useState(false);
+ var _inspectImportModalVisible = useState(false);
+ var _inspectImportResult = useState(null);
+
+ // 省-市 地区数据(示例)
+ var regionOptions = [
+ { value: 'guangdong', label: '广东省', children: [{ value: 'guangzhou', label: '广州市' }, { value: 'shenzhen', label: '深圳市' }] },
+ { value: 'beijing', label: '北京市', children: [{ value: 'beijing', label: '北京市' }] },
+ { value: 'shanghai', label: '上海市', children: [{ value: 'shanghai', label: '上海市' }] }
+ ];
+
+ // 车辆类型、品牌、型号、客户、部门、合同、登记所有权(模拟下拉数据)
+ var vehicleTypeOptions = [{ label: '小型轿车', value: 'type1' }, { label: 'SUV', value: 'type2' }, { label: '厢式货车', value: 'type3' }];
+ var brandOptions = [{ label: '比亚迪', value: 'byd' }, { label: '特斯拉', value: 'tsl' }, { label: '蔚来', value: 'nio' }];
+ var modelOptions = [{ label: '汉EV', value: 'han' }, { label: 'Model 3', value: 'm3' }, { label: 'ET5', value: 'et5' }];
+ var customerOptions = [{ label: '无', value: 'none' }, { label: '客户A', value: 'c1' }, { label: '客户B', value: 'c2' }];
+ var departmentOptions = [{ label: '无', value: 'none' }, { label: '华南区', value: 'd1' }, { label: '华东区', value: 'd2' }];
+ var contractOptions = [{ label: 'HT-2024-001', value: 'HT-2024-001' }, { label: 'HT-2024-002', value: 'HT-2024-002' }];
+ var ownershipOptions = [{ label: '某某租赁公司', value: 'o1' }, { label: '某某科技有限公司', value: 'o2' }];
+
+ // 表格数据(模拟 20 条,状态按产品枚举)
+ // 运营状态: 待运营、库存、租赁、自营、退出运营
+ // 库位状态: 新车入库-待验车、新车入库-证照办理、库存车-可交付车、库存车-不可交付车、库存车-呆滞车、已交付车-租赁交车、已交付车-自营交车、已交付车-替换交车、退出运营-报废车、退出运营-三方退租车、退出运营-过户售车
+ // 出库状态: 异动出库、调拨出库、展示出库、租赁交车、自营交车、替换交车、过户售车、外租退车、报废出库、无
+ // 预占状态: 331 后迭代,暂用「未预占」
+ // 整备状态: 待整备、整备中、正常、无
+ // 过户状态: 过户中、内部过户完成、销售过户完成、无
+ // 维修状态: 待服务站接单、维修中、正常
+ // 证照状态: 正常、异常
+ // 报废状态: 报废中、已报废、无
+ var rawData = [
+ { id: '1', region: '广东省/广州市', vin: 'LGWEF4A59NS123456', plateNo: '粤A12345', vehicleNo: 'V001', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '白色', parking: '天河停车场', customer: '客户A', department: '华南区', manager: '张三', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '12580.50', purchaseDate: '2023-06-15', regDate: '2023-07-01', inspectExpire: '2025-07', lastDeliveryTime: '2024-01-10', lastDeliveryMile: '12000.00', lastReturnTime: '2024-02-01', lastReturnMile: '12580.50', scrapDate: '2038-12-31', contractNo: 'HT-2024-001', location: '广东省广州市天河区天河路100号', gpsTime: '2024-02-12 14:30' },
+ { id: '2', region: '广东省/深圳市', vin: 'LGWEF4A59NS789012', plateNo: '粤B67890', vehicleNo: '-', vehicleType: 'SUV', brand: '特斯拉', model: 'Model 3', color: '黑色', parking: '-', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '库存', storageStatus: '库存车-可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '待整备', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2022', mileage: '25600.00', purchaseDate: '2022-08-20', regDate: '2022-09-01', inspectExpire: '2024-09', lastDeliveryTime: '2024-01-05', lastDeliveryMile: '25500.00', lastReturnTime: '2024-01-20', lastReturnMile: '25600.00', scrapDate: '2037-09-30', contractNo: '-', location: '广东省深圳市南山区科技园南路', gpsTime: '2024-02-11 09:00' },
+ { id: '3', region: '广东省/广州市', vin: 'LSJA24U70PS001234', plateNo: '粤A88K88', vehicleNo: 'V003', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '灰色', parking: '黄埔停车场', customer: '客户A', department: '华南区', manager: '王五', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '8320.00', purchaseDate: '2023-09-01', regDate: '2023-09-20', inspectExpire: '2025-09', lastDeliveryTime: '2024-02-05', lastDeliveryMile: '8100.00', lastReturnTime: '2024-02-10', lastReturnMile: '8320.00', scrapDate: '2039-09-30', contractNo: 'HT-2024-002', location: '广东省广州市黄埔区开泰大道200号', gpsTime: '2024-02-12 10:15' },
+ { id: '4', region: '北京市/北京市', vin: 'WVWZZZ3CZWE123456', plateNo: '京C12345', vehicleNo: '-', vehicleType: 'SUV', brand: '特斯拉', model: 'Model Y', color: '蓝色', parking: '朝阳停车场', customer: '无', department: '无', manager: '-', operateStatus: '自营', storageStatus: '已交付车-自营交车', outStatus: '自营交车', preemptStatus: '未预占', prepareStatus: '整备中', transferStatus: '无', repairStatus: '维修中', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2022', mileage: '18900.25', purchaseDate: '2022-11-10', regDate: '2022-12-01', inspectExpire: '2024-12', lastDeliveryTime: '2024-01-15', lastDeliveryMile: '18500.00', lastReturnTime: '2024-01-28', lastReturnMile: '18900.25', scrapDate: '2037-12-31', contractNo: '-', location: '北京市朝阳区望京街88号', gpsTime: '2024-02-10 16:00' },
+ { id: '5', region: '上海市/上海市', vin: 'LSVAU2BR3NS567890', plateNo: '沪D66666', vehicleNo: 'V005', vehicleType: '厢式货车', brand: '比亚迪', model: '汉EV', color: '白色', parking: '-', customer: '客户C', department: '华东区', manager: '赵六', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '异常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2021', mileage: '45200.80', purchaseDate: '2021-05-20', regDate: '2021-06-15', inspectExpire: '2024-06', lastDeliveryTime: '2024-02-01', lastDeliveryMile: '44800.00', lastReturnTime: '2024-02-08', lastReturnMile: '45200.80', scrapDate: '2036-06-30', contractNo: 'HT-2024-003', location: '上海市浦东新区张江高科路500号', gpsTime: '2024-02-12 09:45' },
+ { id: '6', region: '广东省/深圳市', vin: '5YJ3E1EA1NF123456', plateNo: '粤B12345', vehicleNo: '-', vehicleType: '小型轿车', brand: '特斯拉', model: 'Model 3', color: '红色', parking: '福田停车场', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '5600.00', purchaseDate: '2023-03-08', regDate: '2023-04-01', inspectExpire: '2025-04', lastDeliveryTime: '2024-02-02', lastDeliveryMile: '5200.00', lastReturnTime: '2024-02-11', lastReturnMile: '5600.00', scrapDate: '2038-04-30', contractNo: 'HT-2024-004', location: '广东省深圳市福田区福华路188号', gpsTime: '2024-02-12 11:20' },
+ { id: '7', region: '广东省/广州市', vin: 'LGWEF4A59NS234567', plateNo: '粤A99A99', vehicleNo: 'V007', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '黑色', parking: '天河停车场', customer: '客户A', department: '华南区', manager: '张三', operateStatus: '库存', storageStatus: '库存车-不可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2022', mileage: '22100.30', purchaseDate: '2022-07-15', regDate: '2022-08-01', inspectExpire: '2024-08', lastDeliveryTime: '2024-01-20', lastDeliveryMile: '21800.00', lastReturnTime: '2024-02-05', lastReturnMile: '22100.30', scrapDate: '2037-08-31', contractNo: '-', location: '广东省广州市天河区体育西路200号', gpsTime: '2024-02-11 18:30' },
+ { id: '8', region: '北京市/北京市', vin: 'WVWZZZ3CZWE789012', plateNo: '京E88888', vehicleNo: '-', vehicleType: 'SUV', brand: '蔚来', model: 'ET5', color: '绿色', parking: '-', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '自营', storageStatus: '已交付车-自营交车', outStatus: '自营交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2023', mileage: '11200.00', purchaseDate: '2023-01-12', regDate: '2023-02-01', inspectExpire: '2025-02', lastDeliveryTime: '2024-02-06', lastDeliveryMile: '11000.00', lastReturnTime: '2024-02-12', lastReturnMile: '11200.00', scrapDate: '2038-02-28', contractNo: 'HT-2024-005', location: '北京市海淀区中关村大街1号', gpsTime: '2024-02-12 08:00' },
+ { id: '9', region: '上海市/上海市', vin: 'LSVAU2BR3NS111222', plateNo: '沪A12345', vehicleNo: '-', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '银色', parking: '浦东停车场', customer: '无', department: '无', manager: '-', operateStatus: '库存', storageStatus: '库存车-呆滞车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '待整备', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2021', mileage: '38500.60', purchaseDate: '2021-10-05', regDate: '2021-11-01', inspectExpire: '2024-11', lastDeliveryTime: '2024-01-08', lastDeliveryMile: '38000.00', lastReturnTime: '2024-01-25', lastReturnMile: '38500.60', scrapDate: '2036-11-30', contractNo: '-', location: '上海市徐汇区漕溪路250号', gpsTime: '2024-02-09 14:00' },
+ { id: '10', region: '广东省/深圳市', vin: '5YJ3E1EA2NF333444', plateNo: '粤B55B55', vehicleNo: 'V010', vehicleType: 'SUV', brand: '特斯拉', model: 'Model Y', color: '白色', parking: '南山停车场', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2022', mileage: '16800.00', purchaseDate: '2022-04-18', regDate: '2022-05-10', inspectExpire: '2024-05', lastDeliveryTime: '2024-01-30', lastDeliveryMile: '16500.00', lastReturnTime: '2024-02-09', lastReturnMile: '16800.00', scrapDate: '2037-05-31', contractNo: 'HT-2024-006', location: '广东省深圳市南山区后海大道300号', gpsTime: '2024-02-12 13:10' },
+ { id: '11', region: '广东省/广州市', vin: 'LSJA24U70PS555666', plateNo: '粤A11B22', vehicleNo: '-', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '蓝色', parking: '番禺停车场', customer: '客户A', department: '华南区', manager: '王五', operateStatus: '租赁', storageStatus: '已交付车-替换交车', outStatus: '替换交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '7200.50', purchaseDate: '2023-07-20', regDate: '2023-08-05', inspectExpire: '2025-08', lastDeliveryTime: '2024-02-03', lastDeliveryMile: '7000.00', lastReturnTime: '2024-02-11', lastReturnMile: '7200.50', scrapDate: '2038-08-31', contractNo: 'HT-2024-007', location: '广东省广州市番禺区市桥街100号', gpsTime: '2024-02-12 12:00' },
+ { id: '12', region: '北京市/北京市', vin: 'WVWZZZ3CZWE333555', plateNo: '京F33333', vehicleNo: '-', vehicleType: '厢式货车', brand: '比亚迪', model: '汉EV', color: '灰色', parking: '大兴停车场', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '库存', storageStatus: '库存车-不可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '待整备', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2020', mileage: '52100.00', purchaseDate: '2020-09-01', regDate: '2020-10-01', inspectExpire: '2024-10', lastDeliveryTime: '2024-01-12', lastDeliveryMile: '51800.00', lastReturnTime: '2024-01-30', lastReturnMile: '52100.00', scrapDate: '2035-10-31', contractNo: '-', location: '北京市大兴区亦庄经济开发区', gpsTime: '2024-02-08 11:00' },
+ { id: '13', region: '上海市/上海市', vin: 'LSVAU2BR3NS777888', plateNo: '沪B99999', vehicleNo: 'V013', vehicleType: '小型轿车', brand: '特斯拉', model: 'Model 3', color: '黑色', parking: '-', customer: '客户C', department: '华东区', manager: '赵六', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '内部过户完成', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2022', mileage: '19800.00', purchaseDate: '2022-12-01', regDate: '2023-01-05', inspectExpire: '2025-01', lastDeliveryTime: '2024-02-07', lastDeliveryMile: '19500.00', lastReturnTime: '2024-02-12', lastReturnMile: '19800.00', scrapDate: '2038-01-31', contractNo: 'HT-2024-008', location: '上海市闵行区莘庄镇莘松路88号', gpsTime: '2024-02-12 15:45' },
+ { id: '14', region: '广东省/深圳市', vin: 'LGWEF4A59NS888999', plateNo: '粤B22C33', vehicleNo: '-', vehicleType: 'SUV', brand: '比亚迪', model: '汉EV', color: '白色', parking: '龙岗停车场', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '4200.00', purchaseDate: '2023-10-10', regDate: '2023-11-01', inspectExpire: '2025-11', lastDeliveryTime: '2024-02-04', lastDeliveryMile: '4000.00', lastReturnTime: '2024-02-10', lastReturnMile: '4200.00', scrapDate: '2038-11-30', contractNo: 'HT-2024-009', location: '广东省深圳市龙岗区龙城大道500号', gpsTime: '2024-02-12 10:30' },
+ { id: '15', region: '广东省/广州市', vin: 'LSJA24U70PS000111', plateNo: '粤A66D66', vehicleNo: 'V015', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '红色', parking: '天河停车场', customer: '无', department: '无', manager: '-', operateStatus: '待运营', storageStatus: '新车入库-待验车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '无', transferStatus: '无', repairStatus: '正常', licenseStatus: '异常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2021', mileage: '31200.40', purchaseDate: '2021-03-15', regDate: '2021-04-01', inspectExpire: '2024-04', lastDeliveryTime: '2024-01-18', lastDeliveryMile: '30800.00', lastReturnTime: '2024-02-02', lastReturnMile: '31200.40', scrapDate: '2036-04-30', contractNo: '-', location: '广东省广州市越秀区中山五路66号', gpsTime: '2024-02-07 09:00' },
+ { id: '16', region: '北京市/北京市', vin: '5YJ3E1EA3NF222333', plateNo: '京G12345', vehicleNo: '-', vehicleType: '小型轿车', brand: '特斯拉', model: 'Model 3', color: '银色', parking: '西城停车场', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '9800.00', purchaseDate: '2023-05-01', regDate: '2023-05-20', inspectExpire: '2025-05', lastDeliveryTime: '2024-02-01', lastDeliveryMile: '9500.00', lastReturnTime: '2024-02-11', lastReturnMile: '9800.00', scrapDate: '2038-05-31', contractNo: 'HT-2024-010', location: '北京市西城区金融街28号', gpsTime: '2024-02-12 14:00' },
+ { id: '17', region: '上海市/上海市', vin: 'LGWEF4A59NS444555', plateNo: '沪C11111', vehicleNo: '-', vehicleType: '小型轿车', brand: '比亚迪', model: '汉EV', color: '黑色', parking: '虹口停车场', customer: '客户C', department: '华东区', manager: '赵六', operateStatus: '库存', storageStatus: '库存车-可交付车', outStatus: '无', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '离线', year: '2022', mileage: '24500.00', purchaseDate: '2022-02-28', regDate: '2022-03-15', inspectExpire: '2024-03', lastDeliveryTime: '2024-01-22', lastDeliveryMile: '24200.00', lastReturnTime: '2024-02-06', lastReturnMile: '24500.00', scrapDate: '2037-03-31', contractNo: '-', location: '上海市虹口区四川北路1688号', gpsTime: '2024-02-11 17:00' },
+ { id: '18', region: '广东省/深圳市', vin: 'LSVAU2BR3NS666777', plateNo: '粤B44E44', vehicleNo: 'V018', vehicleType: '厢式货车', brand: '比亚迪', model: '汉EV', color: '灰色', parking: '-', customer: '客户B', department: '华南区', manager: '李四', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '无', repairStatus: '待服务站接单', licenseStatus: '正常', scrapStatus: '无', ownership: '某某科技有限公司', onlineStatus: '在线', year: '2020', mileage: '67800.25', purchaseDate: '2020-06-10', regDate: '2020-07-01', inspectExpire: '2024-07', lastDeliveryTime: '2024-01-25', lastDeliveryMile: '67500.00', lastReturnTime: '2024-02-09', lastReturnMile: '67800.25', scrapDate: '2035-07-31', contractNo: 'HT-2024-011', location: '广东省深圳市宝安区新安街道创业路', gpsTime: '2024-02-12 08:20' },
+ { id: '19', region: '广东省/广州市', vin: '5YJ3E1EA4NF888999', plateNo: '粤A77F77', vehicleNo: '-', vehicleType: 'SUV', brand: '特斯拉', model: 'Model Y', color: '蓝色', parking: '白云停车场', customer: '客户A', department: '华南区', manager: '张三', operateStatus: '租赁', storageStatus: '已交付车-租赁交车', outStatus: '租赁交车', preemptStatus: '未预占', prepareStatus: '正常', transferStatus: '过户中', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '无', ownership: '某某租赁公司', onlineStatus: '在线', year: '2023', mileage: '3500.00', purchaseDate: '2023-11-05', regDate: '2023-11-25', inspectExpire: '2025-11', lastDeliveryTime: '2024-02-08', lastDeliveryMile: '3200.00', lastReturnTime: '2024-02-12', lastReturnMile: '3500.00', scrapDate: '2038-11-30', contractNo: 'HT-2024-012', location: '广东省广州市白云区白云大道南888号', gpsTime: '2024-02-12 16:00' },
+ { id: '20', region: '北京市/北京市', vin: 'LSJA24U70PS999000', plateNo: '京H88888', vehicleNo: '-', vehicleType: '小型轿车', brand: '蔚来', model: 'ET5', color: '白色', parking: '昌平停车场', customer: '客户D', department: '华北区', manager: '孙七', operateStatus: '退出运营', storageStatus: '退出运营-报废车', outStatus: '报废出库', preemptStatus: '未预占', prepareStatus: '无', transferStatus: '过户中', repairStatus: '正常', licenseStatus: '正常', scrapStatus: '报废中', ownership: '某某科技有限公司', onlineStatus: '离线', year: '2022', mileage: '15600.00', purchaseDate: '2022-06-20', regDate: '2022-07-10', inspectExpire: '2024-07', lastDeliveryTime: '2024-01-10', lastDeliveryMile: '15300.00', lastReturnTime: '2024-01-28', lastReturnMile: '15600.00', scrapDate: '2037-07-31', contractNo: '-', location: '北京市昌平区回龙观西大街100号', gpsTime: '2024-02-10 12:30' }
+ ];
+
+ var dataSource = useMemo(function () {
+ var plate = _plateFilter[0];
+ if (!plate || plate.trim() === '') return rawData;
+ return rawData.filter(function (row) {
+ return row.plateNo && row.plateNo.indexOf(plate) !== -1;
+ });
+ }, [rawData, _plateFilter[0]]);
+
+ var onPlateFilterChange = useCallback(function (e) {
+ _plateFilter[1](e.target.value);
+ }, []);
+
+ var onSelectChange = useCallback(function (keys) {
+ _selectedRowKeys[1](keys);
+ }, []);
+
+ var rowSelection = {
+ selectedRowKeys: _selectedRowKeys[0],
+ onChange: onSelectChange
+ };
+
+ var handleExport = useCallback(function () {
+ if (_selectedRowKeys[0].length === 0) {
+ message.warning('请先勾选要导出的记录');
+ return;
+ }
+ message.success('导出功能(联动多选):已选 ' + _selectedRowKeys[0].length + ' 条');
+ }, []);
+
+ var handleBatchImport = useCallback(function () {
+ message.info('批量导入:请上传文件(原型演示)');
+ }, []);
+
+ var openInspectImportModal = useCallback(function () {
+ _inspectImportModalVisible[1](true);
+ _inspectImportResult[1](null);
+ }, []);
+ var closeInspectImportModal = useCallback(function () {
+ _inspectImportModalVisible[1](false);
+ _inspectImportResult[1](null);
+ }, []);
+ var downloadInspectTemplate = useCallback(function () {
+ var csv = '\uFEFF\u8F66\u724C\u53F7,\u7B49\u8BC4\u65F6\u95F4\n';
+ var blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
+ var url = URL.createObjectURL(blob);
+ var a = document.createElement('a');
+ a.href = url;
+ a.download = '\u5BFC\u5165\u6A21\u677F_\u7B49\u8BC4\u65F6\u95F4.csv';
+ a.click();
+ URL.revokeObjectURL(url);
+ message.success('模板已下载');
+ }, []);
+ var handleInspectImportUpload = useCallback(function (file) {
+ var reader = new FileReader();
+ reader.onload = function (e) {
+ var text = e.target && e.target.result ? String(e.target.result) : '';
+ var lines = text.split(/\r?\n/).filter(function (line) { return line.trim(); });
+ var failedList = [];
+ if (lines.length > 0) {
+ var header = lines[0];
+ for (var i = 1; i < Math.min(lines.length, 6); i++) {
+ var parts = lines[i].split(',');
+ var plateNo = (parts[0] || '').trim();
+ var inspectTime = (parts[1] || '').trim();
+ failedList.push({ plateNo: plateNo || '-', inspectTime: inspectTime || '-', reason: '\u8F66\u724C\u53F7\u4E0D\u5B58\u5728\u6216\u683C\u5F0F\u9519\u8BEF' });
+ }
+ }
+ if (failedList.length === 0) failedList.push({ plateNo: '-', inspectTime: '-', reason: '\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF' });
+ _inspectImportResult[1]({ hasFailed: true, failedList: failedList });
+ };
+ reader.readAsText(file, 'UTF-8');
+ return false;
+ }, []);
+ var downloadInspectFailedCsv = useCallback(function () {
+ var result = _inspectImportResult[0];
+ if (!result || !result.failedList || result.failedList.length === 0) return;
+ var escape = function (v) {
+ var s = v == null ? '' : String(v);
+ if (s.indexOf(',') !== -1 || s.indexOf('"') !== -1 || s.indexOf('\n') !== -1) return '"' + s.replace(/"/g, '""') + '"';
+ return s;
+ };
+ var header = '\u8F66\u724C\u53F7,\u7B49\u8BC4\u65F6\u95F4,\u5931\u8D25\u539F\u56E0';
+ var rows = result.failedList.map(function (r) { return escape(r.plateNo) + ',' + escape(r.inspectTime) + ',' + escape(r.reason); });
+ var csv = '\uFEFF' + header + '\n' + rows.join('\n');
+ var blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
+ var url = URL.createObjectURL(blob);
+ var a = document.createElement('a');
+ a.href = url;
+ a.download = '\u7B49\u8BC4\u65F6\u95F4\u5BFC\u5165\u5931\u8D25\u8BB0\u5F55.csv';
+ a.click();
+ URL.revokeObjectURL(url);
+ message.success('失败记录已下载');
+ }, [_inspectImportResult[0]]);
+
+ var goDetail = useCallback(function (record) {
+ _detailRecord[1](record);
+ }, []);
+
+ var backToList = useCallback(function () {
+ _detailRecord[1](null);
+ }, []);
+
+ var openUploadModal = useCallback(function (record) {
+ _currentRow[1](record);
+ _uploadModalVisible[1](true);
+ _confirmModalVisible[1](false);
+ _plateForm[1]({ vin: record.vin || '', plateNo: record.plateNo || '' });
+ _plateError[1]('');
+ }, []);
+
+ var closeUploadModal = useCallback(function () {
+ _uploadModalVisible[1](false);
+ _currentRow[1](null);
+ }, []);
+
+ var startOcrThenConfirm = useCallback(function () {
+ _uploadModalVisible[1](false);
+ _ocrLoadingVisible[1](true);
+ setTimeout(function () {
+ _ocrLoadingVisible[1](false);
+ _confirmModalVisible[1](true);
+ }, 1500);
+ }, []);
+
+ var closeConfirmModal = useCallback(function () {
+ _confirmModalVisible[1](false);
+ _currentRow[1](null);
+ _plateForm[1]({ vin: '', plateNo: '' });
+ _plateError[1]('');
+ }, []);
+
+ var onPlateFormChange = useCallback(function (field, value) {
+ _plateError[1]('');
+ _plateForm[1](function (prev) {
+ var next = {};
+ next[field] = value;
+ return Object.assign({}, prev, next);
+ });
+ }, []);
+
+ var confirmPlate = useCallback(function () {
+ var row = _currentRow[0];
+ var form = _plateForm[0];
+ if (!row) return;
+ var vinMatch = form.vin && row.vin && form.vin.trim() === row.vin.trim();
+ if (!form.vin || !form.plateNo) {
+ _plateError[1]('请填写车辆识别代号与车牌号');
+ return;
+ }
+ if (!vinMatch) {
+ _plateError[1]('车辆识别代号与该车辆不匹配');
+ return;
+ }
+ message.success('上牌信息已更新(原型演示)');
+ closeConfirmModal();
+ }, []);
+
+ var getMoreMenuItems = function (record) {
+ return [
+ { key: 'plate', label: '车辆上牌', onClick: function () { openUploadModal(record); } },
+ { key: 'transfer', label: '车辆过户', onClick: function () { message.info('车辆过户(原型)'); } },
+ { key: 'move', label: '车辆异动', onClick: function () { message.info('车辆异动(原型)'); } },
+ { key: 'allocate', label: '车辆调拨', onClick: function () { message.info('车辆调拨(原型)'); } },
+ { key: 'scrap', label: '车辆报废', onClick: function () { message.info('车辆报废(原型)'); } },
+ { key: 'inspect', label: '车辆年审', onClick: function () { message.info('车辆年审(原型)'); } },
+ { key: 'sell', label: '车辆出售', onClick: function () { message.info('车辆出售(原型)'); } },
+ { key: 'fault', label: '故障提报', onClick: function () { message.info('故障提报(原型)'); } }
+ ];
+ };
+
+ var columns = [
+ { title: '运营城市', dataIndex: 'region', key: 'region', width: 140, fixed: 'left' },
+ { title: '车辆识别代号', dataIndex: 'vin', key: 'vin', width: 180, fixed: 'left' },
+ { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 110, fixed: 'left' },
+ { title: '车辆编号', dataIndex: 'vehicleNo', key: 'vehicleNo', width: 100 },
+ { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 },
+ { title: '品牌', dataIndex: 'brand', key: 'brand', width: 90 },
+ { title: '型号', dataIndex: 'model', key: 'model', width: 100 },
+ { title: '车身颜色', dataIndex: 'color', key: 'color', width: 90 },
+ { title: '归属停车场', dataIndex: 'parking', key: 'parking', width: 120 },
+ { title: '客户名称', dataIndex: 'customer', key: 'customer', width: 100 },
+ { title: '业务部门', dataIndex: 'department', key: 'department', width: 100 },
+ { title: '业务负责人', dataIndex: 'manager', key: 'manager', width: 100 },
+ { title: '运营状态', dataIndex: 'operateStatus', key: 'operateStatus', width: 90 },
+ { title: '库位状态', dataIndex: 'storageStatus', key: 'storageStatus', width: 180 },
+ { title: '出库状态', dataIndex: 'outStatus', key: 'outStatus', width: 90 },
+ { title: '整备状态', dataIndex: 'prepareStatus', key: 'prepareStatus', width: 90 },
+ { title: '过户状态', dataIndex: 'transferStatus', key: 'transferStatus', width: 90 },
+ { title: '维修状态', dataIndex: 'repairStatus', key: 'repairStatus', width: 90 },
+ { title: '证照状态', dataIndex: 'licenseStatus', key: 'licenseStatus', width: 90 },
+ { title: '报废状态', dataIndex: 'scrapStatus', key: 'scrapStatus', width: 90 },
+ { title: '登记所有权', dataIndex: 'ownership', key: 'ownership', width: 160 },
+ { title: '在线状态', dataIndex: 'onlineStatus', key: 'onlineStatus', width: 90 },
+ { title: '出厂年份', dataIndex: 'year', key: 'year', width: 90 },
+ { title: '行驶公里数(KM)', dataIndex: 'mileage', key: 'mileage', width: 130 },
+ { title: '采购入库时间', dataIndex: 'purchaseDate', key: 'purchaseDate', width: 120 },
+ { title: '行驶证注册日期', dataIndex: 'regDate', key: 'regDate', width: 130 },
+ { title: '行驶证检验有效期', dataIndex: 'inspectExpire', key: 'inspectExpire', width: 130 },
+ { title: '上次交车时间', dataIndex: 'lastDeliveryTime', key: 'lastDeliveryTime', width: 120 },
+ { title: '上次交车里程(KM)', dataIndex: 'lastDeliveryMile', key: 'lastDeliveryMile', width: 140 },
+ { title: '上次还车时间', dataIndex: 'lastReturnTime', key: 'lastReturnTime', width: 120 },
+ { title: '上次还车里程(KM)', dataIndex: 'lastReturnMile', key: 'lastReturnMile', width: 140 },
+ { title: '强制报废日期', dataIndex: 'scrapDate', key: 'scrapDate', width: 120 },
+ { title: '合同编号', dataIndex: 'contractNo', key: 'contractNo', width: 120 },
+ { title: '当前位置', dataIndex: 'location', key: 'location', width: 300 },
+ { title: 'GPS最后上传时间', dataIndex: 'gpsTime', key: 'gpsTime', width: 160 },
+ {
+ title: '操作',
+ key: 'action',
+ width: 140,
+ fixed: 'right',
+ render: function (_, record) {
+ return React.createElement(Space, null,
+ React.createElement(Button, { type: 'link', size: 'small', onClick: function () { goDetail(record); } }, '查看'),
+ React.createElement(Dropdown, { menu: { items: getMoreMenuItems(record) }, trigger: ['click'] },
+ React.createElement(Button, { type: 'link', size: 'small' }, '更多')
+ )
+ );
+ }
+ }
+ ];
+
+ var filterControlStyle = { width: '100%' };
+ var layoutStyle = { padding: '16px 24px', background: '#fff', minHeight: '100vh' };
+ var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' };
+ var filterItemStyle = { marginBottom: 12 };
+ var toolbarStyle = { marginBottom: 12, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 };
+ var tableSingleLineStyle = '.vehicle-mgmt-table .ant-table-thead th,.vehicle-mgmt-table .ant-table-tbody td{white-space:nowrap;}';
+
+ // 车辆详情页:卡片标题、默认只显示前三列+收起/展开,下方 Tab+内容卡片
+ var detailRecord = _detailRecord[0];
+ if (detailRecord) {
+ var r = detailRecord;
+ var labelStyle = { color: 'rgba(0,0,0,0.45)', fontSize: 14, flexShrink: 0, minWidth: 100, textAlign: 'right' };
+ var valueStyle = { color: 'rgba(0,0,0,0.85)', fontSize: 14, textAlign: 'left' };
+ var linkStyle = { color: '#1890ff', cursor: 'pointer', fontSize: 14 };
+ var cellStyle = { marginBottom: 16, minHeight: 22, display: 'flex', alignItems: 'center', gap: 8 };
+ var link = function (text, onClick) { return React.createElement('a', { style: linkStyle, onClick: onClick }, text); };
+ var detailCardStyle = { background: '#fff', borderRadius: 8, padding: 24, marginTop: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.03)' };
+ var gridColStyle = { flex: '1', minWidth: 180 };
+ var field = function (label, val) { return React.createElement('div', { style: cellStyle }, React.createElement('span', { style: labelStyle }, label), React.createElement('span', { style: valueStyle }, val)); };
+ var fieldLink = function (label, text, onClick) { return React.createElement('div', { style: cellStyle }, React.createElement('span', { style: labelStyle }, label), React.createElement('span', { style: valueStyle }, link(text, onClick))); };
+ var detailFields = [
+ { label: '车架号', node: field('车架号', r.vin || '-') },
+ { label: '车辆编号', node: field('车辆编号', r.vehicleNo || '-') },
+ { label: '实际所有权', node: field('实际所有权', r.ownership || '-') },
+ { label: '合同编号', node: r.contractNo && r.contractNo !== '-' ? fieldLink('合同编号', r.contractNo, function () { message.info('合同详情(原型)'); }) : field('合同编号', r.contractNo || '-') },
+ { label: '出库状态', node: field('出库状态', r.outStatus || '-') },
+ { label: '过户状态', node: field('过户状态', r.transferStatus || '-') },
+ { label: '车身颜色', node: field('车身颜色', r.color || '-') },
+ { label: '采购交车日期', node: field('采购交车日期', r.purchaseDate || '-') },
+ { label: '登记所有权', node: field('登记所有权', r.ownership || '-') },
+ { label: '客户名称', node: field('客户名称', r.customer || '-') },
+ { label: '整备状态', node: field('整备状态', r.prepareStatus || '-') },
+ { label: '证照状态', node: field('证照状态', r.licenseStatus || '-') },
+ { label: '资源分类', node: field('资源分类', 'XXXXXXXXX') },
+ { label: '出厂年份', node: field('出厂年份', r.year || '-') },
+ { label: '库位状态', node: field('库位状态', r.storageStatus || '-') },
+ { label: '业务部门', node: field('业务部门', r.department || '-') },
+ { label: '上次整备时间', node: field('上次整备时间', r.lastDeliveryTime || '-') },
+ { label: '报废状态', node: field('报废状态', r.scrapStatus || '-') },
+ { label: '资产评级', node: field('资产评级', 'XXXXXXXXX') },
+ { label: '等评时间', node: field('等评时间', r.regDate || '-') },
+ { label: '强制报废期', node: field('强制报废期', r.scrapDate || '-') },
+ { label: '停车位置', node: field('停车位置', r.parking || '-') },
+ { label: '业务负责人', node: r.manager && r.manager !== '-' ? fieldLink('业务负责人', r.manager, function () { message.info('负责人(原型)'); }) : field('业务负责人', r.manager || '-') },
+ { label: '维修状态', node: field('维修状态', r.repairStatus || '-') },
+ { label: '运营城市', node: field('运营城市', r.region ? r.region.replace(/\//g, '-') : '-') },
+ { label: '下次年检时间', node: field('下次年检时间', r.inspectExpire || '-') },
+ { label: '上次维修时间', node: field('上次维修时间', r.lastDeliveryTime || '-') },
+ { label: 'GPS最后上传时间', node: field('GPS最后上传时间', r.gpsTime || '-') },
+ { label: '车辆当前位置', node: fieldLink('车辆当前位置', r.location || '-', function () { message.info('查看位置(原型)'); }) }
+ ];
+ var rowCount = _detailCardExpanded[0] ? 8 : 3;
+ var rows = [];
+ for (var i = 0; i < rowCount; i++) {
+ var rowCells = [];
+ for (var j = 0; j < 4; j++) {
+ var idx = i * 4 + j;
+ rowCells.push(React.createElement('div', { key: idx, style: gridColStyle }, detailFields[idx] ? detailFields[idx].node : null));
+ }
+ rows.push(React.createElement('div', { key: i, style: { display: 'flex', gap: 24, marginBottom: i < rowCount - 1 ? 16 : 0 } }, rowCells));
+ }
+ var tabContentCardStyle = { background: '#fff', borderRadius: 8, padding: 24, marginTop: 0, boxShadow: '0 1px 2px rgba(0,0,0,0.03)' };
+ var tabContent = function (text) { return React.createElement('div', { style: { padding: '8px 0', color: 'rgba(0,0,0,0.65)', fontSize: 14 } }, text); };
+ var sectionTitleStyle = { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 16, fontSize: 15, fontWeight: 600, color: 'rgba(0,0,0,0.85)' };
+ var sectionIconStyle = { width: 6, height: 6, borderRadius: 3, backgroundColor: '#1890ff', flexShrink: 0 };
+ var kvLabelStyle = { color: 'rgba(0,0,0,0.45)', fontSize: 14, flexShrink: 0, minWidth: 90, textAlign: 'right' };
+ var kvValueStyle = { color: 'rgba(0,0,0,0.85)', fontSize: 14, textAlign: 'left' };
+ var kvItem = function (label, value, badge) {
+ var valNode = value;
+ if (badge) valNode = React.createElement('span', null, value, React.createElement('span', { style: { marginLeft: 6, padding: '0 6px', fontSize: 12, background: '#faad14', color: '#fff', borderRadius: 2 } }, badge));
+ return React.createElement('div', { key: label, style: { display: 'flex', alignItems: 'center', gap: 8 } }, React.createElement('span', { style: kvLabelStyle }, label), React.createElement('span', { style: kvValueStyle }, valNode));
+ };
+ var modelParamRowStyle = { display: 'flex', gap: 24, marginBottom: 16 };
+ var fourColRows = function (nodes) {
+ var out = [];
+ for (var i = 0; i < nodes.length; i += 4) {
+ var rowCells = [];
+ for (var j = 0; j < 4; j++) {
+ var idx = i + j;
+ rowCells.push(React.createElement('div', { key: idx, style: gridColStyle }, nodes[idx] || null));
+ }
+ out.push(React.createElement('div', { key: i, style: modelParamRowStyle }, rowCells));
+ }
+ return out;
+ };
+ var maintenanceTableData = [
+ { key: '1', no: 1, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '' },
+ { key: '2', no: 2, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '' },
+ { key: '3', no: 3, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '5000' },
+ { key: '4', no: 4, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '5000' },
+ { key: '5', no: 5, item: '变速器油', kmCycle: '60000', monthCycle: '24', labor: '0', material: '571', total: '571', lastKm: '5000' }
+ ];
+ var maintenanceColumns = [
+ { title: '序号', dataIndex: 'no', key: 'no', width: 60 },
+ { title: '养护项目', dataIndex: 'item', key: 'item', width: 120 },
+ { title: '保养公里周期(km)', dataIndex: 'kmCycle', key: 'kmCycle', width: 140 },
+ { title: '保养时间周期(月)', dataIndex: 'monthCycle', key: 'monthCycle', width: 130 },
+ { title: '工时费(元)', dataIndex: 'labor', key: 'labor', width: 100 },
+ { title: '材料费(元)', dataIndex: 'material', key: 'material', width: 100 },
+ { title: '合计', dataIndex: 'total', key: 'total', width: 80 },
+ { title: '上次保养公里数(KM)', dataIndex: 'lastKm', key: 'lastKm', width: 140 }
+ ];
+ var modelParamContent = React.createElement('div', { style: tabContentCardStyle },
+ React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '型号参数'),
+ React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([
+ kvItem('品牌', r.brand || '苏龙'),
+ kvItem('型号', r.model || '海格牌KLQ5180XYKFCEV'),
+ kvItem('车辆类型', r.vehicleType || '18吨双飞翼货车'),
+ kvItem('燃料种类', '氢'),
+ kvItem('整车尺寸', '5995mm x 2145mm x 3130mm'),
+ kvItem('车牌颜色', '绿牌')
+ ])),
+ React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '轮胎情况'),
+ React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([
+ kvItem('轮胎数量', '8'),
+ kvItem('轮胎规格', '15/80R22.5')
+ ])),
+ React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '电气系统'),
+ React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([
+ kvItem('电池类型', '磷酸铁锂'),
+ kvItem('电池厂家', 'XXXXXXXXXXXXX企业名称'),
+ kvItem('储电量', '100000 kWh'),
+ kvItem('续航里程', '200 KM')
+ ])),
+ React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '供氢系统'),
+ React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([
+ kvItem('氢瓶容量', 'xxx L'),
+ kvItem('仪表盘模式', 'MPa'),
+ kvItem('续航里程', '1000 KM'),
+ kvItem('供氢系统厂家', 'XXXXXXXX企业')
+ ])),
+ React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '其他系统'),
+ React.createElement('div', { style: { marginBottom: 24 } }, fourColRows([
+ kvItem('冷机生产企业', 'XXXXXXXX企业'),
+ kvItem('电堆生产企业', 'XXXXXXXX企业')
+ ])),
+ React.createElement('div', { style: sectionTitleStyle }, React.createElement('span', { style: sectionIconStyle }), '保养参数'),
+ React.createElement(Table, { size: 'small', rowKey: 'key', columns: maintenanceColumns, dataSource: maintenanceTableData, pagination: false })
+ );
+ var detailTabItems = [
+ { key: '型号参数', label: '型号参数', children: modelParamContent },
+ { key: '后装设备', label: '后装设备', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('后装设备列表(原型占位)')) },
+ { key: '证照信息', label: '证照信息', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('证照信息(原型占位)')) },
+ { key: '租赁记录', label: '租赁记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('租赁记录(原型占位)')) },
+ { key: '保险记录', label: '保险记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('保险记录(原型占位)')) },
+ { key: '维修记录', label: '维修记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('维修记录(原型占位)')) },
+ { key: '事故记录', label: '事故记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('事故记录(原型占位)')) },
+ { key: '故障记录', label: '故障记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('故障记录(原型占位)')) },
+ { key: '违章记录', label: '违章记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('违章记录(原型占位)')) },
+ { key: '异动记录', label: '异动记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('异动记录(原型占位)')) },
+ { key: '调拨记录', label: '调拨记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('调拨记录(原型占位)')) },
+ { key: '整备记录', label: '整备记录', children: React.createElement('div', { style: tabContentCardStyle }, tabContent('整备记录(原型占位)')) }
+ ];
+ return React.createElement(App, null,
+ React.createElement('div', { style: layoutStyle },
+ React.createElement(Breadcrumb, {
+ style: { marginBottom: 16 },
+ items: [
+ { title: '运维管理' },
+ { title: React.createElement('a', { onClick: backToList, style: { color: '#1890ff' } }, '车辆管理') },
+ { title: '车辆详情' }
+ ]
+ }),
+ React.createElement('div', { style: detailCardStyle },
+ React.createElement('div', { style: { fontSize: 16, fontWeight: 600, color: 'rgba(0,0,0,0.85)', marginBottom: 24 } }, '车辆详情'),
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, marginBottom: 24 } },
+ React.createElement('span', { style: { width: 8, height: 8, borderRadius: 4, backgroundColor: r.onlineStatus === '在线' ? '#52c41a' : '#d9d9d9', flexShrink: 0 } }),
+ React.createElement('span', { style: { fontSize: 16, fontWeight: 600, color: 'rgba(0,0,0,0.85)' } }, r.plateNo || '-'),
+ React.createElement(Button, { type: 'primary', size: 'small' }, r.operateStatus || '租赁')
+ ),
+ React.createElement('div', { style: { marginBottom: 24 } }, rows),
+ React.createElement('div', { style: { textAlign: 'center', paddingTop: 16, marginTop: 8, borderTop: '1px solid #f0f0f0' } },
+ React.createElement(Button, { type: 'link', size: 'small', onClick: function () { _detailCardExpanded[1](!_detailCardExpanded[0]); } }, _detailCardExpanded[0] ? '收起' : '展开')
+ )
+ ),
+ React.createElement('div', { style: Object.assign({}, detailCardStyle, { marginTop: 24 }) },
+ React.createElement(Tabs, {
+ activeKey: _detailTab[0],
+ onChange: function (key) { _detailTab[1](key); },
+ items: detailTabItems
+ })
+ )
+ )
+ );
+ }
+
+ var requirementText = '# 车辆管理\n一个「车辆资产管理」后台的「车辆管理」模块,功能从上到下依次为:\n\n1.面包屑:\n运维管理-车辆管理\n\n2.筛选:\n2.1.运营城市:地区选择器,支持省-市2级地区选择;\n2.2.车辆类型:选择器,根据车辆类型表拉取;\n2.3.品牌:选择器,根据型号参数表拉取品牌字段;\n2.4.型号:选择器,根据型号参数表拉取型号字段;\n2.5.客户名称:选择器,根据租赁合同/自营合同对应客户名称拉取,并包含无;\n2.6.归属业务部门:选择器,根据租赁合同/自营合同对应归属业务部门拉取,并包含无;\n2.7.合同编号:选择器,支持输入框内输入内容模糊匹配选项;\n2.8.登记所有权:选择器,支持输入框内输入内容模糊匹配选项;\n\n3.列表:\n列表左上角为车牌号筛选框,输入车牌号可快速筛选列表内记录;右侧按钮为导出(联动多选)、批量导入,字段依次为运营城市、车辆识别代号、车牌号、车辆编号、车辆类型、品牌、型号、车身颜色、归属停车场、客户名称、业务部门、业务负责人、运营状态、库位状态、出库状态、预占状态、整备状态、过户状态、维修状态、证照状态、报废状态、登记所有权、在线状态、出厂年份、行驶公里数、采购入库时间、行驶证注册日期、行驶证检验有效期、上次交车时间、上次交车里程(KM)、上次还车时间、上次还车里程(KM)、强制报废日期、合同编号、当前位置、GPS最后上传时间、操作;\n3.1.运营城市:列固定在左侧,车辆实际运营城市,通过GPS当前位置获取,只显示至省-市2级;\n3.2.车辆识别代号:列固定在左侧,显示车辆识别代号(VIN码),为唯一值,采购、外部租赁等方式录入的车架号与系统现有车架号不能重复,导入亦然;\n3.3.车牌号:列固定在左侧,显示车牌号,主要通过上牌管理功能录入;\n3.4.车辆编号:显示车辆编号,主要由旧版迁移数据获取(这批车在港口需要单独以编号记录),其他车辆无此需求;\n3.5.车辆类型:显示车辆对应车辆类型,由车辆类型表中获取;\n3.6.品牌:显示车辆对应品牌,由型号参数表-品牌字段获取;\n3.7.型号:显示车辆对应型号,由型号参数表-型号字段获取;\n3.8.车身颜色:显示车辆对应颜色,由用户采购/外部租赁车辆合同时填写车身颜色(旧版迁移数据)字段获取;\n3.9.归属停车场:显示车辆归属停车场,交车出去租赁给客户的车辆显示为-,还车或发生异动时,根据运维记录,记录最后停放的停车场,停车场从停车场表中获取;\n3.10.客户名称:显示车辆租赁/自营合同发生时,该合同对应客户名称;\n3.11.业务部门:显示车辆租赁/自营合同发生时,该合同对应业务部门;\n3.12.业务负责人:显示车辆租赁/自营合同发生时,该合同对应业务负责人;\n3.13.运营状态:分为待运营、库存、租赁、自营、退出运营\n3.14.库位状态:分为新车入库-待验车、新车入库-证照办理、库存车-可交付车、库存车-不可交付车、库存车-呆滞车、已交付车-租赁交车、已交付车-自营交车、已交付车-替换交车、退出运营-报废车、退出运营-三方退租车、退出运营-过户售车;\n3.15.出库状态:分为异动出库、调拨出库、展示出库、租赁交车、自营交车、替换交车、过户售车、外租退车、报废出库、无;\n3.16.预占状态:331前占位,该功能331后迭代;\n3.17.整备状态:分为待整备、整备中、正常、无;\n3.18.过户状态:分为过户中、内部过户完成、销售过户完成、无;\n3.19.维修状态:分为待服务站接单、维修中、正常;\n3.20.证照状态:分为正常、异常;\n3.21.报废状态:分为报废中、已报废、无;\n3.22.登记所有权:行驶证对应所有人;\n3.23.在线状态:分为在线、离线,显示GPS在线状态,根据实际对接设备取状态;\n3.24.出厂年份:格式为YYYY,根据车辆采购合同时记录的出厂年份获取;\n3.25.行驶公里数(KM):精确至2位小数,根据最近一次交车/还车/备车/异动/调拨记录时记录的行驶公里数获取;\n3.26.采购入库时间:格式为YYYY-MM-DD,根据采购合同时,采购完成车辆验车单提交时的时间;\n3.27.行驶证注册日期:格式为YYYY-MM-DD,根据上牌管理中,最后一次上传行驶证时的行驶证注册日期获取;\n3.28.行驶证检验有效期:根据行驶证(副联)中,检验记录中检验有效期获取;\n3.29.上次交车时间:格式为YYYY-MM-DD,根据最近一次交车记录完成时间获取;\n3.30.上次交车里程(KM):精确至2位小数,根据最近一次交车记录中记录的里程获取;\n3.31.上次还车日期:格式为YYYY-MM-DD,根据最近一次还车记录完成时间获取;\n3.32.上次还车里程(KM):精确至2位小数,根据最近一次还车记录中记录的里程获取;\n3.33.强制报废日期:格式为YYYY-MM-DD,根据行驶证(副联)中强制报废期获取;\n3.34.合同编号:显示当前租赁/自营合同合同编号,如无则显示为-;\n3.35.当前位置:根据车辆GPS当前定位获取,格式为xxx省xx市xx区/县xxxxx路xxxxx号xxxxxx\n3.36.GPS最后上传时间:格式为YYYY-MM-DD HH:MM,显示车辆GPS最后一次获取时间\n3.37.操作:操作列固定在右侧,显示查看、更多,悬浮更多时,显示车辆上牌、车辆过户、车辆异动、车辆调拨、车辆报废、车辆年审、车辆出售、故障提报;\n3.37.1.查看,点击跳转车辆详情页;\n3.38.2.车辆上牌,点击后上传照片,OCR识别过程中卡片提示:识别中,请勿关闭页面,之后弹出卡片,标题为确认上牌信息,左侧为行驶证照片,右侧为车辆识别代号(输入框,根据行驶证照片反写,可编辑)与车牌号(输入框,根据行驶证照片反写,可编辑),下侧为取消和确认按钮,点击确认,判断车辆识别代码与车牌号是否一致,如不一致提示车辆识别代号与该车辆不匹配,如一致则更新车牌号和行驶证照片(具体参考车辆业务-上牌管理);\n3.38.3.车辆过户:\n3.38.4.车辆异动:\n3.38.5.车辆调拨:\n3.38.6.车辆报废:\n3.38.7.车辆年审:\n3.38.8.车辆出售;\n3.38.9.故障提报:';
+
+ return React.createElement(App, null,
+ React.createElement('div', { style: layoutStyle },
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } },
+ React.createElement(Breadcrumb, { items: [{ title: '运维管理' }, { title: '车辆管理' }] }),
+ React.createElement(Button, { type: 'link', onClick: function () { _requirementModalVisible[1](true); } }, '查看需求说明')
+ ),
+
+ React.createElement('div', { style: { marginBottom: 16, display: 'flex', gap: 24, alignItems: 'flex-start' } },
+ React.createElement('div', {
+ style: {
+ display: 'grid',
+ gridTemplateColumns: '1fr 1fr 1fr',
+ gap: '16px 24px',
+ alignItems: 'start',
+ flex: 1,
+ minWidth: 0
+ }
+ }, (function () {
+ var filterItems = [
+ React.createElement('div', { key: 'region', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '运营城市'), React.createElement(Cascader, { options: regionOptions, placeholder: '请选择', style: filterControlStyle, value: _region[0].length ? _region[0] : undefined, onChange: function (v) { _region[1](v || []); } })),
+ React.createElement('div', { key: 'type', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '车辆类型'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: vehicleTypeOptions, value: _vehicleType[0], onChange: _vehicleType[1], allowClear: true })),
+ React.createElement('div', { key: 'brand', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '品牌'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: brandOptions, value: _brand[0], onChange: _brand[1], allowClear: true })),
+ React.createElement('div', { key: 'model', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '型号'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: modelOptions, value: _model[0], onChange: _model[1], allowClear: true })),
+ React.createElement('div', { key: 'customer', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '客户名称'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: customerOptions, value: _customer[0], onChange: _customer[1], allowClear: true })),
+ React.createElement('div', { key: 'dept', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '归属业务部门'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: departmentOptions, value: _department[0], onChange: _department[1], allowClear: true })),
+ React.createElement('div', { key: 'contract', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '合同编号'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: contractOptions, value: _contractNo[0], onChange: _contractNo[1], allowClear: true, showSearch: true, filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } })),
+ React.createElement('div', { key: 'ownership', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '登记所有权'), React.createElement(Select, { placeholder: '请选择', style: filterControlStyle, options: ownershipOptions, value: _ownership[0], onChange: _ownership[1], allowClear: true, showSearch: true, filterOption: function (input, opt) { return (opt.label || '').toString().toLowerCase().indexOf((input || '').toLowerCase()) !== -1; } }))
+ ];
+ var limit = _filterExpanded[0] ? 8 : 6;
+ var out = [];
+ for (var i = 0; i < limit && i < filterItems.length; i++) {
+ out.push(filterItems[i]);
+ }
+ return out;
+ })()),
+ React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 8, flexShrink: 0 } },
+ React.createElement(Button, { type: 'primary', onClick: function () { message.info('查询(原型)'); } }, '查询'),
+ React.createElement(Button, { onClick: function () {
+ _region[1]([]);
+ _vehicleType[1](undefined);
+ _brand[1](undefined);
+ _model[1](undefined);
+ _customer[1](undefined);
+ _department[1](undefined);
+ _contractNo[1](undefined);
+ _ownership[1](undefined);
+ } }, '重置'),
+ React.createElement(Button, { type: 'link', size: 'small', onClick: function () { _filterExpanded[1](!_filterExpanded[0]); } }, _filterExpanded[0] ? '收起' : '展开')
+ )
+ ),
+
+ React.createElement('div', { style: toolbarStyle },
+ React.createElement(Input.Search, {
+ placeholder: '输入车牌号筛选',
+ allowClear: true,
+ value: _plateFilter[0],
+ onChange: onPlateFilterChange,
+ onSearch: onPlateFilterChange,
+ style: { width: 220 }
+ }),
+ React.createElement(Space, null,
+ React.createElement(Button, { onClick: handleExport }, '导出'),
+ React.createElement(Button, { onClick: handleBatchImport }, '批量导入'),
+ React.createElement(Button, { onClick: openInspectImportModal }, '批量导入等评时间')
+ )
+ ),
+
+ React.createElement(React.Fragment, null,
+ React.createElement('style', null, tableSingleLineStyle),
+ React.createElement('div', { className: 'vehicle-mgmt-table' },
+ React.createElement(Table, {
+ rowKey: 'id',
+ columns: columns,
+ dataSource: dataSource,
+ rowSelection: rowSelection,
+ scroll: { x: 3800 },
+ size: 'small',
+ pagination: { showSizeChanger: true, showQuickJumper: true, showTotal: function (t) { return '共 ' + t + ' 条'; } }
+ })
+ )
+ ),
+
+ // 车辆上牌:上传/识别中
+ React.createElement(Modal, {
+ title: '上传行驶证',
+ open: _uploadModalVisible[0],
+ onCancel: closeUploadModal,
+ footer: [
+ React.createElement(Button, { key: 'cancel', onClick: closeUploadModal }, '取消'),
+ React.createElement(Button, { key: 'ok', type: 'primary', onClick: startOcrThenConfirm }, '开始识别')
+ ]
+ }, React.createElement(Upload.Dragger, { accept: 'image/*', multiple: false }, React.createElement('p', null, '点击或拖拽行驶证照片到此区域上传'))),
+
+ React.createElement(Modal, {
+ title: '识别中,请勿关闭页面',
+ open: _ocrLoadingVisible[0],
+ footer: null,
+ closable: false,
+ maskClosable: false
+ }, React.createElement('div', { style: { textAlign: 'center', padding: '24px 0' } }, '正在识别行驶证信息...')),
+
+ React.createElement(Modal, {
+ title: '确认上牌信息',
+ open: _confirmModalVisible[0],
+ onCancel: closeConfirmModal,
+ width: 640,
+ footer: [
+ React.createElement(Button, { key: 'cancel', onClick: closeConfirmModal }, '取消'),
+ React.createElement(Button, { key: 'ok', type: 'primary', onClick: confirmPlate }, '确认')
+ ]
+ }, React.createElement('div', { style: { display: 'flex', gap: 24 } },
+ React.createElement('div', { style: { flex: '0 0 240px' } },
+ React.createElement('div', { style: { marginBottom: 8, color: '#666' } }, '行驶证照片'),
+ React.createElement('img', { src: 'https://picsum.photos/240/160', alt: '行驶证', style: { width: '100%', borderRadius: 8 } })
+ ),
+ React.createElement('div', { style: { flex: 1 } },
+ React.createElement('div', { style: { marginBottom: 12 } },
+ React.createElement('span', { style: { marginRight: 8 } }, '车辆识别代号'),
+ React.createElement(Input, {
+ value: _plateForm[0].vin,
+ onChange: function (e) { onPlateFormChange('vin', e.target.value); },
+ placeholder: '根据行驶证反写,可编辑',
+ style: { width: '100%' }
+ })
+ ),
+ React.createElement('div', { style: { marginBottom: 12 } },
+ React.createElement('span', { style: { marginRight: 8 } }, '车牌号'),
+ React.createElement(Input, {
+ value: _plateForm[0].plateNo,
+ onChange: function (e) { onPlateFormChange('plateNo', e.target.value); },
+ placeholder: '根据行驶证反写,可编辑',
+ style: { width: '100%' }
+ })
+ ),
+ _plateError[0] ? React.createElement('div', { style: { color: '#ff4d4f', fontSize: 12 } }, _plateError[0]) : null
+ )
+ )),
+ React.createElement(Modal, {
+ title: '需求说明',
+ open: _requirementModalVisible[0],
+ onCancel: function () { _requirementModalVisible[1](false); },
+ width: 720,
+ footer: React.createElement(Button, { onClick: function () { _requirementModalVisible[1](false); } }, '关闭')
+ }, React.createElement('div', { style: { maxHeight: 560, overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6, color: 'rgba(0,0,0,0.85)' } }, requirementText)),
+
+ React.createElement(Modal, {
+ title: '批量导入等评时间',
+ open: _inspectImportModalVisible[0],
+ onCancel: closeInspectImportModal,
+ width: 520,
+ footer: React.createElement(Button, { onClick: closeInspectImportModal }, '关闭')
+ }, React.createElement('div', null,
+ React.createElement('div', { style: { marginBottom: 16 } },
+ React.createElement(Upload.Dragger, {
+ accept: '.csv',
+ showUploadList: false,
+ beforeUpload: function (file) { handleInspectImportUpload(file); return false; }
+ }, React.createElement('p', { style: { margin: 0, padding: '16px 0' } }, '点击或拖拽 CSV 文件到此区域批量上传')),
+ React.createElement(Button, { type: 'link', onClick: downloadInspectTemplate, style: { marginTop: 8, padding: 0 } }, '下载导入模板')
+ ),
+ _inspectImportResult[0] && _inspectImportResult[0].hasFailed ? React.createElement('div', { style: { marginTop: 16, padding: 12, background: '#fff2f0', border: '1px solid #ffccc7', borderRadius: 4 } },
+ React.createElement('span', { style: { color: 'rgba(0,0,0,0.85)' } }, '上传失败,'),
+ React.createElement('a', { href: '#', onClick: function (e) { e.preventDefault(); downloadInspectFailedCsv(); }, style: { color: '#1890ff' } }, '查看失败记录')
+ ) : null
+ ))
+ )
+ );
+};
diff --git a/web端/运维管理/车辆业务/交车管理-交车单-查看.jsx b/web端/运维管理/车辆业务/交车管理-交车单-查看.jsx
new file mode 100644
index 0000000..3ddf8c1
--- /dev/null
+++ b/web端/运维管理/车辆业务/交车管理-交车单-查看.jsx
@@ -0,0 +1,494 @@
+// 【重要】必须使用 const Component 作为组件变量名
+// 运维管理 - 车辆业务 - 交车管理 - 交车单 - 查看(只读)
+
+const Component = function () {
+ var useState = React.useState;
+ var useMemo = React.useMemo;
+ 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 Select = antd.Select;
+ var Switch = antd.Switch;
+ var Modal = antd.Modal;
+ var Drawer = antd.Drawer;
+ var message = antd.message;
+
+ // ---------- utils ----------
+ 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 toFixed2(v) {
+ if (v === null || v === undefined || v === '') return '';
+ var n = typeof v === 'number' ? v : parseFloat(v);
+ return isNaN(n) ? '' : n.toFixed(2);
+ }
+
+ function isEmpty(v) {
+ return v === null || v === undefined || String(v).trim() === '';
+ }
+
+ function filterOption(input, option) {
+ var label = (option && (option.label || option.children)) || '';
+ return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0;
+ }
+
+ function makeThumb(url, onPreview) {
+ return React.createElement('div', {
+ style: {
+ width: 64,
+ height: 64,
+ borderRadius: 4,
+ border: '1px solid #f0f0f0',
+ background: '#fafafa',
+ position: 'relative',
+ overflow: 'hidden'
+ }
+ },
+ React.createElement('img', {
+ src: url,
+ style: { width: '100%', height: '100%', objectFit: 'cover', cursor: 'pointer' },
+ onClick: onPreview
+ })
+ );
+ }
+
+ function ReadonlyUploadBox(props) {
+ var label = props.label;
+ var value = props.value; // array of {name,url}
+ var tip = props.tip;
+ var onPreview = props.onPreview;
+
+ return React.createElement('div', null,
+ React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, label),
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } },
+ (value || []).length > 0
+ ? (value || []).map(function (f) {
+ return React.createElement('div', { key: f.uid },
+ makeThumb(f.url, function () { onPreview && onPreview(f); })
+ );
+ })
+ : React.createElement('div', { style: { fontSize: 12, color: '#999' } }, '暂无')
+ ),
+ tip ? React.createElement('div', { style: { marginTop: 6, fontSize: 12, color: '#999' } }, tip) : null
+ );
+ }
+
+ // ---------- styles ----------
+ 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 styles = {
+ formRow: { display: 'flex', gap: 16, marginBottom: 12, alignItems: 'flex-start' },
+ formItem: { flex: 1, minWidth: 0 },
+ label: { fontSize: 12, color: '#666', marginBottom: 6 }
+ };
+
+ function FormItem(props) {
+ var label = props.label;
+ var fullWidth = !!props.fullWidth;
+ return React.createElement('div', { style: fullWidth ? Object.assign({}, styles.formItem, { flex: '0 0 100%' }) : styles.formItem },
+ React.createElement('div', { style: styles.label }, label),
+ props.children
+ );
+ }
+
+ // ---------- mock data (只读样例) ----------
+ var reserveVehicles = useMemo(function () {
+ return [
+ { plateNo: '京A12345', vehicleType: '牵引车', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', hasAd: true, hasTailboard: true },
+ { plateNo: '京C11111', vehicleType: '厢式货车', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', hasAd: false, hasTailboard: false }
+ ];
+ }, []);
+
+ var plateOptions = useMemo(function () {
+ return reserveVehicles.map(function (v) { return { value: v.plateNo, label: v.plateNo }; });
+ }, [reserveVehicles]);
+
+ var initialForm = useMemo(function () {
+ return {
+ plateNo: '京A12345',
+ vehicleType: '牵引车',
+ brand: '东风',
+ model: 'DFH1180',
+ vin: 'LGHXCAE28M1234567',
+
+ hasAd: true,
+ adPhoto: [{ uid: 'ad1', name: '广告照片.jpg', url: 'https://dummyimage.com/640x360/eee/666&text=Ad' }],
+ bigWordPhoto: [{ uid: 'bw1', name: '放大字照片.jpg', url: 'https://dummyimage.com/640x360/eee/666&text=BigWord' }],
+
+ hasTailboard: true,
+
+ spareTirePhoto: [{ uid: 'sp1', name: '备胎照片.jpg', url: 'https://dummyimage.com/640x360/eee/666&text=SpareTire' }],
+ spareTireDepth: '6.50',
+
+ trainingRecognized: true,
+ driverLicenses: [
+ { uid: 'id1', name: '身份证正面.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=ID-Front' },
+ { uid: 'id2', name: '身份证反面.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=ID-Back' },
+ { uid: 'dl', name: '驾驶证.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=DriverLicense' },
+ { uid: 'qc', name: '从业资格证.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=Qualification' }
+ ],
+
+ mileageKm: '120',
+ batteryKwh: '86',
+ hydrogenAmount: '35.60',
+ serviceFee: '200.00'
+ };
+ }, []);
+
+ var photos = useMemo(function () {
+ function one(uid, name) {
+ return [{ uid: uid, name: name + '.jpg', url: 'https://dummyimage.com/640x360/eee/666&text=' + encodeURIComponent(name) }];
+ }
+ return {
+ vehicle: {
+ '仪表盘': one('v1', '车辆-仪表盘'),
+ '车辆正面': one('v2', '车辆-正面'),
+ '车辆左前方': one('v3', '车辆-左前方'),
+ '车辆左后方': one('v4', '车辆-左后方'),
+ '车辆右后方': one('v5', '车辆-右后方'),
+ '车辆右前方': one('v6', '车辆-右前方')
+ },
+ chassis: {
+ '正前方底部': one('c1', '底盘-正前方'),
+ '左侧前方底部': one('c2', '底盘-左前'),
+ '左侧后方底部': one('c3', '底盘-左后'),
+ '正后方底部': one('c4', '底盘-正后方'),
+ '右侧后方底部': one('c5', '底盘-右后'),
+ '右侧前方底部': one('c6', '底盘-右前')
+ },
+ tire: {
+ '左前轮': one('t1', '轮胎-左前'),
+ '左后轮(内)': one('t2', '轮胎-左后内'),
+ '左后轮(外)': one('t3', '轮胎-左后外'),
+ '右前轮': one('t4', '轮胎-右前'),
+ '右后轮(内)': one('t5', '轮胎-右后内'),
+ '右后轮(外)': one('t6', '轮胎-右后外'),
+ '备胎': one('t7', '轮胎-备胎')
+ },
+ defect: [{ uid: 'd1', name: '瑕疵1.jpg', url: 'https://dummyimage.com/640x360/eee/666&text=Defect-1' }],
+ other: [{ uid: 'o1', name: '其他1.jpg', url: 'https://dummyimage.com/640x360/eee/666&text=Other-1' }]
+ };
+ }, []);
+
+ // ---------- inspection checklist (只读样例) ----------
+ var inspectionCategoryItems = useMemo(function () {
+ return {
+ '车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'],
+ '仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'],
+ '驾驶室': ['点烟器', '车窗升降', '按键开关', '雨刮器', '内后视镜是否正常', '内/外门把手', '安全带', '空调冷暖风', '仪表盘', '门锁功能', '手刹', '车钥匙功能是否正常', '喇叭', '音响功能', '遮阳板', '主副驾座椅', '方向盘', '内饰干净整洁'],
+ '轮胎': ['前左胎', '前右胎', '后左胎', '后右胎', '备胎'],
+ '液位检查': ['冷却液', '制动液', '玻璃水'],
+ '外观检查': ['车身外观', '漆面', '玻璃'],
+ '车辆外观': ['整车外观'],
+ '其他': ['其他检查项'],
+ '随车工具': ['三角牌', '灭火器', '反光背心'],
+ '随车证件': ['行驶证', '营运证', '保险单'],
+ '整车': ['整车状态'],
+ '燃料电池系统': ['氢系统', '储氢瓶'],
+ '冷机': ['冷机运行'],
+ '制动系统': ['制动踏板', '驻车制动']
+ };
+ }, []);
+
+ var inspectionList = useMemo(function () {
+ var list = [];
+ var cats = Object.keys(inspectionCategoryItems);
+ for (var i = 0; i < cats.length; i++) {
+ var cat = cats[i];
+ var items = inspectionCategoryItems[cat] || [];
+ for (var j = 0; j < items.length; j++) {
+ var it = items[j];
+ var isTire = cat === '轮胎';
+ list.push({
+ key: 'ins-' + i + '-' + j,
+ category: cat,
+ item: it,
+ checked: true,
+ treadDepth: isTire ? '6.5' : '',
+ remark: ''
+ });
+ }
+ }
+ return list;
+ }, [inspectionCategoryItems]);
+
+ var inspectionListRef = useRef(null);
+ inspectionListRef.current = inspectionList;
+
+ var inspectionColumns = useMemo(function () {
+ return [
+ {
+ title: '类别',
+ dataIndex: 'category',
+ key: 'category',
+ width: 140,
+ render: function (text, record, index) {
+ var rows = inspectionListRef.current || [];
+ var cat = record && record.category;
+ if (!cat) return { children: text, props: { rowSpan: 1 } };
+
+ var isFirst = true;
+ for (var i = index - 1; i >= 0; i--) {
+ if (!rows[i] || rows[i].category !== cat) break;
+ isFirst = false;
+ break;
+ }
+ if (!isFirst) return { children: null, props: { rowSpan: 0 } };
+
+ var span = 1;
+ for (var j = index + 1; j < rows.length; j++) {
+ if (!rows[j] || rows[j].category !== cat) break;
+ span++;
+ }
+ return { children: text, props: { rowSpan: span } };
+ }
+ },
+ { title: '检查项目', dataIndex: 'item', key: 'item', width: 220 },
+ {
+ title: '检查情况',
+ dataIndex: 'checked',
+ key: 'checked',
+ width: 220,
+ render: function (_, record) {
+ var isTire = record && record.category === '轮胎';
+ return isTire
+ ? React.createElement(Input, { value: record.treadDepth, disabled: true, placeholder: '请输入胎纹深度', addonAfter: 'mm' })
+ : React.createElement(Switch, { checked: !!record.checked, disabled: true });
+ }
+ },
+ {
+ title: '备注',
+ dataIndex: 'remark',
+ key: 'remark',
+ render: function (_, record) {
+ return React.createElement(Input, { value: record.remark, disabled: true, placeholder: '请输入' });
+ }
+ }
+ ];
+ }, []);
+
+ // ---------- states ----------
+ var previewState = useState({ open: false, url: '', title: '' });
+ var inspectionDrawerOpenState = useState(false);
+
+ function openPreview(file) {
+ if (!file) return;
+ previewState[1]({ open: true, url: file.url, title: file.name || '预览' });
+ }
+
+ function goBack() {
+ try {
+ if (window.history && window.history.length > 1) window.history.back();
+ else message.info('返回上一页(原型)');
+ } catch (e) {
+ message.info('返回上一页(原型)');
+ }
+ }
+
+ // ---------- render helpers ----------
+ function PhotoGridColumn(title, items, required) {
+ return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: 12 } },
+ React.createElement('div', { style: { fontWeight: 600, marginBottom: 12 } }, required ? RequiredLabel(title) : title),
+ React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12 } },
+ items.map(function (it) {
+ return React.createElement('div', { key: it.key },
+ ReadonlyUploadBox({ label: required ? RequiredLabel(it.label) : it.label, value: it.value, onPreview: openPreview })
+ );
+ })
+ )
+ );
+ }
+
+ function PhotoGridSimple(title, value) {
+ return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: 12 } },
+ React.createElement('div', { style: { fontWeight: 600, marginBottom: 12 } }, title),
+ ReadonlyUploadBox({ label: '照片', value: value, onPreview: openPreview })
+ );
+ }
+
+ 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: '车辆业务' }, { title: '交车管理' }, { title: '交车单-查看' }] })
+ ),
+
+ React.createElement(Card, { title: '交车明细', style: cardStyle },
+ React.createElement('div', null,
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '车牌号' },
+ React.createElement(Select, {
+ value: initialForm.plateNo,
+ options: plateOptions,
+ showSearch: true,
+ filterOption: filterOption,
+ placeholder: '请输入或选择车牌号',
+ allowClear: true,
+ disabled: true,
+ style: { width: '100%' }
+ })
+ ),
+ React.createElement(FormItem, { label: '车辆类型' },
+ React.createElement(Input, { value: initialForm.vehicleType, disabled: true })
+ )
+ ),
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '品牌' },
+ React.createElement(Input, { value: initialForm.brand, disabled: true })
+ ),
+ React.createElement(FormItem, { label: '型号' },
+ React.createElement(Input, { value: initialForm.model, disabled: true })
+ )
+ ),
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '车辆识别代码' },
+ React.createElement(Input, { value: initialForm.vin, disabled: true })
+ ),
+ React.createElement('div', { style: styles.formItem })
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '车身广告及放大字', fullWidth: true },
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10 } },
+ React.createElement(Switch, { checked: !!initialForm.hasAd, disabled: true }),
+ React.createElement('span', { style: { color: '#666', fontSize: 12 } }, initialForm.hasAd ? '有车身广告' : '无车身广告')
+ )
+ )
+ ),
+ initialForm.hasAd ? React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 12 } },
+ React.createElement('div', null,
+ ReadonlyUploadBox({ label: '广告照片', value: initialForm.adPhoto, max: 1, onPreview: openPreview })
+ ),
+ React.createElement('div', null,
+ ReadonlyUploadBox({ label: '放大字照片', value: initialForm.bigWordPhoto, max: 1, onPreview: openPreview })
+ )
+ ) : null,
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '尾板', fullWidth: true },
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10 } },
+ React.createElement(Switch, { checked: !!initialForm.hasTailboard, disabled: true }),
+ React.createElement('span', { style: { color: '#666', fontSize: 12 } }, initialForm.hasTailboard ? '有尾板' : '无尾板')
+ )
+ )
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '备胎照片' },
+ ReadonlyUploadBox({ label: '', value: initialForm.spareTirePhoto, onPreview: openPreview })
+ ),
+ React.createElement(FormItem, { label: '备胎胎纹深度' },
+ React.createElement(Input, { value: initialForm.spareTireDepth, placeholder: '请输入备胎胎纹深度', addonAfter: 'mm', disabled: true })
+ )
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '驾驶培训', fullWidth: true },
+ React.createElement('span', { style: { color: '#52c41a', fontWeight: 600 } }, '已完成视频培训')
+ )
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '司机证照', fullWidth: true },
+ React.createElement('div', null,
+ (initialForm.driverLicenses && initialForm.driverLicenses.length > 0)
+ ? React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 } },
+ initialForm.driverLicenses.map(function (f) {
+ return React.createElement('div', { key: f.uid },
+ makeThumb(f.url, function () { previewState[1]({ open: true, url: f.url, title: f.name || '预览' }); }),
+ React.createElement('div', { style: { marginTop: 6, fontSize: 12, color: '#666' } }, f.name)
+ );
+ })
+ )
+ : React.createElement('div', { style: { fontSize: 12, color: '#999' } }, '上传验车码后显示')
+ )
+ )
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '交车里程' },
+ React.createElement(Input, { value: initialForm.mileageKm, addonAfter: '公里', disabled: true })
+ ),
+ React.createElement(FormItem, { label: '交车电量' },
+ React.createElement(Input, { value: initialForm.batteryKwh, addonAfter: 'kWh', disabled: true })
+ )
+ ),
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '交车氢量' },
+ React.createElement(Input, { value: initialForm.hydrogenAmount, addonAfter: 'MPa', disabled: true })
+ ),
+ React.createElement(FormItem, { label: '送车服务费' },
+ React.createElement(Input, { value: isEmpty(initialForm.serviceFee) ? '' : toFixed2(initialForm.serviceFee), placeholder: '请输入送车服务费金额', addonAfter: '元', disabled: true })
+ )
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '车辆检查', fullWidth: true },
+ React.createElement(Button, { onClick: function () { inspectionDrawerOpenState[1](true); } }, '交车检查单')
+ )
+ )
+ )
+ ),
+
+ React.createElement(Card, { title: '交车照片', style: cardStyle },
+ React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 12 } },
+ PhotoGridColumn('车辆', Object.keys(photos.vehicle).map(function (k) {
+ return { key: 'v-' + k, label: k, value: photos.vehicle[k] };
+ }), true),
+ PhotoGridColumn('底盘', Object.keys(photos.chassis).map(function (k) {
+ return { key: 'c-' + k, label: k, value: photos.chassis[k] };
+ }), true),
+ PhotoGridColumn('轮胎', Object.keys(photos.tire).map(function (k) {
+ return { key: 't-' + k, label: k, value: photos.tire[k] };
+ }), true)
+ ),
+ React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 12, marginTop: 12 } },
+ PhotoGridSimple('瑕疵', photos.defect),
+ PhotoGridSimple('其他', photos.other),
+ React.createElement('div', { style: { background: 'transparent' } })
+ )
+ ),
+
+ React.createElement('div', { style: footerStyle },
+ React.createElement(Button, { onClick: goBack }, '返回')
+ ),
+
+ React.createElement(Modal, {
+ open: !!previewState[0].open,
+ title: previewState[0].title || '预览',
+ footer: null,
+ onCancel: function () { previewState[1]({ open: false, url: '', title: '' }); },
+ width: 860
+ },
+ React.createElement('div', { style: { textAlign: 'center' } },
+ previewState[0].url ? React.createElement('img', { src: previewState[0].url, style: { maxWidth: '100%', maxHeight: '70vh' } }) : null
+ )
+ ),
+
+ React.createElement(Drawer, {
+ open: inspectionDrawerOpenState[0],
+ title: '交车检查单',
+ width: 920,
+ placement: 'right',
+ onClose: function () { inspectionDrawerOpenState[1](false); },
+ styles: { body: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', paddingBottom: 0 } },
+ footer: React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-start', gap: 8 } },
+ React.createElement(Button, { onClick: function () { inspectionDrawerOpenState[1](false); } }, '返回')
+ )
+ },
+ React.createElement('div', { style: { flex: 1, minHeight: 0, overflow: 'auto' } },
+ React.createElement(Table, { rowKey: 'key', columns: inspectionColumns, dataSource: inspectionList, pagination: false, bordered: true, size: 'small' })
+ )
+ )
+ );
+};
+
diff --git a/web端/运维管理/车辆业务/交车管理-交车单-编辑.jsx b/web端/运维管理/车辆业务/交车管理-交车单-编辑.jsx
new file mode 100644
index 0000000..6657c58
--- /dev/null
+++ b/web端/运维管理/车辆业务/交车管理-交车单-编辑.jsx
@@ -0,0 +1,854 @@
+// 【重要】必须使用 const Component 作为组件变量名
+// 运维管理 - 车辆业务 - 交车管理 - 交车单 - 编辑
+
+const Component = function () {
+ var useState = React.useState;
+ var useMemo = React.useMemo;
+ var useCallback = React.useCallback;
+ 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 Select = antd.Select;
+ var Switch = antd.Switch;
+ var Modal = antd.Modal;
+ var Drawer = antd.Drawer;
+ var message = antd.message;
+
+ var TextArea = Input.TextArea;
+
+ // ---------- utils ----------
+ 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 toFixed2(v) {
+ if (v === null || v === undefined || v === '') return '';
+ var n = typeof v === 'number' ? v : parseFloat(v);
+ return isNaN(n) ? '' : n.toFixed(2);
+ }
+
+ function isEmpty(v) {
+ return v === null || v === undefined || String(v).trim() === '';
+ }
+
+ function filterOption(input, option) {
+ var label = (option && (option.label || option.children)) || '';
+ return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0;
+ }
+
+ function fileToDataUrl(file, cb) {
+ try {
+ var reader = new FileReader();
+ reader.onload = function (e) { cb(null, (e && e.target && e.target.result) || ''); };
+ reader.onerror = function () { cb(new Error('read error')); };
+ reader.readAsDataURL(file);
+ } catch (e) {
+ cb(e);
+ }
+ }
+
+ function makeThumb(url, onPreview, onRemove, disabled) {
+ return React.createElement('div', {
+ style: {
+ width: 64,
+ height: 64,
+ borderRadius: 4,
+ border: '1px solid #f0f0f0',
+ background: '#fafafa',
+ position: 'relative',
+ overflow: 'hidden'
+ }
+ },
+ React.createElement('img', {
+ src: url,
+ style: { width: '100%', height: '100%', objectFit: 'cover', cursor: 'pointer' },
+ onClick: disabled ? undefined : onPreview
+ }),
+ (disabled || !onRemove) ? null : React.createElement('div', {
+ style: {
+ position: 'absolute',
+ right: 6,
+ top: 6,
+ width: 20,
+ height: 20,
+ borderRadius: 999,
+ background: 'rgba(0,0,0,0.55)',
+ color: '#fff',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ cursor: 'pointer',
+ fontSize: 12,
+ userSelect: 'none'
+ },
+ onClick: function (e) { e.stopPropagation(); onRemove && onRemove(); }
+ }, '×')
+ );
+ }
+
+ function UploadBox(props) {
+ var label = props.label;
+ var value = props.value; // array of {name,url}
+ var max = props.max || 1;
+ var onChange = props.onChange;
+ var disabled = !!props.disabled;
+ var tip = props.tip;
+
+ function handlePick(e) {
+ var f = e && e.target && e.target.files && e.target.files[0];
+ if (!f) return;
+ fileToDataUrl(f, function (err, url) {
+ if (err) { message.error('上传失败(原型)'); return; }
+ var next = (value || []).slice();
+ next.push({ uid: String(Date.now()), name: f.name || 'image', url: url });
+ if (next.length > max) next = next.slice(next.length - max);
+ onChange && onChange(next);
+ });
+ e.target.value = '';
+ }
+
+ return React.createElement('div', null,
+ React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, label),
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } },
+ (value || []).map(function (f) {
+ return React.createElement('div', { key: f.uid },
+ makeThumb(
+ f.url,
+ function () { previewState[1]({ open: true, url: f.url, title: f.name || '预览' }); },
+ function () { onChange && onChange((value || []).filter(function (x) { return x.uid !== f.uid; })); },
+ disabled
+ )
+ );
+ }),
+ disabled ? null : ((value || []).length >= max ? null : React.createElement('label', {
+ style: {
+ width: 64,
+ height: 64,
+ borderRadius: 4,
+ border: '1px dashed #d9d9d9',
+ background: '#fff',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ color: '#999',
+ cursor: 'pointer'
+ }
+ },
+ React.createElement('input', { type: 'file', accept: 'image/*', style: { display: 'none' }, onChange: handlePick }),
+ '上传'
+ ))
+ ),
+ tip ? React.createElement('div', { style: { marginTop: 6, fontSize: 12, color: '#999' } }, tip) : null
+ );
+ }
+
+ // ---------- styles ----------
+ 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 styles = {
+ formRow: { display: 'flex', gap: 16, marginBottom: 12, alignItems: 'flex-start' },
+ formItem: { flex: 1, minWidth: 0 },
+ label: { fontSize: 12, color: '#666', marginBottom: 6 }
+};
+
+function FormItem(props) {
+ var label = props.label;
+ var required = !!props.required;
+ var fullWidth = !!props.fullWidth;
+ return React.createElement('div', { style: fullWidth ? Object.assign({}, styles.formItem, { flex: '0 0 100%' }) : styles.formItem },
+ React.createElement('div', { style: styles.label }, required ? RequiredLabel(label) : label),
+ props.children
+ );
+}
+
+ // ---------- mock reserve vehicles ----------
+ var reserveVehicles = useMemo(function () {
+ return [
+ {
+ plateNo: '京A12345',
+ vehicleType: '牵引车',
+ brand: '东风',
+ model: 'DFH1180',
+ vin: 'LJNAU1A2XK1234567',
+ hasAd: true,
+ adPhoto: [{ uid: 'ad1', name: '广告照片.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=Ad' }],
+ bigWordPhoto: [{ uid: 'bw1', name: '放大字照片.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=BigWord' }],
+ hasTailboard: true
+ },
+ {
+ plateNo: '浙F80088',
+ vehicleType: '厢式车',
+ brand: '福田',
+ model: 'BJ1180',
+ vin: 'LJNAU1A2XK7654321',
+ hasAd: false,
+ adPhoto: [],
+ bigWordPhoto: [],
+ hasTailboard: false
+ },
+ {
+ plateNo: '沪A30003',
+ vehicleType: '厢式车',
+ brand: '重汽',
+ model: 'HOWO-T5G',
+ vin: 'LJNAU1A2XK9999000',
+ hasAd: true,
+ adPhoto: [{ uid: 'ad2', name: '广告照片2.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=Ad2' }],
+ bigWordPhoto: [{ uid: 'bw2', name: '放大字照片2.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=BigWord2' }],
+ hasTailboard: true
+ }
+ ];
+ }, []);
+
+ var plateOptions = useMemo(function () {
+ return reserveVehicles.map(function (v) { return { value: v.plateNo, label: v.plateNo }; });
+ }, [reserveVehicles]);
+ var vehicleByPlate = useMemo(function () {
+ var map = {};
+ reserveVehicles.forEach(function (v) { map[v.plateNo] = v; });
+ return map;
+ }, [reserveVehicles]);
+
+ // ---------- states ----------
+ var previewState = useState({ open: false, url: '', title: '' });
+
+ var formState = useState({
+ plateNo: undefined,
+ vehicleType: '',
+ brand: '',
+ model: '',
+ vin: '',
+
+ hasAd: false,
+ adPhoto: [],
+ bigWordPhoto: [],
+
+ hasTailboard: false,
+
+ spareTirePhoto: [],
+ spareTireDepth: '',
+
+ trainingFile: null, // {name}
+ trainingRecognized: false,
+
+ driverLicenses: [], // 4 photos {uid,name,url}
+
+ mileageKm: '',
+ batteryKwh: '',
+ hydrogenAmount: '',
+ serviceFee: ''
+ });
+ var form = formState[0];
+ var setForm = formState[1];
+
+ var ocrModalState = useState({ open: false, photoUrl: '', depth: '6.50' });
+
+ var inspectionDrawerOpenState = useState(false);
+
+ var requirementModalOpenState = useState(false);
+ var requirementDocContent =
+ '交车管理-交车单-编辑\n'
+ + '一个「数字化资产ONEOS运管平台」中的「交车管理-交车单-编辑」模块\n'
+ + '1.面包屑:\n'
+ + '1.1.运维管理-车辆业务-交车管理-交车单-编辑\n'
+ + '每个模块为一个单独卡片:\n\n'
+ + '2.交车明细:列表结构;\n'
+ + '2.1.车辆类型:输入框(禁用),根据车牌号反写车辆类型;\n'
+ + '2.2.品牌:输入框(禁用),根据车牌号反写品牌;\n'
+ + '2.2.型号:输入框(禁用),根据车牌号反写型号;\n'
+ + '2.3.车牌号:选择器,输入框,支持输入内容下拉模糊匹配选项,仅能选择备车库中车辆;\n'
+ + '2.4.车辆识别代码:输入框(禁用),根据车牌号反写车辆识别代码;\n'
+ + '2.5.车身广告及放大字:必选项,开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中, 安装时间以该条备车记录提交成功为准,不够选择广告照片和放大字照片字段隐藏不显示;\n'
+ + '2.6.广告照片:必填项,图片上传,最多支持上传1张图片,支持主流照片格式。上传后,上传按钮切换为显示已上传图片缩略图,支持点击预览和删除,删除后,切换为上传按钮,从备车记录自动反写;\n'
+ + '2.7.放大字照片:必填项,图片上传,最多支持上传1张图片,支持主流照片格式。上传后,上传按钮切换为显示已上传图片缩略图,支持点击预览和删除,删除后,切换为上传按钮,从备车记录自动反写;\n'
+ + '2.8.尾板:必填项,开关,选择车辆后拉取该车辆「后装设备」「尾板」,如果该车辆有「尾板」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「尾板」中,安装时间以该条备车记录提交成功为准;\n'
+ + '2.9.备胎照片:必填项,图片上传,最多支持上传1张图片,支持主流照片格式。上传时弹出卡片,提示正在识别中,请勿关闭页面,之后卡片左侧显示备胎照片,右侧输入框显示识别出的胎纹深度,后缀单位为mm,点击卡片中确认按钮,反写至备胎胎纹深度字段下;\n'
+ + '2.10.备胎胎纹深度:必填项,输入框,反写备胎照片OCR识别胎纹深度结果,支持修改;\n'
+ + '2.11.驾驶培训:必填项,附件上传按钮,后方为提示信息:请上传司机现场培训二维码图片。识别成功后隐藏驾驶培训按钮和提示,显示:已完成视频培训;\n'
+ + '2.12.司机证照:显示4张照片、身份证(正面)、身份证(反面)、驾驶证、从业资格证,根据驾驶培训上传二维码识别后自动反写;\n'
+ + '2.13.交车里程:必填项,输入框,单位为公里;\n'
+ + '2.14.交车电量:必填项,输入框,单位为kWh;\n'
+ + '2.15.交车氢量:必填项,输入框,单位为%或MPa,根据型号参数中该车型实际仪表盘单位显示;\n'
+ + '2.16.送车服务费:选填项,输入框,精确至2位小数,格式为xx.xx元;\n'
+ + '2.17.车辆检查:按钮,文字为交车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、检查情况、备注;\n'
+ + ' 2.11.1.类别:分为车灯、仪表盘、驾驶室、轮胎、液位检查、外观检查、车辆外观、其他、随车工具、随车证件、整车、燃料电池系统、冷机、制动系统;\n'
+ + ' 2.11.2.检查项目:车灯类别对应(大灯、转向灯、小灯、示廓灯、刹车灯、倒车灯、牌照灯、防雾灯、室内灯)、仪表盘对应(氢系统指示、电控系统指示、数值清晰准确、故障报警灯)、驾驶室对应(点烟器、车窗升降、按键开关、雨刮器、内后视镜是否正常、内/外门把手、安全带、空调冷暖风、仪表盘、门锁功能、手刹、车钥匙功能是否正常、喇叭、音响功能、遮阳板、主副驾座椅、方向盘、内饰干净整洁)\n'
+ + ' 2.11.3.检查情况:其他项为开关,在检查项目每项后方显示,从备车记录反写,可手动进行关闭,轮胎为输入框,提示请输入胎纹深度;\n'
+ + ' 2.11.4.备注:输入框;\n\n'
+ + '3.交车照片:多个模块分3列显示,由照片标题和照片上传按钮组成,照片点击上传,从本地文件上传单张图片,上传成功后可通过图片右上角删除按钮删除,点击图片可放大预览;;\n'
+ + '3.1.车辆:必填项,包括仪表盘、车辆正面、车辆左前方、车辆左后方、车辆右后方、车辆右前方;\n'
+ + '3.2.底盘:必填项,包括正前方底部、左侧前方底部、左侧后方底部、正后方底部、右侧后方底部、右侧前方底部;\n'
+ + '3.3.轮胎:必填项,包括左前轮、左后轮(内)、左后轮(外)、右前轮、右后轮(内)、右后轮(外)、备胎;\n'
+ + '3.4.瑕疵:必填项,包括照片上传按钮,最多支持4张照片;\n'
+ + '3.5.其他:必填项,包括照片上传按钮,最多支持4张照片;\n'
+ + '照片点击上传,从本地文件上传单张图片,上传成功后可通过图片右上角删除按钮删除,点击图片可放大预览;\n\n'
+ + '4.底部为提交、保存、取消按钮;\n'
+ + ' 4.1.提交:点击进行二次确认,点击确认完成该车辆交车;\n'
+ + ' 4.2.保存:点击暂存交车单(不做校验);\n'
+ + ' 4.3.取消:点击返回交车单页;\n';
+
+ var trainingInputRef = useRef(null);
+
+ // photo sections
+ var photoState = useState({
+ vehicle: {
+ '仪表盘': [],
+ '车辆正面': [],
+ '车辆左前方': [],
+ '车辆左后方': [],
+ '车辆右后方': [],
+ '车辆右前方': []
+ },
+ chassis: {
+ '正前方底部': [],
+ '左侧前方底部': [],
+ '左侧后方底部': [],
+ '正后方底部': [],
+ '右侧后方底部': [],
+ '右侧前方底部': []
+ },
+ tire: {
+ '左前轮': [],
+ '左后轮(内)': [],
+ '左后轮(外)': [],
+ '右前轮': [],
+ '右后轮(内)': [],
+ '右后轮(外)': [],
+ '备胎': []
+ },
+ defect: [],
+ other: []
+ });
+ var photos = photoState[0];
+ var setPhotos = photoState[1];
+
+ function updateForm(patch) {
+ setForm(function (p) { return Object.assign({}, p, patch); });
+ }
+
+ function handlePlateChange(v) {
+ var veh = vehicleByPlate[v];
+ updateForm({
+ plateNo: v,
+ vehicleType: (veh && veh.vehicleType) || '',
+ brand: (veh && veh.brand) || '',
+ model: (veh && veh.model) || '',
+ vin: (veh && veh.vin) || '',
+ hasAd: !!(veh && veh.hasAd),
+ adPhoto: (veh && (veh.adPhoto || [])) || [],
+ bigWordPhoto: (veh && (veh.bigWordPhoto || [])) || [],
+ hasTailboard: !!(veh && veh.hasTailboard),
+ spareTirePhoto: [],
+ spareTireDepth: '',
+ trainingFile: null,
+ trainingRecognized: false,
+ driverLicenses: []
+ });
+ }
+
+ function handleSparePhotoChange(list) {
+ updateForm({ spareTirePhoto: list || [] });
+ if ((list || []).length > 0) {
+ ocrModalState[1]({ open: true, photoUrl: list[0].url, depth: '6.50' });
+ }
+ }
+
+ function confirmOcr() {
+ updateForm({ spareTireDepth: ocrModalState[0].depth });
+ ocrModalState[1](Object.assign({}, ocrModalState[0], { open: false }));
+ message.success('已反写备胎胎纹深度');
+ }
+
+ function handleTrainingPick(e) {
+ var f = e && e.target && e.target.files && e.target.files[0];
+ if (!f) return;
+ // 原型:上传即识别成功
+ setTimeout(function () {
+ updateForm({ trainingFile: { name: f.name || '二维码图片' }, trainingRecognized: true });
+ // 自动反写司机证照(示例)
+ updateForm({
+ driverLicenses: [
+ { uid: 'id1', name: '身份证正面.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=ID-Front' },
+ { uid: 'id2', name: '身份证反面.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=ID-Back' },
+ { uid: 'dl', name: '驾驶证.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=DriverLicense' },
+ { uid: 'qc', name: '从业资格证.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=Qualification' }
+ ]
+ });
+ message.success('识别成功:已完成视频培训(原型)');
+ }, 600);
+ e.target.value = '';
+ }
+
+ function validateSubmit() {
+ if (isEmpty(form.plateNo)) return '请选择车牌号';
+ // 车身广告及放大字:必选项(hasAd 视为必选,允许开/关,但若开则必须有两张图)
+ if (form.hasAd) {
+ if (!form.adPhoto || form.adPhoto.length === 0) return '请上传广告照片';
+ if (!form.bigWordPhoto || form.bigWordPhoto.length === 0) return '请上传放大字照片';
+ }
+ if (form.hasTailboard !== true && form.hasTailboard !== false) return '请填写尾板';
+ // 尾板必填:但开关值本身必选,这里只要求已选;若未选 plateChange 已赋默认 false
+ // 备胎照片/胎纹深度必填
+ if (!form.spareTirePhoto || form.spareTirePhoto.length === 0) return '请上传备胎照片';
+ if (isEmpty(form.spareTireDepth)) return '请填写备胎胎纹深度';
+ // 驾驶培训必填:识别成功才算完成
+ if (!form.trainingRecognized) return '请上传验车码并完成识别';
+ if (isEmpty(form.mileageKm)) return '请填写交车里程';
+ if (isEmpty(form.batteryKwh)) return '请填写交车电量';
+ if (isEmpty(form.hydrogenAmount)) return '请填写交车氢量';
+ // 交车照片必填:车辆/底盘/轮胎(瑕疵、其他非必填)
+ var requiredGroups = [
+ { key: 'vehicle', label: '车辆' },
+ { key: 'chassis', label: '底盘' },
+ { key: 'tire', label: '轮胎' }
+ ];
+ for (var gi = 0; gi < requiredGroups.length; gi++) {
+ var g = requiredGroups[gi];
+ var groupMap = photos && photos[g.key];
+ var keys = groupMap ? Object.keys(groupMap) : [];
+ for (var ki = 0; ki < keys.length; ki++) {
+ var k = keys[ki];
+ var arr = groupMap[k] || [];
+ if (!arr || arr.length === 0) return '请上传' + g.label + '照片:' + k;
+ }
+ }
+ // 尾板为必填项:如果业务上要求必须开/关都可以,这里不做强制为开,仅保证已选择
+ return '';
+ }
+
+ function handleSubmit() {
+ var err = validateSubmit();
+ if (err) { message.error(err); return; }
+ Modal.confirm({
+ title: '确认交车',
+ content: '请确认信息填写无误,点击确认完成该车辆交车。',
+ okText: '确认',
+ cancelText: '取消',
+ onOk: function () {
+ message.success('交车成功(原型)');
+ }
+ });
+ }
+
+function handleSave() {
+ message.success('已保存(原型)');
+}
+
+ function handleCancel() {
+ message.info('返回交车单页(原型)');
+ }
+
+ // ---------- inspection checklist ----------
+ // 按需求 2.11.1~2.11.4 生成完整检查清单(原型:默认从备车记录反写为“开/正常”)
+ var inspectionCategoryItems = {
+ '车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'],
+ '仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'],
+ '驾驶室': ['点烟器', '车窗升降', '按键开关', '雨刮器', '内后视镜是否正常', '内/外门把手', '安全带', '空调冷暖风', '仪表盘', '门锁功能', '手刹', '车钥匙功能是否正常', '喇叭', '音响功能', '遮阳板', '主副驾座椅', '方向盘', '内饰干净整洁'],
+ '轮胎': ['前左胎', '前右胎', '后左胎', '后右胎', '备胎'],
+ '液位检查': ['冷却液', '制动液', '玻璃水'],
+ '外观检查': ['车身外观', '漆面', '玻璃'],
+ '车辆外观': ['整车外观'],
+ '其他': ['其他检查项'],
+ '随车工具': ['三角牌', '灭火器', '反光背心'],
+ '随车证件': ['行驶证', '营运证', '保险单'],
+ '整车': ['整车状态'],
+ '燃料电池系统': ['氢系统', '储氢瓶'],
+ '冷机': ['冷机运行'],
+ '制动系统': ['制动踏板', '驻车制动']
+ };
+
+ function buildInspectionList() {
+ var list = [];
+ var categories = Object.keys(inspectionCategoryItems);
+ for (var i = 0; i < categories.length; i++) {
+ var cat = categories[i];
+ var items = inspectionCategoryItems[cat] || [];
+ for (var j = 0; j < items.length; j++) {
+ var it = items[j];
+ var isTire = cat === '轮胎';
+ list.push({
+ key: 'ins-' + i + '-' + j,
+ category: cat,
+ item: it,
+ checked: true,
+ treadDepth: isTire ? '6.5' : '',
+ remark: ''
+ });
+ }
+ }
+ return list;
+ }
+
+ var inspectionListState = useState(function () {
+ return buildInspectionList();
+ });
+ var inspectionList = inspectionListState[0];
+ var setInspectionList = inspectionListState[1];
+ var inspectionListRef = useRef(null);
+ inspectionListRef.current = inspectionList;
+
+ function updateInspectionRow(key, patch) {
+ setInspectionList(function (prev) {
+ return (prev || []).map(function (r) {
+ if (r.key !== key) return r;
+ return Object.assign({}, r, patch);
+ });
+ });
+ }
+
+ var inspectionColumns = useMemo(function () {
+ return [
+ {
+ title: '类别',
+ dataIndex: 'category',
+ key: 'category',
+ width: 140,
+ render: function (text, record, index) {
+ var rows = inspectionListRef.current || [];
+ var cat = record && record.category;
+ if (!cat) return { children: text, props: { rowSpan: 1 } };
+
+ // 仅合并同类别的连续行
+ var isFirst = true;
+ for (var i = index - 1; i >= 0; i--) {
+ if (!rows[i] || rows[i].category !== cat) break;
+ isFirst = false;
+ break;
+ }
+ if (!isFirst) return { children: null, props: { rowSpan: 0 } };
+
+ var span = 1;
+ for (var j = index + 1; j < rows.length; j++) {
+ if (!rows[j] || rows[j].category !== cat) break;
+ span++;
+ }
+ return { children: text, props: { rowSpan: span } };
+ }
+ },
+ { title: '检查项目', dataIndex: 'item', key: 'item', width: 220 },
+ {
+ title: '检查情况',
+ dataIndex: 'checked',
+ key: 'checked',
+ width: 220,
+ render: function (_, record) {
+ var isTire = record && (record.category === '轮胎' || String(record.item || '').indexOf('胎纹') >= 0);
+ return isTire
+ ? React.createElement(Input, {
+ value: record.treadDepth,
+ placeholder: '请输入胎纹深度',
+ addonAfter: 'mm',
+ onChange: function (e) { updateInspectionRow(record.key, { treadDepth: e.target.value }); }
+ })
+ : React.createElement(Switch, {
+ checked: !!record.checked,
+ onChange: function (v) { updateInspectionRow(record.key, { checked: !!v }); }
+ });
+ }
+ },
+ {
+ title: '备注',
+ dataIndex: 'remark',
+ key: 'remark',
+ render: function (_, record) {
+ return React.createElement(Input, {
+ value: record.remark,
+ placeholder: '请输入',
+ onChange: function (e) { updateInspectionRow(record.key, { remark: e.target.value }); }
+ });
+ }
+ }
+ ];
+ }, []);
+
+ // ---------- render helpers ----------
+ // 交车明细布局对齐「备车-新增」:使用 FormItem + styles.formRow
+
+ function PhotoGridColumn(title, items, required) {
+ return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: 12 } },
+ React.createElement('div', { style: { fontWeight: 600, marginBottom: 12 } }, required ? RequiredLabel(title) : title),
+ React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12 } },
+ items.map(function (it) {
+ return React.createElement('div', { key: it.key },
+ UploadBox({
+ label: required ? RequiredLabel(it.label) : it.label,
+ value: it.value,
+ max: 1,
+ onChange: it.onChange
+ })
+ );
+ })
+ )
+ );
+ }
+
+ function PhotoGridSimple(title, value, max, onChange) {
+ return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: 12 } },
+ React.createElement('div', { style: { fontWeight: 600, marginBottom: 12 } }, title),
+ UploadBox({ label: '照片', value: value, max: max, onChange: onChange, tip: '最多支持上传' + max + '张' })
+ );
+ }
+
+// 在线打印已按最新需求移除
+
+ // ---------- render ----------
+ 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: '车辆业务' }, { title: '交车管理' }, { title: '交车单-编辑' }] }),
+ React.createElement(Button, { type: 'link', onClick: function () { requirementModalOpenState[1](true); } }, '查看需求说明')
+ ),
+
+ React.createElement(Card, { title: '交车明细', style: cardStyle },
+ React.createElement('div', null,
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '车牌号', required: true },
+ React.createElement(Select, {
+ value: form.plateNo,
+ options: plateOptions,
+ showSearch: true,
+ filterOption: filterOption,
+ placeholder: '请输入或选择车牌号',
+ allowClear: true,
+ style: { width: '100%' },
+ onChange: handlePlateChange
+ })
+ ),
+ React.createElement(FormItem, { label: '车辆类型' },
+ React.createElement(Input, { value: form.vehicleType, disabled: true })
+ )
+ ),
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '品牌' },
+ React.createElement(Input, { value: form.brand, disabled: true })
+ ),
+ React.createElement(FormItem, { label: '型号' },
+ React.createElement(Input, { value: form.model, disabled: true })
+ )
+ ),
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '车辆识别代码' },
+ React.createElement(Input, { value: form.vin, disabled: true })
+ ),
+ React.createElement('div', { style: styles.formItem })
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '车身广告及放大字', required: true, fullWidth: true },
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10 } },
+ React.createElement(Switch, { checked: !!form.hasAd, onChange: function (v) { updateForm({ hasAd: !!v }); } }),
+ React.createElement('span', { style: { color: '#666', fontSize: 12 } }, form.hasAd ? '有车身广告' : '无车身广告')
+ )
+ )
+ ),
+ form.hasAd ? React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 12 } },
+ React.createElement('div', null,
+ UploadBox({ label: RequiredLabel('广告照片'), value: form.adPhoto, max: 1, onChange: function (l) { updateForm({ adPhoto: l }); } })
+ ),
+ React.createElement('div', null,
+ UploadBox({ label: RequiredLabel('放大字照片'), value: form.bigWordPhoto, max: 1, onChange: function (l) { updateForm({ bigWordPhoto: l }); } })
+ )
+ ) : null,
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '尾板', required: true, fullWidth: true },
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10 } },
+ React.createElement(Switch, { checked: !!form.hasTailboard, onChange: function (v) { updateForm({ hasTailboard: !!v }); } }),
+ React.createElement('span', { style: { color: '#666', fontSize: 12 } }, form.hasTailboard ? '有尾板' : '无尾板')
+ )
+ )
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '备胎照片', required: true },
+ UploadBox({ label: '', value: form.spareTirePhoto, max: 1, onChange: handleSparePhotoChange })
+ ),
+ React.createElement(FormItem, { label: '备胎胎纹深度', required: true },
+ React.createElement(Input, { value: form.spareTireDepth, placeholder: '请输入备胎胎纹深度', addonAfter: 'mm', onChange: function (e) { updateForm({ spareTireDepth: e.target.value }); } })
+ )
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '驾驶培训', required: true, fullWidth: true },
+ React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 6 } },
+ form.trainingRecognized
+ ? React.createElement('span', { style: { color: '#52c41a', fontWeight: 600 } }, '已完成视频培训')
+ : React.createElement(React.Fragment, null,
+ React.createElement('div', { style: { display: 'inline-flex', alignItems: 'center', gap: 8 } },
+ React.createElement('input', { type: 'file', ref: trainingInputRef, accept: 'image/*', style: { display: 'none' }, onChange: handleTrainingPick }),
+ React.createElement(Button, { onClick: function () { trainingInputRef.current && trainingInputRef.current.click(); } }, '上传验车码')
+ )
+ )
+ )
+ )
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '司机证照', fullWidth: true },
+ React.createElement('div', null,
+ (form.driverLicenses && form.driverLicenses.length > 0)
+ ? React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 } },
+ form.driverLicenses.map(function (f) {
+ return React.createElement('div', { key: f.uid },
+ makeThumb(
+ f.url,
+ function () { previewState[1]({ open: true, url: f.url, title: f.name || '预览' }); },
+ null,
+ false
+ ),
+ React.createElement('div', { style: { marginTop: 6, fontSize: 12, color: '#666' } }, f.name)
+ );
+ })
+ )
+ : React.createElement('div', { style: { fontSize: 12, color: '#999' } }, '上传验车码后显示')
+ )
+ )
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '交车里程', required: true },
+ React.createElement(Input, { value: form.mileageKm, placeholder: '请输入', addonAfter: '公里', onChange: function (e) { updateForm({ mileageKm: e.target.value }); } })
+ ),
+ React.createElement(FormItem, { label: '交车电量', required: true },
+ React.createElement(Input, { value: form.batteryKwh, placeholder: '请输入', addonAfter: 'kWh', onChange: function (e) { updateForm({ batteryKwh: e.target.value }); } })
+ )
+ ),
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '交车氢量', required: true },
+ React.createElement(Input, { value: form.hydrogenAmount, placeholder: '请输入交车氢量', addonAfter: 'MPa', onChange: function (e) { updateForm({ hydrogenAmount: e.target.value }); } })
+ ),
+ React.createElement(FormItem, { label: '送车服务费' },
+ React.createElement(Input, { value: form.serviceFee, placeholder: '请输入送车服务费金额', addonAfter: '元', onChange: function (e) { updateForm({ serviceFee: e.target.value }); } })
+ )
+ ),
+
+ React.createElement('div', { style: styles.formRow },
+ React.createElement(FormItem, { label: '车辆检查', fullWidth: true },
+ React.createElement(Button, { onClick: function () { inspectionDrawerOpenState[1](true); } }, '交车检查单')
+ )
+ )
+ )
+ ),
+
+ React.createElement(Card, { title: '交车照片', style: cardStyle },
+ React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 12 } },
+ PhotoGridColumn('车辆', Object.keys(photos.vehicle).map(function (k) {
+ return {
+ key: 'v-' + k,
+ label: k,
+ value: photos.vehicle[k],
+ onChange: function (l) { setPhotos(function (p) { var n = Object.assign({}, p); n.vehicle = Object.assign({}, p.vehicle); n.vehicle[k] = l; return n; }); }
+ };
+ }), true),
+ PhotoGridColumn('底盘', Object.keys(photos.chassis).map(function (k) {
+ return {
+ key: 'c-' + k,
+ label: k,
+ value: photos.chassis[k],
+ onChange: function (l) { setPhotos(function (p) { var n = Object.assign({}, p); n.chassis = Object.assign({}, p.chassis); n.chassis[k] = l; return n; }); }
+ };
+ }), true),
+ PhotoGridColumn('轮胎', Object.keys(photos.tire).map(function (k) {
+ return {
+ key: 't-' + k,
+ label: k,
+ value: photos.tire[k],
+ onChange: function (l) { setPhotos(function (p) { var n = Object.assign({}, p); n.tire = Object.assign({}, p.tire); n.tire[k] = l; return n; }); }
+ };
+ }), true)
+ ),
+ React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 12, marginTop: 12 } },
+ PhotoGridSimple('瑕疵', photos.defect, 4, function (l) { setPhotos(function (p) { return Object.assign({}, p, { defect: l }); }); }),
+ PhotoGridSimple('其他', photos.other, 4, function (l) { setPhotos(function (p) { return Object.assign({}, p, { other: l }); }); })
+ )
+ ),
+
+ React.createElement('div', { style: footerStyle },
+ React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'),
+ React.createElement(Button, { onClick: handleSave }, '保存'),
+ React.createElement(Button, { onClick: handleCancel }, '取消')
+ ),
+
+ // preview modal
+ React.createElement(Modal, {
+ open: !!previewState[0].open,
+ title: previewState[0].title || '预览',
+ footer: null,
+ onCancel: function () { previewState[1]({ open: false, url: '', title: '' }); },
+ width: 860
+ },
+ previewState[0].url ? React.createElement('img', { src: previewState[0].url, style: { width: '100%', maxHeight: '70vh', objectFit: 'contain' } }) : null
+ ),
+
+ // OCR modal
+ React.createElement(Modal, {
+ open: !!ocrModalState[0].open,
+ title: '正在识别中(原型)',
+ onCancel: function () { ocrModalState[1](Object.assign({}, ocrModalState[0], { open: false })); },
+ onOk: confirmOcr,
+ okText: '确认',
+ cancelText: '取消',
+ width: 860
+ },
+ React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 320px', gap: 16, alignItems: 'start' } },
+ React.createElement('div', { style: { border: '1px solid #f0f0f0', borderRadius: 8, overflow: 'hidden', background: '#fafafa' } },
+ ocrModalState[0].photoUrl ? React.createElement('img', { src: ocrModalState[0].photoUrl, style: { width: '100%', maxHeight: 420, objectFit: 'contain', display: 'block' } }) : null
+ ),
+ React.createElement('div', null,
+ React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, '识别出的胎纹深度'),
+ React.createElement(Input, {
+ value: ocrModalState[0].depth,
+ addonAfter: 'mm',
+ onChange: function (e) { ocrModalState[1](Object.assign({}, ocrModalState[0], { depth: e.target.value })); }
+ }),
+ React.createElement('div', { style: { marginTop: 8, fontSize: 12, color: '#999', lineHeight: 1.7 } }, '提示:识别中请勿关闭页面;点击确认后将反写至“备胎胎纹深度”。')
+ )
+ )
+ ),
+
+ // inspection drawer
+ React.createElement(Drawer, {
+ open: inspectionDrawerOpenState[0],
+ title: '交车检查单',
+ width: 920,
+ placement: 'right',
+ onClose: function () { inspectionDrawerOpenState[1](false); },
+ styles: { body: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', paddingBottom: 0 } },
+ footer: React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-start', gap: 8 } },
+ React.createElement(Button, {
+ type: 'primary',
+ onClick: function () {
+ message.success('提交成功(原型)');
+ inspectionDrawerOpenState[1](false);
+ }
+ }, '提交'),
+ React.createElement(Button, { onClick: function () { inspectionDrawerOpenState[1](false); } }, '返回')
+ )
+ },
+ React.createElement('div', { style: { flex: 1, minHeight: 0, overflow: 'auto' } },
+ React.createElement(Table, { rowKey: 'key', columns: inspectionColumns, dataSource: inspectionList, pagination: false, bordered: true, size: 'small' })
+ )
+ ),
+ 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)
+ ),
+
+ // 在线打印已按最新需求移除
+ );
+};
+
diff --git a/web端/运维管理/车辆业务/交车管理-交车单.jsx b/web端/运维管理/车辆业务/交车管理-交车单.jsx
index 7c65a14..945da5f 100644
--- a/web端/运维管理/车辆业务/交车管理-交车单.jsx
+++ b/web端/运维管理/车辆业务/交车管理-交车单.jsx
@@ -13,8 +13,13 @@ const Component = function () {
var Select = antd.Select;
var Table = antd.Table;
var DatePicker = antd.DatePicker;
+ var Modal = antd.Modal;
var message = antd.message;
+ var requirementModalOpen = useState(false);
+ var setRequirementModalOpen = requirementModalOpen[1];
+ var requirementDocContent = '交车管理-交车单\n一个「数字化资产ONEOS运管平台」中的「交车管理-交车单」模块\n\n1.面包屑:\n1.1.运维管理-车辆业务-交车管理-交车单\n\n每个模块为一个单独卡片:\n\n2.项目详情:表单结构:\n2.1.项目名称:输入框禁用,显示该交车单对应车辆租赁合同中项目名称;\n2.2.合同编码:输入框禁用,显示该交车单对应车辆租赁合同中项目合同编码;\n2.3.客户名称:输入框禁用,显示该交车单对应车辆租赁合同中客户名称;\n2.4.预计交车日期:日期选择器禁用,显示该交车单预计交车日期,分为单日和日期区间两种,格式为:YYYY-MM-DD、YYYY-MM-DD至YYYY-MM-DD;\n2.5.交车区域:输入框禁用,显示该交车单对应车辆租赁合同中交车区域;\n2.6.交车地点:输入框禁用,显示该交车单对应车辆租赁合同中交车地点;\n2.7.业务部门:输入框禁用,显示该交车单对应车辆租赁合同中业务部门;\n2.8.业务负责人:输入框禁用,显示该交车单对应车辆租赁合同中业务负责人;\n\n3.交车明细:列表结构;\n3.1.序号:与车辆租赁合同中序号对应,显示该交车任务所有车辆序号;\n3.2.品牌:选择器禁用,显示该交车单对应车辆租赁合同中品牌;\n3.3.型号:选择器禁用,显示该交车单对应车辆租赁合同中型号;\n3.4.车牌号:已交车车辆显示车牌号,未交车车辆显示-;\n3.5.实际交车日期:日期选择器禁用,显示该交车单实际交车日期,精确至日,格式为:YYYY-MM-DD;\n3.6.交车人:输入框禁用,显示该交车单中该车辆实际交车人员(注意这里是交车单中某辆车的交车人员,一个交车单可同时多个交车人员操作同时交多辆车,而不是最终提交整个交车单的人员);\n3.7.交车状态:已完成、待提交、已签章;\n 3.7.1.已完成:已完成交车提交,但还未完成被授权人签章;\n 3.7.2.待提交:仅点击保存,未完成交车提交;\n 3.7.3.已签章:已完成交车提交并完成被授权人签章;\n3.8.操作:查看、编辑、下载签章文件;\n 3.8.1.查看:点击跳转该车辆交车明细;\n 3.8.2.编辑:交车状态为待提交时显示,点击弹出卡片至交车明细编辑页;\n 3.8.3.下载签章文件:交车完成并完成被授权人签章时显示,点击下载签章文件;\n\n4.底部为提交、取消按钮;\n 4.1.提交:提交按钮必须所有车辆交车状态为已签章时才可提交,否则为禁用状态;\n 4.2.取消:点击返回交车管理列表页;';
+
// 项目详情(只读,来自交车任务/租赁合同)
var projectDetail = useMemo(function () {
return {
@@ -93,19 +98,13 @@ const Component = function () {
message.success('下载签章文件(原型)');
}, []);
- var card1Collapsed = useState(false)[0];
- var setCard1Collapsed = useState(false)[1];
- var card2Collapsed = useState(false)[0];
- var setCard2Collapsed = useState(false)[1];
-
var styles = {
page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', fontFamily: '"PingFang SC", "苹方-简", -apple-system, BlinkMacSystemFont, "Microsoft YaHei", sans-serif', fontSize: 14 },
breadcrumb: { marginBottom: 16, color: '#666' },
breadcrumbSep: { margin: '0 8px', color: '#999' },
card: { backgroundColor: '#fff', borderRadius: 8, marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.05)', overflow: 'hidden' },
- cardHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #f0f0f0', cursor: 'pointer' },
+ cardHeader: { display: 'flex', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid #f0f0f0' },
cardTitle: { fontSize: 16, fontWeight: 600, color: '#333' },
- cardToggle: { color: '#999', fontSize: 14 },
cardBody: { padding: '20px 24px' },
formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 },
formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8 },
@@ -117,11 +116,10 @@ const Component = function () {
var CardBlock = function (props) {
return React.createElement('div', { id: props.id, style: styles.card },
- React.createElement('div', { style: styles.cardHeader, onClick: function () { props.setCollapsed(!props.collapsed); } },
- React.createElement('span', { style: styles.cardTitle }, props.title),
- React.createElement('span', { style: styles.cardToggle }, props.collapsed ? '展开' : '收起')
+ React.createElement('div', { style: styles.cardHeader },
+ React.createElement('span', { style: styles.cardTitle }, props.title)
),
- !props.collapsed ? React.createElement('div', { style: styles.cardBody }, props.children) : null
+ React.createElement('div', { style: styles.cardBody }, props.children)
);
};
@@ -143,16 +141,11 @@ const Component = function () {
key: 'plateNo',
width: 120,
render: function (v, record, index) {
- return React.createElement(Select, {
- placeholder: '请选择或搜索',
- style: { width: '100%' },
- value: v || undefined,
- onChange: function (val) { updateDetailRow(index, 'plateNo', val || ''); },
- showSearch: true,
- allowClear: true,
- options: reservePlateOptions,
- filterOption: function (input, opt) { return (opt && opt.label && String(opt.label).toLowerCase().indexOf((input || '').toLowerCase()) >= 0); }
- });
+ var isDelivered = record.status === '已签章' || record.status === '已完成';
+ if (isDelivered) {
+ return React.createElement(Input, { value: v || '', disabled: true, style: { width: '100%', background: '#f5f5f5' } });
+ }
+ return React.createElement(Input, { value: '-', disabled: true, style: { width: '100%', background: '#f5f5f5' } });
}
},
{
@@ -186,17 +179,20 @@ const Component = function () {
];
return React.createElement('div', { style: styles.page },
- React.createElement('div', { style: styles.breadcrumb },
- React.createElement('span', null, '运维管理'),
- React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
- React.createElement('span', null, '车辆业务'),
- React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
- React.createElement('span', null, '交车管理'),
- React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
- React.createElement('span', { style: { color: '#1890ff' } }, '交车单')
+ React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } },
+ React.createElement('div', { style: styles.breadcrumb },
+ React.createElement('span', null, '运维管理'),
+ React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
+ React.createElement('span', null, '车辆业务'),
+ React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
+ React.createElement('span', null, '交车管理'),
+ React.createElement('span', { style: styles.breadcrumbSep }, ' / '),
+ React.createElement('span', { style: { color: '#1890ff' } }, '交车单')
+ ),
+ React.createElement(Button, { type: 'link', onClick: function () { setRequirementModalOpen(true); } }, '查看需求说明')
),
- React.createElement('div', { id: 'card-project' }, React.createElement(CardBlock, { id: 'card-project', title: '项目详情', collapsed: card1Collapsed, setCollapsed: setCard1Collapsed }, React.createElement('div', { style: styles.formRow },
+ React.createElement('div', { id: 'card-project' }, React.createElement(CardBlock, { id: 'card-project', title: '项目详情' }, React.createElement('div', { style: styles.formRow },
React.createElement(FormItem, { label: '项目名称' }, React.createElement(Input, { value: projectDetail.projectName, disabled: true, style: styles.inputDisabled })),
React.createElement(FormItem, { label: '合同编码' }, React.createElement(Input, { value: projectDetail.contractCode, disabled: true, style: styles.inputDisabled })),
React.createElement(FormItem, { label: '客户名称' }, React.createElement(Input, { value: projectDetail.customerName, disabled: true, style: styles.inputDisabled })),
@@ -207,7 +203,7 @@ const Component = function () {
React.createElement(FormItem, { label: '业务负责人' }, React.createElement(Input, { value: projectDetail.businessOwner, disabled: true, style: styles.inputDisabled }))
))),
- React.createElement('div', { id: 'card-detail', style: { marginTop: 16 } }, React.createElement(CardBlock, { id: 'card-detail', title: '交车明细', collapsed: card2Collapsed, setCollapsed: setCard2Collapsed }, React.createElement(Table, {
+ React.createElement('div', { id: 'card-detail', style: { marginTop: 16 } }, React.createElement(CardBlock, { id: 'card-detail', title: '交车明细' }, React.createElement(Table, {
columns: columns,
dataSource: detailList,
rowKey: 'key',
@@ -220,6 +216,15 @@ const Component = function () {
React.createElement('div', { style: styles.footer },
React.createElement(Button, { type: 'primary', disabled: !allSigned, onClick: handleSubmit }, '提交'),
React.createElement(Button, { onClick: handleCancel }, '取消')
- )
+ ),
+
+ React.createElement(Modal, {
+ title: '需求说明',
+ open: requirementModalOpen[0],
+ onCancel: function () { setRequirementModalOpen(false); },
+ footer: React.createElement(Button, { onClick: function () { setRequirementModalOpen(false); } }, '关闭'),
+ width: 640,
+ destroyOnClose: true
+ }, React.createElement('div', { style: { maxHeight: 560, overflowY: 'auto', whiteSpace: 'pre-wrap', lineHeight: 1.6, fontSize: 13 } }, requirementDocContent))
);
};
diff --git a/web端/运维管理/车辆业务/交车管理.jsx b/web端/运维管理/车辆业务/交车管理.jsx
index 55bb090..8ad8878 100644
--- a/web端/运维管理/车辆业务/交车管理.jsx
+++ b/web端/运维管理/车辆业务/交车管理.jsx
@@ -52,7 +52,7 @@ const Component = function () {
var requirementModalOpen = useState(false);
var setRequirementModalOpen = requirementModalOpen[1];
- var requirementDocContent = '交车管理\n一个「数字化资产ONEOS运管平台」中的「交车管理」模块\n\n1.面包屑:\n1.1.运维管理-车辆业务-交车管理\n\n2.筛选:\n2.1.合同编码:选择器,默认为所有合同;提示信息为:请输入或选择合同编码,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n2.2.项目名称:选择器,默认为所有项目;提示信息为:请输入或选择项目名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n2.3.客户名称:选择器,默认为所有客户;提示信息为:请输入或选择客户名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n2.4.交车区域:地区选择器,支持省-市2级筛选;\n2.5.交车时间:日期选择器,默认提示信息为:请选择交车开始时间 请选择交车结束时间,单输入框,双日历,支持时间段选择,精确至天,格式为:YYYY-MM-DD - YYYY-MM-DD;\n2.6.交车人:选择器,默认为所有交车人;提示信息为:请输入或选择交车人姓名,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n2.7.查询:点击查询,根据单个或多个筛选条件(且)联动表格进行查询;\n2.8.重置:点击清空查询条件至默认;\n\n3.列表:分为两个tab:待处理、历史记录,默认显示为待处理tab;\n3.1.待处理tab:列表显示预计交车时间、合同编码、项目名称、客户名称、交车区域、交车地点、交车数量(重点色,点击气泡卡片:车辆类型、品牌、型号、车牌号、交车时间、交车人员)、创建时间、创建人、最后修改时间、最后修改人、操作(查看、交车单);\n3.2.历史记录tab:同列,操作仅 查看(跳转交车管理-查看页)。';
+ var requirementDocContent = '交车管理\n一个「数字化资产ONEOS运管平台」中的「交车管理」模块\n\n1.面包屑:\n1.1.运维管理-车辆业务-交车管理\n\n2.筛选:\n2.1.合同编码:选择器,默认为所有合同;提示信息为:请输入或选择合同编码,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n2.2.项目名称:选择器,默认为所有项目;提示信息为:请输入或选择项目名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n2.3.客户名称:选择器,默认为所有客户;提示信息为:请输入或选择客户名称,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n2.4.交车区域:地区选择器,支持省-市2级筛选;\n2.5.交车时间:日期选择器,日期选择器,默认提示信息为:请选择交车开始时间 请选择交车结束时间,单输入框,双日历,支持时间段选择,精确至天,格式为:YYYY-MM-DD - YYYY-MM-DD;\n2.6.交车人:选择器,默认为所有交车人;提示信息为:请输入或选择交车人姓名,支持从输入框输入内容进行模糊搜索,下拉显示结果;\n2.7.查询:点击查询,根据单个或多个筛选条件(且)联动表格进行查询;\n2.8.重置:点击清空查询条件至默认;\n\n3.列表:\n分为两个tab:待处理、历史记录,默认显示为待处理tab;\n3.1.待处理tab:列表显示以下字段:\n3.1.1.预计交车时间:支持单日及开始-结束日期两种方式,格式为:YYYY-MM-DD及YYYY-MM-DD至YYYY-MM-DD,取自对应交车任务中预计交车时间;\n3.1.2.合同编码:显示租赁/自营合同编码;\n3.1.3.项目名称:显示租赁/自营合同项目名称;\n3.1.4.客户名称:显示租赁/自营合同客户名称;\n3.1.5.交车区域:显示交车区域,交车区域来自车辆租赁合同-交车区域,格式为省-市;\n3.1.6.交车地点:显示交车地点,交车地点来自车辆租赁合同-交车地点,显示详细地址;\n3.1.7.交车数量:显示交车数量,重点色显示,点击弹出气泡卡片,列表显示:车辆类型、品牌、型号、车牌号、交车时间、交车人员;\n 3.1.7.1.车辆类型:显示车辆类型;\n 3.1.7.2.品牌:显示车辆品牌;\n 3.1.7.3.型号:显示车辆型号;\n 3.1.7.4.车牌号:显示交车车辆车牌号,如果该车还未交车则显示为-;\n 3.1.7.5.交车时间:显示实际车辆完成交车时间,格式为:YYYY-MM-DD;\n 3.1.7.6.交车人员:显示实际车辆完成交车操作人姓名;\n3.1.8.创建时间:显示交车任务单创建时间,格式为:YYYY-MM-DD HH:MM;\n3.1.9.创建人:显示交车任务单创建人;\n3.1.10.最后修改时间:显示交车任务单最后修改时间,格式为:YYYY-MM-DD HH:MM;\n3.1.11.最后修改人:显示交车任务单最后修改人姓名;\n3.1.12.操作:交车;\n 3.1.12.1.交车单:点击跳转交车管理-交车单;\n\n3.2.历史记录:显示以下字段:\n3.2.1.预计交车时间:支持单日及开始-结束日期两种方式,格式为:YYYY-MM-DD及YYYY-MM-DD至YYYY-MM-DD,取自对应交车任务中预计交车时间;\n3.1.2.合同编码:显示租赁/自营合同编码;\n3.1.3.项目名称:显示租赁/自营合同项目名称;\n3.1.4.客户名称:显示租赁/自营合同客户名称;\n3.1.5.交车区域:显示交车区域,交车区域来自车辆租赁合同-交车区域,格式为省-市;\n3.1.6.交车地点:显示交车地点,交车地点来自车辆租赁合同-交车地点,显示详细地址;\n3.1.7.交车数量:显示交车数量,重点色显示,点击弹出气泡卡片,列表显示:车辆类型、品牌、型号、车牌号、交车时间、交车人员;\n 3.1.7.1.车辆类型:显示车辆类型;\n 3.1.7.2.品牌:显示车辆品牌;\n 3.1.7.3.型号:显示车辆型号;\n 3.1.7.4.车牌号:显示交车车辆车牌号,如果该车还未交车则显示为-;\n 3.1.7.5.交车时间:显示实际车辆完成交车时间,格式为:YYYY-MM-DD;\n 3.1.7.6.交车人员:显示实际车辆完成交车操作人姓名;\n3.1.8.创建时间:显示交车任务单创建时间,格式为:YYYY-MM-DD HH:MM;\n3.1.9.创建人:显示交车任务单创建人;\n3.1.10.最后修改时间:显示交车任务单最后修改时间,格式为:YYYY-MM-DD HH:MM;\n3.1.11.最后修改人:显示交车任务单最后修改人姓名;\n3.1.12.操作:查看、交车;\n 3.1.12.1.查看:跳转交车管理-查看页;';
// 交车区域:省-市 二级
var regionOptions = [
@@ -318,7 +318,7 @@ const Component = function () {
);
}
- // 待处理:预计交车时间、合同编码、项目名称、客户名称、交车区域、交车地点、交车数量、创建时间、创建人、最后修改时间、最后修改人、操作(查看、交车单)
+ // 待处理:预计交车时间、合同编码、项目名称、客户名称、交车区域、交车地点、交车数量、创建时间、创建人、最后修改时间、最后修改人、操作(交车单)
var pendingColumns = [
{ title: '预计交车时间', dataIndex: 'expectedDate', key: 'expectedDate', width: 220, ellipsis: true },
{ title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 150, ellipsis: true },
@@ -331,11 +331,8 @@ const Component = function () {
{ title: '创建人', dataIndex: 'createBy', key: 'createBy', width: 90, ellipsis: true },
{ title: '最后修改时间', dataIndex: 'lastModifyTime', key: 'lastModifyTime', width: 160, ellipsis: true },
{ title: '最后修改人', dataIndex: 'lastModifyBy', key: 'lastModifyBy', width: 90, ellipsis: true },
- { title: '操作', key: 'action', width: 120, fixed: 'right', render: function (_, r) {
- return React.createElement(React.Fragment, null,
- React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('跳转交车管理-查看页'); } }, '查看'),
- React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('跳转交车管理-交车单'); } }, '交车单')
- );
+ { title: '操作', key: 'action', width: 80, fixed: 'right', render: function (_, r) {
+ return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('跳转交车管理-交车单'); } }, '交车单');
} }
];
diff --git a/web端/运维管理/车辆业务/证照管理.jsx b/web端/运维管理/车辆业务/证照管理.jsx
new file mode 100644
index 0000000..d101fb3
--- /dev/null
+++ b/web端/运维管理/车辆业务/证照管理.jsx
@@ -0,0 +1,303 @@
+// 【重要】必须使用 const Component 作为组件变量名
+// 运维管理 - 车辆管理 - 证照管理
+
+const Component = function () {
+ var useState = React.useState;
+ var useMemo = React.useMemo;
+ var useCallback = React.useCallback;
+
+ var antd = window.antd;
+ var Breadcrumb = antd.Breadcrumb;
+ var Card = antd.Card;
+ var Table = antd.Table;
+ var Button = antd.Button;
+ var Select = antd.Select;
+ var Input = antd.Input;
+ var DatePicker = antd.DatePicker;
+ var Modal = antd.Modal;
+ var message = antd.message;
+
+ function pad2(n) { return n < 10 ? '0' + n : '' + n; }
+ function fmtDate(d) { return d.getFullYear() + '-' + pad2(d.getMonth() + 1) + '-' + pad2(d.getDate()); }
+ function todayPlus(days) { var d = new Date(); d.setDate(d.getDate() + days); return fmtDate(d); }
+
+ var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' };
+ var cardStyle = { marginBottom: 16 };
+
+ // 筛选
+ var filtersState = useState({
+ customerName: undefined,
+ contractCode: undefined,
+ plateNo: undefined,
+ vin: undefined,
+ licenseInspectValid: null,
+ operationPermitValid: null,
+ passPermitValid: null
+ });
+ var filters = filtersState[0];
+ var setFilters = filtersState[1];
+ var moreOpenState = useState(false);
+
+ // mock 列表数据
+ var tableDataState = useState(function () {
+ var rows = [];
+ for (var i = 1; i <= 20; i++) {
+ rows.push({
+ key: 'row-' + i,
+ plateNo: i < 10 ? ('粤A1234' + i) : ('京A5432' + i),
+ vin: 'LJ8ABC' + (100000 + i),
+ licenseRegDate: todayPlus(-500 - i),
+ licenseScrapDate: todayPlus(2000 + i),
+ licenseValidDate: todayPlus(60 + i),
+ operationCertNo: 'YYZ-' + (10000 + i),
+ operationRegDate: todayPlus(-480 - i),
+ operationInspectValid: todayPlus(90 + i),
+ operationValid: todayPlus(365 + i),
+ passPermitNo: 'TXZ-' + (20000 + i),
+ passArea: i % 3 === 0 ? '上海市-浦东新区' : i % 3 === 1 ? '广东省-广州市' : '北京市-朝阳区',
+ passValid: todayPlus(120 + i),
+ h2CertCode: 'JQZ-' + (30000 + i),
+ h2CertInspectDate: todayPlus(-30 - i),
+ h2CardCode: 'JQK-' + (40000 + i),
+ safetyValveInspectDate: todayPlus(-20 - i),
+ safetyValveCycleMonth: 12,
+ pressureGaugeInspectDate: todayPlus(-10 - i),
+ pressureGaugeCycleMonth: 12,
+ h2BottleVendor: i % 2 === 0 ? '亿华' : '中集',
+ h2BottleInspectDate: todayPlus(-5 - i),
+ h2BottleCycleMonth: 24
+ });
+ }
+ return rows;
+ });
+ var tableData = tableDataState[0];
+ var setTableData = tableDataState[1];
+
+ // 下拉选项(来自列表 mock)
+ var options = useMemo(function () {
+ var customers = ['嘉兴某某物流有限公司', '上海某某运输公司', '北京某某租赁有限公司'];
+ var contracts = ['HI-2024-001', 'HI-2024-002', 'HI-2024-003'];
+ return {
+ customerName: customers.map(function (v) { return { value: v, label: v }; }),
+ contractCode: contracts.map(function (v) { return { value: v, label: v }; }),
+ plateNo: tableData.map(function (r) { return r.plateNo; }).slice(0, 20).map(function (v) { return { value: v, label: v }; }),
+ vin: tableData.map(function (r) { return r.vin; }).slice(0, 20).map(function (v) { return { value: v, label: v }; })
+ };
+ }, [tableData]);
+
+ var filteredData = useMemo(function () {
+ var list = tableData;
+ if (filters.customerName) {
+ // mock:按 index 分组匹配
+ list = list.filter(function (_, idx) { return (idx % 3) === 0; });
+ }
+ if (filters.contractCode) {
+ list = list.filter(function (_, idx) { return (idx % 3) === 1; });
+ }
+ if (filters.plateNo) list = list.filter(function (r) { return r.plateNo === filters.plateNo; });
+ if (filters.vin) list = list.filter(function (r) { return r.vin === filters.vin; });
+ return list;
+ }, [tableData, filters.customerName, filters.contractCode, filters.plateNo, filters.vin]);
+
+ // 选中
+ var selectedRowKeysState = useState([]);
+
+ // 导入弹窗
+ var importOpenState = useState(false);
+ var importFileState = useState(null);
+
+ function resetFilters() {
+ setFilters({
+ customerName: undefined,
+ contractCode: undefined,
+ plateNo: undefined,
+ vin: undefined,
+ licenseInspectValid: null,
+ operationPermitValid: null,
+ passPermitValid: null
+ });
+ message.success('已重置');
+ }
+
+ function handleQuery() {
+ message.success('已查询(原型)');
+ }
+
+ function openAdd() {
+ message.info('进入证照录入页(原型)');
+ }
+
+ function confirmDeleteSelected() {
+ var keys = selectedRowKeysState[0] || [];
+ if (!keys.length) return;
+ Modal.confirm({
+ title: '确认删除',
+ content: '确定要删除选中的证照记录吗?删除后将无法恢复。',
+ okText: '确认删除',
+ cancelText: '取消',
+ onOk: function () {
+ setTableData(function (p) { return p.filter(function (r) { return keys.indexOf(r.key) === -1; }); });
+ selectedRowKeysState[1]([]);
+ message.success('已删除');
+ }
+ });
+ }
+
+ function openExport() {
+ message.info('导出当前筛选结果(原型)');
+ }
+
+ function openImport() {
+ importFileState[1](null);
+ importOpenState[1](true);
+ }
+
+ function onPickImportFile(e) {
+ var f = e && e.target && e.target.files && e.target.files[0];
+ if (!f) return;
+ var name = String(f.name || '').toLowerCase();
+ if (!(name.endsWith('.xls') || name.endsWith('.xlsx'))) {
+ message.error('仅支持 .xls、.xlsx 格式');
+ return;
+ }
+ importFileState[1](f);
+ }
+
+ function doImportUpload() {
+ if (!importFileState[0]) {
+ message.error('请先选取要上传的文件');
+ return;
+ }
+ message.success('上传成功(原型)');
+ importOpenState[1](false);
+ }
+
+ // 表格列
+ var columns = useMemo(function () {
+ return [
+ { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 110, fixed: 'left' },
+ {
+ title: 'vin码',
+ dataIndex: 'vin',
+ key: 'vin',
+ width: 150,
+ render: function (v) {
+ return React.createElement('span', { style: { color: '#f5222d', fontWeight: 600 } }, v || '-');
+ }
+ },
+ { title: '行驶证 注册日期', dataIndex: 'licenseRegDate', key: 'licenseRegDate', width: 140 },
+ { title: '行驶证 强制报废日期', dataIndex: 'licenseScrapDate', key: 'licenseScrapDate', width: 160 },
+ { title: '行驶证 有效期', dataIndex: 'licenseValidDate', key: 'licenseValidDate', width: 130 },
+ { title: '营运证 编号', dataIndex: 'operationCertNo', key: 'operationCertNo', width: 140 },
+ { title: '营运证 注册日期', dataIndex: 'operationRegDate', key: 'operationRegDate', width: 140 },
+ { title: '营运证 审验有效期', dataIndex: 'operationInspectValid', key: 'operationInspectValid', width: 160 },
+ { title: '营运证 有效期', dataIndex: 'operationValid', key: 'operationValid', width: 140 },
+ { title: '通行证 编号', dataIndex: 'passPermitNo', key: 'passPermitNo', width: 140 },
+ { title: '通行区域', dataIndex: 'passArea', key: 'passArea', width: 160, ellipsis: true },
+ { title: '通行证 有效期', dataIndex: 'passValid', key: 'passValid', width: 140 },
+ { title: '加氢证 编码', dataIndex: 'h2CertCode', key: 'h2CertCode', width: 140 },
+ { title: '加氢证 检验日期', dataIndex: 'h2CertInspectDate', key: 'h2CertInspectDate', width: 150 },
+ { title: '加氢卡 编码', dataIndex: 'h2CardCode', key: 'h2CardCode', width: 140 },
+ { title: '安全阀 检验日期', dataIndex: 'safetyValveInspectDate', key: 'safetyValveInspectDate', width: 150 },
+ { title: '安全阀 检验周期:单位 (月)', dataIndex: 'safetyValveCycleMonth', key: 'safetyValveCycleMonth', width: 200 },
+ { title: '压力表 检验日期', dataIndex: 'pressureGaugeInspectDate', key: 'pressureGaugeInspectDate', width: 150 },
+ { title: '压力表 检验周期:单位 (月)', dataIndex: 'pressureGaugeCycleMonth', key: 'pressureGaugeCycleMonth', width: 200 },
+ { title: '氢气瓶 厂家', dataIndex: 'h2BottleVendor', key: 'h2BottleVendor', width: 120 },
+ { title: '氢气瓶 检验日期', dataIndex: 'h2BottleInspectDate', key: 'h2BottleInspectDate', width: 150 },
+ { title: '氢气瓶 检验周期:单位 (月)', dataIndex: 'h2BottleCycleMonth', key: 'h2BottleCycleMonth', width: 200 },
+ {
+ title: '操作',
+ key: 'action',
+ width: 120,
+ fixed: 'right',
+ render: function (_, r) {
+ return React.createElement('div', { style: { display: 'flex', gap: 8 } },
+ React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('编辑:' + (r.plateNo || '')); } }, '编辑'),
+ React.createElement(Button, { type: 'link', size: 'small', onClick: function () { message.info('查看:' + (r.plateNo || '')); } }, '查看')
+ );
+ }
+ }
+ ];
+ }, []);
+
+ function filterOption(input, opt) {
+ return String((opt && opt.label) || '').toLowerCase().indexOf(String(input || '').toLowerCase()) !== -1;
+ }
+
+ 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: '车辆管理' }, { title: '证照管理' }] })
+ ),
+
+ React.createElement(Card, { title: '筛选与操作区', style: cardStyle },
+ React.createElement('div', { style: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 } },
+ React.createElement('div', { style: { flex: 1 } },
+ React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', gap: 12 } },
+ React.createElement(Select, { placeholder: '请选择客户名称', value: filters.customerName, onChange: function (v) { setFilters(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.customerName = v; return n; }); }, allowClear: true, showSearch: true, filterOption: filterOption, options: options.customerName }),
+ React.createElement(Select, { placeholder: '请选择合同编码', value: filters.contractCode, onChange: function (v) { setFilters(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.contractCode = v; return n; }); }, allowClear: true, showSearch: true, filterOption: filterOption, options: options.contractCode }),
+ React.createElement(Select, { placeholder: '请选择单车车牌号', value: filters.plateNo, onChange: function (v) { setFilters(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.plateNo = v; return n; }); }, allowClear: true, showSearch: true, filterOption: filterOption, options: options.plateNo }),
+ React.createElement(Select, { placeholder: '请选择车辆VIN', value: filters.vin, onChange: function (v) { setFilters(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.vin = v; return n; }); }, allowClear: true, showSearch: true, filterOption: filterOption, options: options.vin })
+ ),
+ moreOpenState[0]
+ ? React.createElement('div', { style: { marginTop: 12, display: 'grid', gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', gap: 12 } },
+ React.createElement(DatePicker, { placeholder: '请选择审验有效期', style: { width: '100%' }, value: filters.licenseInspectValid, onChange: function (v) { setFilters(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.licenseInspectValid = v; return n; }); } }),
+ React.createElement(DatePicker, { placeholder: '请选择证件有效期', style: { width: '100%' }, value: filters.operationPermitValid, onChange: function (v) { setFilters(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.operationPermitValid = v; return n; }); } }),
+ React.createElement(DatePicker, { placeholder: '请选择有效期', style: { width: '100%' }, value: filters.passPermitValid, onChange: function (v) { setFilters(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.passPermitValid = v; return n; }); } }),
+ React.createElement('div', null)
+ )
+ : null,
+ React.createElement('div', { style: { marginTop: 12, display: 'flex', alignItems: 'center', gap: 8 } },
+ React.createElement(Button, { type: 'link', onClick: function () { moreOpenState[1](!moreOpenState[0]); } }, moreOpenState[0] ? '收起条件 ▲' : '更多条件 ▼'),
+ React.createElement(Button, { onClick: resetFilters }, '重置'),
+ React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询')
+ )
+ ),
+
+ React.createElement('div', { style: { width: 360, display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 10 } },
+ React.createElement('div', { style: { color: '#666', fontSize: 13 } },
+ '选中 ', (selectedRowKeysState[0] || []).length, '/', (filteredData || []).length, ' 条'
+ ),
+ React.createElement('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap', justifyContent: 'flex-end' } },
+ React.createElement(Button, { type: 'primary', onClick: openAdd }, '+ 新增'),
+ React.createElement(Button, { danger: true, disabled: !(selectedRowKeysState[0] || []).length, onClick: confirmDeleteSelected }, '删除'),
+ React.createElement(Button, { onClick: openExport }, '导出'),
+ React.createElement(Button, { onClick: openImport }, '导入')
+ )
+ )
+ )
+ ),
+
+ React.createElement(Card, { title: '证照列表', style: cardStyle },
+ React.createElement(Table, {
+ rowKey: 'key',
+ size: 'middle',
+ bordered: true,
+ columns: columns,
+ dataSource: filteredData,
+ rowSelection: {
+ selectedRowKeys: selectedRowKeysState[0],
+ onChange: function (keys) { selectedRowKeysState[1](keys || []); }
+ },
+ scroll: { x: 2600 },
+ pagination: { pageSize: 20, showSizeChanger: true, pageSizeOptions: ['20', '50', '100'], showTotal: function (t) { return '共 ' + t + ' 条'; }, showQuickJumper: true }
+ })
+ ),
+
+ React.createElement(Modal, {
+ title: '证件导入',
+ open: importOpenState[0],
+ onCancel: function () { importOpenState[1](false); },
+ okText: '上传',
+ cancelText: '取消',
+ onOk: doImportUpload
+ },
+ React.createElement('div', { style: { marginBottom: 12, color: '#666', fontSize: 13 } }, '*支持文件类型 .xls .xlsx'),
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 } },
+ React.createElement(Button, { onClick: function () { message.info('下载模板:证件信息模板.xlsx(原型)'); } }, '模板下载'),
+ React.createElement('input', { type: 'file', accept: '.xls,.xlsx', onChange: onPickImportFile })
+ ),
+ React.createElement('div', { style: { fontSize: 13, color: '#333' } }, '已选文件:', (importFileState[0] && importFileState[0].name) ? importFileState[0].name : '—')
+ )
+ );
+};
+
diff --git a/web端/需求说明/交车单查看 b/web端/需求说明/交车单-查看
similarity index 96%
rename from web端/需求说明/交车单查看
rename to web端/需求说明/交车单-查看
index 720a7c3..7c24312 100644
--- a/web端/需求说明/交车单查看
+++ b/web端/需求说明/交车单-查看
@@ -1,5 +1,5 @@
交车单查看
-一个「数字化资产ONEOS运管平台」中的「交车单查看」模块
+一个「数字化资产ONEOS运管平台」中的「交车管理-查看」模块
1.面包屑:
1.1.运维管理-车辆业务-交车管理-查看交车信息
diff --git a/web端/需求说明/交车管理 b/web端/需求说明/交车管理
index 9a18167..e22b00c 100644
--- a/web端/需求说明/交车管理
+++ b/web端/需求说明/交车管理
@@ -33,9 +33,8 @@
3.1.9.创建人:显示交车任务单创建人;
3.1.10.最后修改时间:显示交车任务单最后修改时间,格式为:YYYY-MM-DD HH:MM;
3.1.11.最后修改人:显示交车任务单最后修改人姓名;
-3.1.12.操作:查看、交车;
- 3.1.12.1.查看:跳转交车管理-查看页;
- 3.1.12.2.交车单:点击跳转交车管理-交车单;
+3.1.12.操作:交车;
+ 3.1.12.1.交车单:点击跳转交车管理-交车单;
3.2.历史记录:显示以下字段:
3.2.1.预计交车时间:支持单日及开始-结束日期两种方式,格式为:YYYY-MM-DD及YYYY-MM-DD至YYYY-MM-DD,取自对应交车任务中预计交车时间;
diff --git a/web端/需求说明/财务管理/还车应结款 b/web端/需求说明/财务管理/还车应结款
new file mode 100644
index 0000000..05de422
--- /dev/null
+++ b/web端/需求说明/财务管理/还车应结款
@@ -0,0 +1,35 @@
+一个「数字化资产ONEOS运管平台」中的「还车应结款」模块
+#面包屑:财务管理-还车应结款
+
+1.筛选;
+1.1.合同编号:选择器,支持输入框内输入合同编号模糊搜索下拉显示匹配项;
+1.2.客户名称:选择器,支持输入框内输入客户名称模糊搜索下拉显示匹配项;
+1.3.项目名称:选择器,支持输入框内输入项目名称模糊搜索下拉显示匹配项;
+1.4.车牌号:选择器,支持输入框内输入车牌号模糊搜索下拉显示匹配项;
+1.5.还车时间:日期选择器,支持单输入框双日历选择开始-结束日期,精确至日;
+1.6.审批状态:选择器,分为待提交、待审批、审批中、审批完成、审批驳回、撤回;
+
+2.还车应结款列表:右侧为导出;
+2.1.合同编号:显示合同编号;
+2.2.提交情况(合并表头):分为安全组、业务服务组、运维组、能源组,内容为各组提交人姓名,前方为已提交/未提交图标;
+2.3.审批状态:待提交、待审批、审批中、审批完成、审批驳回、撤回;
+ 2.3.1.待提交:未完成4部门提交也未提交审批;
+ 2.3.2.待审批:已提交审批,但没有任意节点进行审批;
+ 2.3.3.审批中:已提交审批,已有节点完成审批,但未完成最终节点审批;
+ 2.3.4.审批完成:已提交审批,并完成最终节点审批;
+ 2.3.5.审批驳回:已提交审批,但在任意节点被驳回;
+ 2.3.6.撤回:已提交审批,但在最终节点审批完成前主动撤回;
+2.4.客户名称:显示合同对应客户名称;
+2.5.项目名称:显示合同对应项目名称;
+2.6.车牌号:显示合同对应车牌号;
+2.7.业务部门:显示合同对应业务部门;
+2.8.业务负责人:显示合同对应业务负责人;
+2.9.交车时间:显示该车辆交车时间;
+2.10.还车时间:显示该车辆还车时间;
+2.11.还车人:显示该车辆还车人姓名;
+2.12.操作:查看、生成账单、费用明细、撤回;
+ 2.12.1.查看:点击跳转还车应结款-查看页面;
+ 2.12.2.生成账单:点击生成账单,弹框显示账单界面,审批状态为待审批的记录才可生成账单,其他状态生成账单隐藏;
+ 2.12.3.费用明细:点击跳转还车应结款-费用明细页面,审批状态为待审批、审批中、审批完成时,不显示费用明细;
+ 2.12.4.撤回:审批状态为审批中时,不显示费用明细点击二次确认,提示:是否确认撤回,点击确定后,提示:撤回成功,同时审批状态修改为撤回;
+2.13.右下角为分页符,支持单页查看数据条数;
\ No newline at end of file
diff --git a/web端/需求说明/财务管理/还车应结款-费用明细 b/web端/需求说明/财务管理/还车应结款-费用明细
new file mode 100644
index 0000000..33d10ab
--- /dev/null
+++ b/web端/需求说明/财务管理/还车应结款-费用明细
@@ -0,0 +1,109 @@
+还车应结款
+一个「数字化资产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.取消:点击返回还车应结款列表页;
\ No newline at end of file
diff --git a/web端/需求说明/运维管理-车务管理/交车单-查看 b/web端/需求说明/运维管理-车务管理/交车单-查看
new file mode 100644
index 0000000..7c24312
--- /dev/null
+++ b/web端/需求说明/运维管理-车务管理/交车单-查看
@@ -0,0 +1,34 @@
+交车单查看
+一个「数字化资产ONEOS运管平台」中的「交车管理-查看」模块
+1.面包屑:
+1.1.运维管理-车辆业务-交车管理-查看交车信息
+
+2.表单:
+分为项目详情、交车明细、签章文件卡片3部分;
+2.1.项目详情为表单结构:
+2.1.1.项目名称:输入框禁用,显示该交车单对应车辆租赁合同中项目名称;
+2.1.2.合同编码:输入框禁用,显示该交车单对应车辆租赁合同中项目合同编码;
+2.1.2.客户名称:输入框禁用,显示该交车单对应车辆租赁合同中客户名称;
+2.1.3.预计交车日期:日期选择器禁用,显示该交车单预计交车日期,分为单日和日期区间两种,格式为:YYYY-MM-DD、YYYY-MM-DD至YYYY-MM-DD;
+2.1.3.交车区域:输入框禁用,显示该交车单对应车辆租赁合同中交车区域;
+2.1.4.交车地点:输入框禁用,显示该交车单对应车辆租赁合同中交车地点;
+2.1.5.业务部门:输入框禁用,显示该交车单对应车辆租赁合同中业务部门;
+2.1.6.业务负责人:输入框禁用,显示该交车单对应车辆租赁合同中业务负责人;
+2.2.交车明细:
+交车明细为列表结构;
+2.2.1.车辆类型:选择器禁用,显示该交车单对应车辆租赁合同中车辆类型;
+2.2.2.品牌:选择器禁用,显示该交车单对应车辆租赁合同中品牌;
+2.2.3.型号:选择器禁用,显示该交车单对应车辆租赁合同中型号;
+2.2.4.车牌号:选择器禁用,显示该交车单对应车辆租赁合同中车牌号;
+2.2.5.停车场:选择器禁用,显示该车辆交车时对应停车场;
+2.2.6.实际交车日期:日期选择器禁用,显示该交车单实际交车日期,精确至日,格式为:YYYY-MM-DD;
+2.2.7.交车人:输入框禁用,显示该交车单中该车辆实际交车人员(注意这里是交车单中某辆车的交车人员,一个交车单可同时多个交车人员操作同时交多辆车,而不是最终提交整个交车单的人员);
+2.2.8.完成时间:日期选择器禁用,显示该车辆完成交车时间(注意这里是完成交车,而不是完成交车单);
+2.2.9.司机姓名:显示交车时对应司机姓名;
+2.2.10.司机身份证:显示交车时对应司机身份证号码;
+2.2.11.司机手机号:显示交车时对应司机手机号;
+
+2.3.签章文件:
+2.3.1.最终授权人:显示最终签字授权人姓名,由小程序交车单最终提交时选择授权人;
+2.3.2.签章文件:点击查看该交车单对应签章文件信息;
+2.3.3.授权人签字时间:显示授权人通过E签宝签字的完成时间;
\ No newline at end of file
diff --git a/web端/需求说明/运维管理-车务管理/交车明细-查看 b/web端/需求说明/运维管理-车务管理/交车明细-查看
new file mode 100644
index 0000000..3aefa50
--- /dev/null
+++ b/web端/需求说明/运维管理-车务管理/交车明细-查看
@@ -0,0 +1,31 @@
+4.交车明细-查看:
+点击时弹层,弹层中表单显示一下内容:
+4.1.车牌号:必选项,选择器,支持从输入框内输入内容进行模糊搜索,默认拉取「车牌管理」中所有「车牌号」;
+4.2.车辆类型:输入框禁用,选择车牌号后自动拉取该车牌号对应「车辆类型」;
+4.3.品牌:输入框禁用,选择车牌号后自动拉取该车牌号对应「品牌」;
+4.4.型号:输入框禁用,选择车牌号后自动拉取该车牌号对应「型号」
+4.5.车辆识别代码:输入框禁用,选择车牌号后自动拉取该车牌号「车辆识别代码」;
+4.6.车身广告及放大字:必选项,开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中,安装时间以该条备车记录提交成功为准,不够选择广告照片和放大字照片字段隐藏不显示;
+4.7.广告照片:必填项,图片上传,最多支持上传1张图片,支持主流照片格式。上传后,上传按钮切换为显示已上传图片缩略图,支持点击预览和删除,删除后,切换为上传按钮,从备车记录自动反写;
+4.8.放大字照片:必填项,图片上传,最多支持上传1张图片,支持主流照片格式。上传后,上传按钮切换为显示已上传图片缩略图,支持点击预览和删除,删除后,切换为上传按钮,从备车记录自动反写;
+4.8.尾板:开关,选择车辆后拉取该车辆「后装设备」「尾板」,如果该车辆有「尾板」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「尾板」中,安装时间以该条备车记录提交成功为准;
+4.9.备胎照片:图片上传,最多支持上传1张图片,支持主流照片格式。上传时弹出卡片,提示正在识别中,请勿关闭页面,之后卡片左侧显示备胎照片,右侧输入框显示识别出的胎纹深度,后缀单位为mm,点击卡片中确认按钮,反写至备胎胎纹深度字段下;
+4.10.备胎胎纹深度:输入框,反写备胎照片OCR识别胎纹深度结果,支持修改;
+4.11.驾驶培训:附件上传按钮,上传司机现场培训二维码图片,识别成功后驾驶培训后方显示:已完成视频培训;
+4.12.司机证照:根据司机链接中的司机证照正面及反面照片及驾驶证、从业资格证照片显示;
+4.13.交车里程:必填项,输入框,单位为公里;
+4.14.交车电量:必填项,输入框,单位为kWh;
+4.15.交车氢量:必填项,输入框,单位为%或MPa,根据型号参数中该车型实际仪表盘单位显示;
+4.16.送车服务费:选填项,输入框,精确至2位小数,格式为xx.xx元;
+4.17.车辆检查:按钮,文字为备车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、选择、备注;
+ 2.11.1.类别:分为车灯、仪表盘、驾驶室、轮胎、液位检查、外观检查、车辆外观、其他、随车工具、随车证件、整车、燃料电池系统、冷机、制动系统;
+ 2.11.2.检查项目:车灯类别对应(大灯、转向灯、小灯、示廓灯、刹车灯、倒车灯、牌照灯、防雾灯、室内灯)、仪表盘对应(氢系统指示、电控系统指示、数值清晰准确、故障报警灯)、驾驶室对应(点烟器、车窗升降、按键开关、雨刮器、内后视镜是否正常、内/外门把手、安全带、空调冷暖风、仪表盘、门锁功能、手刹、车钥匙功能是否正常、喇叭、音响功能、遮阳板、主副驾座椅、方向盘、内饰干净整洁)
+ 2.11.3.检查情况:其他项为开关,在检查项目每项后方显示,默认为开,可手动进行关闭,轮胎为输入框,提示请输入胎纹深度;
+ 2.11.4.备注:输入框;
+
+4.交车照片:多个模块分3列显示,由照片标题和照片上传按钮组成;
+4.1.车辆:仪表盘、车辆正面、车辆左前方、车辆左后方、车辆右后方、车辆右前方;
+4.2.底盘:正前方底部、左侧前方底部、左侧后方底部、正后方底部、右侧后方底部、右侧前方底部;
+4.3.轮胎:左前轮、左后轮(内)、左后轮(外)、右前轮、右后轮(内)、右后轮(外)、备胎;
+4.4.瑕疵:照片上传按钮,最多支持4张照片;
+4.5.其他:照片上传按钮,最多支持4张照片;
\ No newline at end of file
diff --git a/web端/需求说明/运维管理-车务管理/交车管理-交车单 b/web端/需求说明/运维管理-车务管理/交车管理-交车单
new file mode 100644
index 0000000..2451967
--- /dev/null
+++ b/web端/需求说明/运维管理-车务管理/交车管理-交车单
@@ -0,0 +1,35 @@
+交车管理-交车单
+一个「数字化资产ONEOS运管平台」中的「交车管理-交车单」模块
+1.面包屑:
+1.1.运维管理-车辆业务-交车管理-交车单
+每个模块为一个单独卡片:
+
+2.项目详情:表单结构:
+2.1.项目名称:输入框禁用,显示该交车单对应车辆租赁合同中项目名称;
+2.2.合同编码:输入框禁用,显示该交车单对应车辆租赁合同中项目合同编码;
+2.3.客户名称:输入框禁用,显示该交车单对应车辆租赁合同中客户名称;
+2.4.预计交车日期:日期选择器禁用,显示该交车单预计交车日期,分为单日和日期区间两种,格式为:YYYY-MM-DD、YYYY-MM-DD至YYYY-MM-DD;
+2.5.交车区域:输入框禁用,显示该交车单对应车辆租赁合同中交车区域;
+2.6.交车地点:输入框禁用,显示该交车单对应车辆租赁合同中交车地点;
+2.7.业务部门:输入框禁用,显示该交车单对应车辆租赁合同中业务部门;
+2.8.业务负责人:输入框禁用,显示该交车单对应车辆租赁合同中业务负责人;
+
+3.交车明细:列表结构;
+3.1.序号:与车辆租赁合同中序号对应,显示该交车任务所有车辆序号;
+3.2.品牌:选择器禁用,显示该交车单对应车辆租赁合同中品牌;
+3.3.型号:选择器禁用,显示该交车单对应车辆租赁合同中型号;
+3.4.车牌号:已交车车辆显示车牌号,未交车车辆显示-;
+3.5.实际交车日期:日期选择器禁用,显示该交车单实际交车日期,精确至日,格式为:YYYY-MM-DD;
+3.6.交车人:输入框禁用,显示该交车单中该车辆实际交车人员(注意这里是交车单中某辆车的交车人员,一个交车单可同时多个交车人员操作同时交多辆车,而不是最终提交整个交车单的人员);
+3.7.交车状态:已完成、待提交、已签章;
+ 3.7.1.已完成:已完成交车提交,但还未完成被授权人签章;
+ 3.7.2.待提交:仅点击保存,未完成交车提交;
+ 3.7.3.已签章:已完成交车提交并完成被授权人签章;
+3.8.操作:查看、编辑、下载签章文件;
+ 3.8.1.查看:点击跳转该车辆交车明细;
+ 3.8.2.编辑:交车状态为待提交时显示,点击弹出卡片至交车明细编辑页;
+ 3.8.3.下载签章文件:交车完成并完成被授权人签章时显示,点击下载签章文件;
+
+4.底部为提交、取消按钮;
+ 4.1.提交:提交按钮必须所有车辆交车状态为已签章时才可提交,否则为禁用状态;
+ 4.2.取消:点击返回交车管理列表页;
\ No newline at end of file
diff --git a/web端/需求说明/运维管理-车务管理/交车管理-交车单-编辑 b/web端/需求说明/运维管理-车务管理/交车管理-交车单-编辑
new file mode 100644
index 0000000..076e33a
--- /dev/null
+++ b/web端/需求说明/运维管理-车务管理/交车管理-交车单-编辑
@@ -0,0 +1,42 @@
+交车管理-交车单-编辑
+一个「数字化资产ONEOS运管平台」中的「交车管理-交车单-编辑」模块
+1.面包屑:
+1.1.运维管理-车辆业务-交车管理-交车单-编辑
+每个模块为一个单独卡片:
+
+2.交车明细:列表结构;
+2.1.车辆类型:输入框(禁用),根据车牌号反写车辆类型;
+2.2.品牌:输入框(禁用),根据车牌号反写品牌;
+2.2.型号:输入框(禁用),根据车牌号反写型号;
+2.3.车牌号:选择器,输入框,支持输入内容下拉模糊匹配选项,仅能选择备车库中车辆;
+2.4.车辆识别代码:输入框(禁用),根据车牌号反写车辆识别代码;
+2.5.车身广告及放大字:必选项,开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中, 安装时间以该条备车记录提交成功为准,不够选择广告照片和放大字照片字段隐藏不显示;
+2.6.广告照片:必填项,图片上传,最多支持上传1张图片,支持主流照片格式。上传后,上传按钮切换为显示已上传图片缩略图,支持点击预览和删除,删除后,切换为上传按钮,从备车记录自动反写;
+2.7.放大字照片:必填项,图片上传,最多支持上传1张图片,支持主流照片格式。上传后,上传按钮切换为显示已上传图片缩略图,支持点击预览和删除,删除后,切换为上传按钮,从备车记录自动反写;
+2.8.尾板:必填项,开关,选择车辆后拉取该车辆「后装设备」「尾板」,如果该车辆有「尾板」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「尾板」中,安装时间以该条备车记录提交成功为准;
+2.9.备胎照片:必填项,图片上传,最多支持上传1张图片,支持主流照片格式。上传时弹出卡片,提示正在识别中,请勿关闭页面,之后卡片左侧显示备胎照片,右侧输入框显示识别出的胎纹深度,后缀单位为mm,点击卡片中确认按钮,反写至备胎胎纹深度字段下;
+2.10.备胎胎纹深度:必填项,输入框,反写备胎照片OCR识别胎纹深度结果,支持修改;
+2.11.驾驶培训:必填项,附件上传按钮,后方为提示信息:请上传司机现场培训二维码图片。识别成功后隐藏驾驶培训按钮和提示,显示:已完成视频培训;
+2.12.司机证照:显示4张照片、身份证(正面)、身份证(反面)、驾驶证、从业资格证,根据驾驶培训上传二维码识别后自动反写;
+2.13.交车里程:必填项,输入框,单位为公里;
+2.14.交车电量:必填项,输入框,单位为kWh;
+2.15.交车氢量:必填项,输入框,单位为%或MPa,根据型号参数中该车型实际仪表盘单位显示;
+2.16.送车服务费:选填项,输入框,精确至2位小数,格式为xx.xx元;
+2.17.车辆检查:按钮,文字为交车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、检查情况、备注;
+ 2.11.1.类别:分为车灯、仪表盘、驾驶室、轮胎、液位检查、外观检查、车辆外观、其他、随车工具、随车证件、整车、燃料电池系统、冷机、制动系统;
+ 2.11.2.检查项目:车灯类别对应(大灯、转向灯、小灯、示廓灯、刹车灯、倒车灯、牌照灯、防雾灯、室内灯)、仪表盘对应(氢系统指示、电控系统指示、数值清晰准确、故障报警灯)、驾驶室对应(点烟器、车窗升降、按键开关、雨刮器、内后视镜是否正常、内/外门把手、安全带、空调冷暖风、仪表盘、门锁功能、手刹、车钥匙功能是否正常、喇叭、音响功能、遮阳板、主副驾座椅、方向盘、内饰干净整洁)
+ 2.11.3.检查情况:其他项为开关,在检查项目每项后方显示,从备车记录反写,可手动进行关闭,轮胎为输入框,提示请输入胎纹深度;
+ 2.11.4.备注:输入框;
+
+3.交车照片:多个模块分3列显示,由照片标题和照片上传按钮组成,照片点击上传,从本地文件上传单张图片,上传成功后可通过图片右上角删除按钮删除,点击图片可放大预览;;
+3.1.车辆:必填项,包括仪表盘、车辆正面、车辆左前方、车辆左后方、车辆右后方、车辆右前方;
+3.2.底盘:必填项,包括正前方底部、左侧前方底部、左侧后方底部、正后方底部、右侧后方底部、右侧前方底部;
+3.3.轮胎:必填项,包括左前轮、左后轮(内)、左后轮(外)、右前轮、右后轮(内)、右后轮(外)、备胎;
+3.4.瑕疵:必填项,包括照片上传按钮,最多支持4张照片;
+3.5.其他:必填项,包括照片上传按钮,最多支持4张照片;
+照片点击上传,从本地文件上传单张图片,上传成功后可通过图片右上角删除按钮删除,点击图片可放大预览;
+
+4.底部为提交、取消按钮;
+ 4.1.提交:点击进行二次确认,点击确认完成该车辆交车;
+ 4.2.保存:点击暂存交车单(不做校验);
+ 4.2.取消:点击返回交车单页;
\ No newline at end of file