运维/财务:完善交车单编辑/查看与还车应结款页面

- 交车单编辑页:布局对齐、检查单合并、照片必填与需求说明
- 新增交车单查看页:只读展示与样例数据
- 还车应结款相关页面与需求说明补齐

Made-with: Cursor
This commit is contained in:
王冕
2026-03-18 22:10:05 +08:00
parent e842a778a4
commit 92d3b97bca
18 changed files with 6636 additions and 719 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.DS_Store
**/.DS_Store

View File

@@ -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 }, '返回')
)
);
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -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' })
)
)
);
};

View File

@@ -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)
),
// 在线打印已按最新需求移除
);
};

View File

@@ -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))
);
};

View File

@@ -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('跳转交车管理-交车单'); } }, '交车单');
} }
];

View File

@@ -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 : '—')
)
);
};

View File

@@ -1,5 +1,5 @@
交车单查看
一个「数字化资产ONEOS运管平台」中的「交车查看」模块
一个「数字化资产ONEOS运管平台」中的「交车管理-查看」模块
1.面包屑:
1.1.运维管理-车辆业务-交车管理-查看交车信息

View File

@@ -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取自对应交车任务中预计交车时间

View File

@@ -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.右下角为分页符,支持单页查看数据条数;

View File

@@ -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.取消:点击返回还车应结款列表页;

View File

@@ -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签宝签字的完成时间

View File

@@ -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张照片

View File

@@ -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.取消:点击返回交车管理列表页;

View File

@@ -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.取消:点击返回交车单页;