// 【重要】必须使用 const Component 作为组件变量名 // 车辆业务 - 交车管理(ONEOS运管平台,布局参照新增租赁合同) var DV_KPI_STYLE = '' + '.dv-kpi-stats-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin-bottom:16px;}' + '@media (max-width:768px){.dv-kpi-stats-row{grid-template-columns:repeat(1,minmax(0,1fr));}}' + '.lc-alert-card{display:flex;align-items:flex-start;gap:12px;padding:14px 30px 14px 16px;border-radius:12px;border:1px solid #e2e8f0;background:#fff;position:relative;overflow:hidden;min-width:0;}' + '.lc-alert-card-main{flex:1;min-width:0;}' + '.lc-alert-card-icon{flex-shrink:0;width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;}' + '.lc-alert-card-val{font-size:26px;font-weight:800;line-height:1.1;color:#0f172a;font-variant-numeric:tabular-nums;}' + '.lc-alert-card-title{font-size:13px;font-weight:600;color:#334155;margin-top:2px;}' + '.lc-alert-card-tip-anchor{position:absolute;top:8px;right:8px;z-index:2;line-height:0;}' + '.lc-alert-card-tip{width:18px;height:18px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;color:#94a3b8;background:rgba(255,255,255,.92);border:1px solid #e2e8f0;cursor:help;line-height:0;}' + '.lc-alert-card-tip:hover{color:#64748b;border-color:#cbd5e1;background:#fff;}' + '.lc-alert-card--total{background:linear-gradient(135deg,#f8fafc 0%,#fff 100%);}' + '.lc-alert-card--total .lc-alert-card-icon{background:#e2e8f0;color:#475569;}' + '.lc-alert-card--progress{background:linear-gradient(135deg,#fff7ed 0%,#fff 55%);border-color:#fed7aa;}' + '.lc-alert-card--progress .lc-alert-card-icon{background:#ffedd5;color:#ea580c;}' + '.lc-alert-card--progress .lc-alert-card-val{color:#c2410c;}' + '.lc-alert-card--completed{background:linear-gradient(135deg,#ecfdf5 0%,#fff 55%);border-color:#bbf7d0;}' + '.lc-alert-card--completed .lc-alert-card-icon{background:#d1fae5;color:#059669;}' + '.lc-alert-card--completed .lc-alert-card-val{color:#047857;}' + '.lc-alert-card-clickable{cursor:pointer;transition:box-shadow .2s ease,border-color .2s ease,transform .2s ease;}' + '.lc-alert-card-clickable:hover{box-shadow:0 4px 14px rgba(15,23,42,.08);}' + '.lc-alert-card-active{box-shadow:0 0 0 2px rgba(22,93,255,.2)!important;border-color:#165dff!important;}'; var DV_KPI_ICONS = { total: React.createElement('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' }, React.createElement('rect', { x: 3, y: 3, width: 7, height: 7 }), React.createElement('rect', { x: 14, y: 3, width: 7, height: 7 }), React.createElement('rect', { x: 14, y: 14, width: 7, height: 7 }), React.createElement('rect', { x: 3, y: 14, width: 7, height: 7 })), progress: React.createElement('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' }, React.createElement('circle', { cx: 12, cy: 12, r: 10 }), React.createElement('polyline', { points: '12 6 12 12 16 14' })), completed: React.createElement('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' }, React.createElement('path', { d: 'M22 11.08V12a10 10 0 1 1-5.93-9.14' }), React.createElement('polyline', { points: '22 4 12 14.01 9 11.01' })) }; var DV_KPI_TIP_SVG = React.createElement('svg', { width: 12, height: 12, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2.2, strokeLinecap: 'round', strokeLinejoin: 'round' }, React.createElement('circle', { cx: 12, cy: 12, r: 10 }), React.createElement('line', { x1: 12, y1: 16, x2: 12, y2: 12 }), React.createElement('line', { x1: 12, y1: 8, x2: 12.01, y2: 8 })); function formatDeliveryRegion(region) { if (!region || region === '-') return '-'; var r = String(region).trim(); if (r.indexOf('-') === -1) { var m = r.match(/^(.+?(?:省|市|自治区|特别行政区))(.+)$/); if (m && m[2]) r = m[1] + '-' + m[2]; } return r; } function parseDeliveryRegionParts(region) { if (!region || region === '-') return { province: '', city: '', raw: '' }; var r = String(region).trim(); var idx = r.indexOf('-'); if (idx >= 0) { return { province: r.slice(0, idx).trim(), city: r.slice(idx + 1).trim(), raw: r }; } if (r.indexOf('省') >= 0) return { province: r, city: '', raw: r }; if (r.indexOf('市') >= 0) return { province: '', city: r, raw: r }; return { province: r, city: '', raw: r }; } function regionCityMatch(cityA, cityB) { if (!cityA || !cityB) return false; return cityA === cityB || cityA.indexOf(cityB) >= 0 || cityB.indexOf(cityA) >= 0; } /** 运维人员区域权限是否覆盖目标区域(省-市) */ function matchRegionPermission(permissions, targetRegion) { var target = parseDeliveryRegionParts(formatDeliveryRegion(targetRegion)); if (!target.province && !target.city) return true; var perms = permissions || []; for (var i = 0; i < perms.length; i++) { var perm = String(perms[i] || '').trim(); if (!perm) continue; if (perm.indexOf('-') >= 0) { var scoped = parseDeliveryRegionParts(perm); if (scoped.province && target.province && scoped.province !== target.province) continue; if (scoped.city) { if (regionCityMatch(target.city, scoped.city)) return true; continue; } if (scoped.province && scoped.province === target.province) return true; continue; } if (perm.indexOf('省') >= 0) { if (target.province === perm) return true; continue; } if (regionCityMatch(target.city, perm)) return true; } return false; } var DV_RESERVE_VEHICLE_STATUS_READY = '已备车'; function getOperatorRegionPermissions() { if (typeof window !== 'undefined' && window.DV_MOCK_OPERATOR_REGION_PERMISSIONS && window.DV_MOCK_OPERATOR_REGION_PERMISSIONS.length) { return window.DV_MOCK_OPERATOR_REGION_PERMISSIONS.slice(); } return ['浙江省-嘉兴市']; } function filterRowsByOperatorRegion(rows) { var perms = getOperatorRegionPermissions(); return (rows || []).filter(function (row) { return matchRegionPermission(perms, row.deliveryRegion); }); } function filterSelectableReserveVehicles(vehicles, operatorPermissions) { var perms = operatorPermissions || getOperatorRegionPermissions(); return (vehicles || []).filter(function (vehicle) { if ((vehicle.vehicleStatus || '') !== DV_RESERVE_VEHICLE_STATUS_READY) return false; return matchRegionPermission(perms, vehicle.parkingRegion); }); } function isDeliverySignedStatus(status) { return status === '客户已签章' || status === '已签章'; } function buildDeliverySignFileName(record) { var plate = (record.plateNo && String(record.plateNo).trim()) ? String(record.plateNo).trim() : '车牌待选'; var orderId = record.orderId != null ? String(record.orderId) : 'unknown'; var vehicleKey = record.vehicleKey != null ? String(record.vehicleKey) : ''; return '交车签章文件_' + orderId + (vehicleKey ? '_' + vehicleKey : '') + '_' + plate + '.pdf'; } function buildDeliverySignFileContent(record) { var plate = (record.plateNo && String(record.plateNo).trim()) ? String(record.plateNo).trim() : '车牌待选'; return [ '交车签章文件(原型 Mock,联调后对接 E 签宝签章 PDF)', '', '合同编号:' + (record.contractCode || '-'), '项目名称:' + (record.projectName || '-'), '客户名称:' + (record.customerName || '-'), '车牌号:' + plate, '品牌型号:' + (record.brand || '-') + ' / ' + (record.model || '-'), '交车人:' + (record.deliveryPerson || '-'), '完成交车时间:' + (record.deliveryTime || '-'), '签章状态:客户已签章', '', '生成时间:' + new Date().toLocaleString('zh-CN', { hour12: false }) ].join('\n'); } function escapeSignHtml(text) { return String(text == null ? '-' : text) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function buildDeliverySignPreviewHtml(record) { var filename = buildDeliverySignFileName(record); var plate = (record.plateNo && String(record.plateNo).trim()) ? String(record.plateNo).trim() : '车牌待选'; var rows = [ ['文件名称', filename], ['合同编号', record.contractCode || '-'], ['项目名称', record.projectName || '-'], ['客户名称', record.customerName || '-'], ['车牌号', plate], ['品牌型号', (record.brand || '-') + ' / ' + (record.model || '-')], ['交车人', record.deliveryPerson || '-'], ['完成交车时间', record.deliveryTime || '-'], ['签章状态', '客户已签章'], ['签章方', record.customerName || '-'], ['预览时间', new Date().toLocaleString('zh-CN', { hour12: false })] ]; var bodyRows = rows.map(function (row) { return '' + escapeSignHtml(row[0]) + '' + escapeSignHtml(row[1]) + ''; }).join(''); return '' + '' + escapeSignHtml(filename) + '' + '
E签宝签章文件' + '

' + escapeSignHtml(filename) + '

数字化资产 ONEOS · 交车管理 · 签章文件预览(原型 Mock)
' + '
' + bodyRows + '
' + '
本页为原型预览占位,联调后将展示 E 签宝返回的真实签章 PDF 或在线预览地址。
' + '
'; } function previewDeliverySignFile(record) { if (!record || !isDeliverySignedStatus(record.deliveryStatus || record.status)) return; if (typeof window === 'undefined') return; var html = buildDeliverySignPreviewHtml(record); var blob = new Blob([html], { type: 'text/html;charset=utf-8' }); var url = URL.createObjectURL(blob); var opened = window.open(url, '_blank', 'noopener,noreferrer'); if (!opened) { URL.revokeObjectURL(url); if (typeof message !== 'undefined' && message.warning) message.warning('请允许浏览器弹出窗口以预览签章文件'); return; } if (typeof message !== 'undefined' && message.success) message.success('已在新页面打开签章文件预览'); setTimeout(function () { URL.revokeObjectURL(url); }, 120000); } function downloadDeliverySignFile(record) { if (!record || !isDeliverySignedStatus(record.deliveryStatus || record.status)) return; var filename = buildDeliverySignFileName(record); var content = buildDeliverySignFileContent(record); var blob = new Blob([content], { type: 'application/octet-stream' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = filename; a.click(); URL.revokeObjectURL(url); if (typeof message !== 'undefined' && message.success) message.success('已开始下载签章文件'); } // 查看交车单:禁用输入框/选择器可读性增强(背景更淡、文字纯黑) var DV_VIEW_READONLY_CTRL_CSS = '' + '.dv-edit-drawer-view .ant-input[disabled],' + '.dv-edit-drawer-view textarea.ant-input[disabled]{' + 'color:#000!important;-webkit-text-fill-color:#000!important;' + 'background-color:#fafafa!important;border-color:#e5e7eb!important;' + 'cursor:default!important;opacity:1!important;' + '}' + '.dv-edit-drawer-view .ant-input-affix-wrapper-disabled,' + '.dv-edit-drawer-view .ant-input-affix-wrapper[disabled]{' + 'color:#000!important;background-color:#fafafa!important;' + 'border-color:#e5e7eb!important;cursor:default!important;opacity:1!important;' + '}' + '.dv-edit-drawer-view .ant-input-affix-wrapper-disabled input[disabled],' + '.dv-edit-drawer-view .ant-input-affix-wrapper[disabled] input[disabled]{' + 'color:#000!important;-webkit-text-fill-color:#000!important;' + 'background-color:transparent!important;cursor:default!important;' + '}' + '.dv-edit-drawer-view .ant-input-affix-wrapper-disabled .ant-input-suffix,' + '.dv-edit-drawer-view .ant-input-affix-wrapper[disabled] .ant-input-suffix{' + 'color:rgba(0,0,0,.65)!important;' + '}' + '.dv-edit-drawer-view .ant-select-disabled.ant-select .ant-select-selector{' + 'color:#000!important;background-color:#fafafa!important;' + 'border-color:#e5e7eb!important;cursor:default!important;opacity:1!important;' + '}' + '.dv-edit-drawer-view .ant-select-disabled .ant-select-selection-item,' + '.dv-edit-drawer-view .ant-select-disabled .ant-select-selection-placeholder{' + 'color:#000!important;' + '}'; // 使用方式:window.DeliveryEditDrawer(props) function DeliveryEditDrawer(props) { var useState = React.useState; var useMemo = React.useMemo; var useCallback = React.useCallback; var useRef = React.useRef; var useEffect = React.useEffect; var open = props.open; var record = props.record; var onClose = props.onClose; var onSave = props.onSave; var onSubmit = props.onSubmit; var readOnly = props.readOnly === true || props.mode === 'view'; var antd = window.antd; var Drawer = antd.Drawer; var Button = antd.Button; var Input = antd.Input; var Select = antd.Select; var Switch = antd.Switch; var Modal = antd.Modal; var Table = antd.Table; var Tag = antd.Tag; var message = antd.message; function RequiredLabel(text) { return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 4 } }, React.createElement('span', { style: { color: '#ef4444', fontWeight: 600 } }, '*'), React.createElement('span', null, text) ); } 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); } } var reserveVehicles = useMemo(function () { return [ { plateNo: '浙F80088', vehicleStatus: DV_RESERVE_VEHICLE_STATUS_READY, parkingRegion: '浙江省-嘉兴市', parkingLot: '嘉兴港区氢能停车场', vehicleType: '厢式车', brand: '福田', model: 'BJ1180', vin: 'LJNAU1A2XK7654321', hasAd: false, adPhoto: [], bigWordPhoto: [], hasTailboard: false }, { plateNo: '浙F88601', vehicleStatus: DV_RESERVE_VEHICLE_STATUS_READY, parkingRegion: '浙江省-嘉兴市', parkingLot: '平湖指定停车场', vehicleType: '4.5吨冷链车', brand: '现代', model: '帕力安牌4.5吨冷链车', vin: 'LNBSCPKB8RR123888', hasAd: false, adPhoto: [], bigWordPhoto: [], hasTailboard: true }, { plateNo: '浙A10088', vehicleStatus: DV_RESERVE_VEHICLE_STATUS_READY, parkingRegion: '浙江省-杭州市', parkingLot: '未来科技城地下停车场', vehicleType: '城配货车', brand: '福田', model: 'BJ1190', vin: 'LJNAU1A2XK8888001', hasAd: true, adPhoto: [{ uid: 'ad-hz', name: '广告照片.jpg', url: 'https://dummyimage.com/600x400/e2e8f0/475569&text=Ad-HZ' }], bigWordPhoto: [], hasTailboard: false }, { plateNo: '粤AGP4598', vehicleStatus: DV_RESERVE_VEHICLE_STATUS_READY, parkingRegion: '广东省-广州市', parkingLot: '广州南沙物流园停车场', vehicleType: '4.5吨冷链车', brand: '现代', model: '帕力安牌4.5吨冷链车', vin: 'LNBSCPKB9RR223402', hasAd: false, adPhoto: [], bigWordPhoto: [], hasTailboard: false }, { plateNo: '川A99999', vehicleStatus: DV_RESERVE_VEHICLE_STATUS_READY, parkingRegion: '四川省-成都市', parkingLot: '成都龙泉驿停车场', vehicleType: '4.5吨冷链车', brand: '现代', model: '帕力安牌4.5吨冷链车', vin: 'LNBSCPKB9RR223999', hasAd: false, adPhoto: [], bigWordPhoto: [], hasTailboard: false }, { plateNo: '京A12345', vehicleStatus: '备车中', parkingRegion: '浙江省-嘉兴市', parkingLot: '嘉兴测试停车场', vehicleType: '牵引车', brand: '东风', model: 'DFH1180', vin: 'LJNAU1A2XK1234567', hasAd: true, adPhoto: [{ uid: 'ad1', name: '广告照片.jpg', url: 'https://dummyimage.com/600x400/e2e8f0/475569&text=Ad' }], bigWordPhoto: [{ uid: 'bw1', name: '放大字.jpg', url: 'https://dummyimage.com/600x400/e2e8f0/475569&text=BigWord' }], hasTailboard: true }, { plateNo: '沪A30003', vehicleStatus: DV_RESERVE_VEHICLE_STATUS_READY, parkingRegion: '上海市-上海市', parkingLot: '浦东停车场', vehicleType: '厢式车', brand: '重汽', model: 'HOWO-T5G', vin: 'LJNAU1A2XK9999000', hasAd: true, adPhoto: [{ uid: 'ad2', name: '广告2.jpg', url: 'https://dummyimage.com/600x400/e2e8f0/475569&text=Ad2' }], bigWordPhoto: [{ uid: 'bw2', name: '放大字2.jpg', url: 'https://dummyimage.com/600x400/e2e8f0/475569&text=BW2' }], hasTailboard: true } ]; }, []); var plateOptions = useMemo(function () { return filterSelectableReserveVehicles(reserveVehicles, getOperatorRegionPermissions()).map(function (v) { return { value: v.plateNo, label: v.plateNo + ' · ' + (v.parkingLot || formatDeliveryRegion(v.parkingRegion)) }; }); }, [reserveVehicles]); var vehicleByPlate = useMemo(function () { var map = {}; reserveVehicles.forEach(function (v) { map[v.plateNo] = v; }); return map; }, [reserveVehicles]); function isPlatePendingInForm(plateNo) { if (plateNo == null || plateNo === undefined) return true; var s = String(plateNo).trim(); return s === '' || s === '-'; } function displayDisabledField(v) { var s = v == null || v === undefined ? '' : String(v).trim(); return s || '-'; } function displayFormVin(plateNo, vin) { if (isPlatePendingInForm(plateNo)) return '-'; return displayDisabledField(vin); } function syncVehicleInfoFromReserveRecord(reserveRecord, deliveryRec, plate) { // 备车记录 → 交车单「车辆信息」同步(驾驶培训字段不参与同步,见 buildInitialForm / handlePlateChange) var hasPlate = plate && !isPlatePendingInForm(plate); return { vehicleType: (reserveRecord && reserveRecord.vehicleType) || (deliveryRec && deliveryRec.vehicleType) || '', brand: (reserveRecord && reserveRecord.brand) || (deliveryRec && deliveryRec.brand) || '', model: (reserveRecord && reserveRecord.model) || (deliveryRec && deliveryRec.model) || '', vin: hasPlate ? ((reserveRecord && reserveRecord.vin) || (deliveryRec && deliveryRec.vin) || '') : '', hasAd: !!(reserveRecord && reserveRecord.hasAd), adPhoto: (reserveRecord && reserveRecord.adPhoto) ? reserveRecord.adPhoto.slice() : [], bigWordPhoto: (reserveRecord && reserveRecord.bigWordPhoto) ? reserveRecord.bigWordPhoto.slice() : [], hasTailboard: reserveRecord ? !!reserveRecord.hasTailboard : false, spareTirePhoto: (reserveRecord && reserveRecord.spareTirePhoto) ? reserveRecord.spareTirePhoto.slice() : [], spareTireDepth: (reserveRecord && reserveRecord.spareTireDepth) || '' }; } function buildInitialForm(rec) { var plate = (rec && rec.plateNo && String(rec.plateNo).trim()) || undefined; var reserveRecord = plate ? vehicleByPlate[plate] : null; if (!reserveRecord && rec && rec.brand && plate) { reserveRecord = { plateNo: plate, vehicleType: rec.vehicleType || '', brand: rec.brand || '', model: rec.model || '', vin: rec.vin || '', hasAd: false, adPhoto: [], bigWordPhoto: [], hasTailboard: false }; } var synced = syncVehicleInfoFromReserveRecord(reserveRecord, rec, plate); return Object.assign({ plateNo: plate, trainingRecognized: false, driverLicenses: [], driverFrontPhoto: [], mileageKm: rec && rec.deliveryMileage != null ? String(rec.deliveryMileage) : '', batteryPct: rec && rec.deliveryElec != null ? String(rec.deliveryElec) : '', hydrogenAmount: rec && rec.deliveryH2 != null ? String(rec.deliveryH2) : '', hydrogenUnit: (rec && rec.deliveryH2Unit === 'MPa') ? 'MPa' : '%', serviceFee: '' }, synced); } var formState = useState(buildInitialForm(null)); var form = formState[0]; var setForm = formState[1]; var activeSectionState = useState('basic'); var activeSection = activeSectionState[0]; var setActiveSection = activeSectionState[1]; var submittingState = useState(false); var submitting = submittingState[0]; var setSubmitting = submittingState[1]; var previewState = useState({ open: false, url: '', title: '', gallery: [], index: 0 }); var ocrModalState = useState({ open: false, photoUrl: '', depth: '6.50' }); var trainingInputRef = useRef(null); useEffect(function () { if (open && record) { var initial = buildInitialForm(record); var nextPhotos = createEmptyPhotos(); if (readOnly && record.deliveryStatus && record.deliveryStatus !== '未开始' && record.deliveryStatus !== '已保存') { initial = Object.assign({}, initial, buildDriverInfoFromPickupCode(), buildViewFormPhotoExtras()); nextPhotos = buildViewDeliveryPhotos(); } setForm(initial); setActiveSection('basic'); setSubmitting(false); setPhotos(nextPhotos); setInspectionList(buildInspectionList()); } }, [open, record && record.id, readOnly]); function updateForm(patch) { setForm(function (p) { return Object.assign({}, p, patch); }); } function handlePlateChange(v) { if (!v) { updateForm({ plateNo: undefined, vehicleType: '', brand: '', model: '', vin: '', hasAd: false, adPhoto: [], bigWordPhoto: [], hasTailboard: false, spareTirePhoto: [], spareTireDepth: '', trainingRecognized: false, driverLicenses: [], driverFrontPhoto: [] }); return; } var reserveRecord = vehicleByPlate[v]; updateForm(Object.assign({ plateNo: v, trainingRecognized: false, driverLicenses: [], driverFrontPhoto: [] }, syncVehicleInfoFromReserveRecord(reserveRecord, null, v))); } function makeThumb(url, onPreview, onRemove) { return React.createElement('div', { style: { width: 72, height: 72, borderRadius: 8, border: '1px solid #e2e8f0', overflow: 'hidden', position: 'relative', background: '#f8fafc' } }, React.createElement('img', { src: url, style: { width: '100%', height: '100%', objectFit: 'cover', cursor: 'pointer' }, onClick: onPreview }), onRemove ? React.createElement('button', { type: 'button', 'aria-label': '删除图片', style: { position: 'absolute', right: 4, top: 4, width: 22, height: 22, borderRadius: 999, border: 'none', background: 'rgba(15,23,42,.65)', color: '#fff', cursor: 'pointer', fontSize: 12, lineHeight: '22px', padding: 0 }, onClick: function (e) { e.stopPropagation(); onRemove(); } }, '×') : null ); } function mockViewPhoto(uid, name) { return [{ uid: uid, name: name + '.jpg', url: 'https://dummyimage.com/640x360/e2e8f0/475569&text=' + encodeURIComponent(name) }]; } function mockDeliveryPhotoItem(uid, seed, slotLabel) { return { uid: uid, name: slotLabel + '.jpg', url: 'https://picsum.photos/seed/dv-' + seed + '/960/540' }; } function mockDeliveryPhotoSlot(uid, seed, slotLabel) { return [mockDeliveryPhotoItem(uid, seed, slotLabel)]; } function mockDeliveryPhotoList(items) { return items.map(function (it) { return mockDeliveryPhotoItem(it.uid, it.seed, it.label); }); } function buildDeliveryPhotoGallery(photoData) { var list = []; var slotGroups = [ { key: 'vehicle', label: '车辆' }, { key: 'chassis', label: '底盘' }, { key: 'tire', label: '轮胎' } ]; slotGroups.forEach(function (group) { var groupMap = (photoData && photoData[group.key]) || {}; Object.keys(groupMap).forEach(function (slotLabel) { (groupMap[slotLabel] || []).forEach(function (file) { list.push({ uid: file.uid, url: file.url, name: file.name, title: group.label + ' · ' + slotLabel }); }); }); }); ['defect', 'other'].forEach(function (groupKey) { var groupLabel = groupKey === 'defect' ? '瑕疵' : '其他'; ((photoData && photoData[groupKey]) || []).forEach(function (file, idx) { var files = (photoData && photoData[groupKey]) || []; list.push({ uid: file.uid, url: file.url, name: file.name, title: files.length > 1 ? groupLabel + ' · ' + (file.name || (groupLabel + (idx + 1))) : groupLabel + ' · ' + (file.name || groupLabel) }); }); }); return list; } function closePhotoPreview() { previewState[1]({ open: false, url: '', title: '', gallery: [], index: 0 }); } function openPhotoPreview(options) { var gallery = (options && options.gallery) || []; var index = 0; if (gallery.length && options && options.uid) { for (var i = 0; i < gallery.length; i++) { if (gallery[i].uid === options.uid) { index = i; break; } } } var current = gallery.length ? gallery[index] : { url: options.url, title: options.title }; previewState[1]({ open: true, url: current.url, title: current.title || (options && options.title) || '预览', gallery: gallery, index: index }); } function shiftPhotoPreview(step) { previewState[1](function (prev) { var gallery = prev.gallery || []; if (gallery.length <= 1) return prev; var nextIndex = prev.index + step; if (nextIndex < 0) nextIndex = gallery.length - 1; if (nextIndex >= gallery.length) nextIndex = 0; var item = gallery[nextIndex]; return Object.assign({}, prev, { index: nextIndex, url: item.url, title: item.title }); }); } function buildViewFormPhotoExtras() { return { spareTirePhoto: mockViewPhoto('sp1', '备胎照片'), spareTireDepth: '6.50' }; } function buildViewDeliveryPhotos() { return { vehicle: { '仪表盘': mockDeliveryPhotoSlot('v1', 'vehicle-dash', '仪表盘'), '车辆正前': mockDeliveryPhotoSlot('v2', 'vehicle-front', '车辆正前'), '车辆左前方': mockDeliveryPhotoSlot('v3', 'vehicle-left-front', '车辆左前方'), '车辆左后方': mockDeliveryPhotoSlot('v4', 'vehicle-left-rear', '车辆左后方'), '车辆右前方': mockDeliveryPhotoSlot('v5', 'vehicle-right-front', '车辆右前方'), '车辆右后方': mockDeliveryPhotoSlot('v6', 'vehicle-right-rear', '车辆右后方') }, chassis: { '正前方位底部': mockDeliveryPhotoSlot('c1', 'chassis-front', '正前方位底部'), '左侧前方底部': mockDeliveryPhotoSlot('c2', 'chassis-left-front', '左侧前方底部'), '左侧后方底部': mockDeliveryPhotoSlot('c3', 'chassis-left-rear', '左侧后方底部'), '正后方位底部': mockDeliveryPhotoSlot('c4', 'chassis-rear', '正后方位底部'), '右侧前方底部': mockDeliveryPhotoSlot('c5', 'chassis-right-front', '右侧前方底部'), '右侧后方底部': mockDeliveryPhotoSlot('c6', 'chassis-right-rear', '右侧后方底部') }, tire: { '左前': mockDeliveryPhotoSlot('t1', 'tire-left-front', '左前'), '右前': mockDeliveryPhotoSlot('t2', 'tire-right-front', '右前'), '左后内': mockDeliveryPhotoSlot('t3', 'tire-left-rear-in', '左后内'), '左后外': mockDeliveryPhotoSlot('t4', 'tire-left-rear-out', '左后外'), '右后内': mockDeliveryPhotoSlot('t5', 'tire-right-rear-in', '右后内'), '右后外': mockDeliveryPhotoSlot('t6', 'tire-right-rear-out', '右后外') }, defect: mockDeliveryPhotoList([ { uid: 'd1', seed: 'defect-scratch', label: '瑕疵-刮擦' }, { uid: 'd2', seed: 'defect-dent', label: '瑕疵-凹陷' }, { uid: 'd3', seed: 'defect-paint', label: '瑕疵-漆面' } ]), other: mockDeliveryPhotoList([ { uid: 'o1', seed: 'other-tools', label: '其他-随车工具' }, { uid: 'o2', seed: 'other-cargo', label: '其他-车厢物品' } ]) }; } function ReadonlyPhotoBox(boxProps) { var label = boxProps.label; var value = boxProps.value || []; var gallery = boxProps.gallery; return React.createElement('div', null, label ? React.createElement('div', { style: { fontSize: 13, color: '#475569', marginBottom: 8, fontWeight: 500 } }, label) : null, value.length > 0 ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' } }, value.map(function (f) { return React.createElement('div', { key: f.uid }, makeThumb(f.url, function () { openPhotoPreview({ gallery: gallery && gallery.length ? gallery : null, uid: f.uid, url: f.url, title: (gallery && gallery.length) ? ((gallery.filter(function (g) { return g.uid === f.uid; })[0] || {}).title || f.name || label || '预览') : (f.name || label || '预览') }); }) ); }) ) : React.createElement('span', { style: { fontSize: 13, color: '#94a3b8' } }, '-') ); } function UploadBox(uploadProps) { var label = uploadProps.label; var value = uploadProps.value || []; var unlimited = !!uploadProps.unlimited; var max = unlimited ? Infinity : (uploadProps.max || 1); var onChange = uploadProps.onChange; var disabled = !!uploadProps.disabled; function handlePick(e) { if (disabled) return; 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 (!unlimited && next.length > max) next = next.slice(next.length - max); onChange && onChange(next); }); e.target.value = ''; } return React.createElement('div', null, label ? React.createElement('div', { style: { fontSize: 13, color: '#475569', marginBottom: 8, fontWeight: 500 } }, label) : null, React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10, 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 }); }, disabled ? null : function () { onChange && onChange(value.filter(function (x) { return x.uid !== f.uid; })); }) ); }), (!disabled && (unlimited || value.length < max)) ? React.createElement('label', { style: { width: 72, height: 72, borderRadius: 8, border: '1px dashed #cbd5e1', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#64748b', cursor: 'pointer', fontSize: 20, lineHeight: 1, background: '#fff', transition: 'border-color .2s' } }, React.createElement('input', { type: 'file', accept: 'image/*', style: { display: 'none' }, onChange: handlePick }), React.createElement('span', { style: { fontSize: 22, lineHeight: 1, marginBottom: 2 } }, '+'), React.createElement('span', { style: { fontSize: 12 } }, '上传') ) : null ), uploadProps.tip ? React.createElement('div', { style: { marginTop: 6, fontSize: 12, color: '#94a3b8', lineHeight: 1.5 } }, uploadProps.tip) : null ); } var EDIT_PHOTO_UPLOAD_HINT = '照片上传说明:jpg、jpeg、png、gif、webp 格式,单张不超过 5MB,支持预览与删除(适用于本页所有照片上传项)'; function createEmptyPhotos() { return { vehicle: { '仪表盘': [], '车辆正前': [], '车辆左前方': [], '车辆左后方': [], '车辆右前方': [], '车辆右后方': [] }, chassis: { '正前方位底部': [], '左侧前方底部': [], '左侧后方底部': [], '正后方位底部': [], '右侧前方底部': [], '右侧后方底部': [] }, tire: { '左前': [], '右前': [], '左后内': [], '左后外': [], '右后内': [], '右后外': [] }, defect: [], other: [] }; } function updatePhotoSlot(groupKey, slotKey, list) { setPhotos(function (p) { var n = Object.assign({}, p); if (slotKey) { n[groupKey] = Object.assign({}, p[groupKey] || {}); n[groupKey][slotKey] = list; } else { n[groupKey] = list; } return n; }); } function PhotoModuleTitle(title, required) { return React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10, fontSize: 14, fontWeight: 700, color: '#0f172a' } }, React.createElement('span', { style: { width: 3, height: 14, borderRadius: 2, background: '#2563eb', flexShrink: 0 } }), (required && !readOnly) ? RequiredLabel(title) : title ); } function PhotoGridColumn(title, items, required, previewGallery) { return React.createElement('div', { style: { marginBottom: 20, minWidth: 0 } }, PhotoModuleTitle(title, required), React.createElement('div', { style: { border: '1px solid #e2e8f0', borderRadius: 10, padding: 12, background: '#fafbfc' } }, React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 12 } }, items.map(function (it) { return React.createElement('div', { key: it.key }, readOnly ? ReadonlyPhotoBox({ label: it.label, value: it.value, gallery: previewGallery }) : UploadBox({ label: required ? RequiredLabel(it.label) : it.label, value: it.value, max: 1, disabled: readOnly, onChange: it.onChange }) ); }) ) ) ); } function PhotoGridSimple(title, value, onChange, previewGallery) { return React.createElement('div', { style: { marginBottom: 20, minWidth: 0 } }, PhotoModuleTitle(title, false), React.createElement('div', { style: { border: '1px solid #e2e8f0', borderRadius: 10, padding: 12, background: '#fafbfc' } }, readOnly ? ReadonlyPhotoBox({ value: value, gallery: previewGallery }) : UploadBox({ value: value, unlimited: true, disabled: readOnly, onChange: onChange }) ) ); } function buildPhotoGridItems(groupKey) { var group = photos[groupKey] || {}; return Object.keys(group).map(function (k) { return { key: groupKey + '-' + k, label: k, value: group[k] || [], onChange: function (l) { updatePhotoSlot(groupKey, k, l); } }; }); } function FormItem(itemProps) { return React.createElement('div', { style: { marginBottom: 16, minWidth: 0 } }, React.createElement('div', { style: { fontSize: 13, color: '#475569', marginBottom: 6, fontWeight: 500 } }, itemProps.required ? RequiredLabel(itemProps.label) : itemProps.label ), itemProps.children ); } var inspectionCategoryItems = { '车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'], '仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'], '驾驶室': ['点烟器', '车窗升降', '按键开关', '雨刮器', '内后视镜是否正常', '内/外门把手', '安全带', '空调冷暖风', '仪表盘', '门锁功能', '手刹', '车钥匙功能是否正常', '喇叭', '音响功能', '遮阳板', '主副驾座椅', '方向盘', '内饰干净整洁'], '轮胎': ['前左胎', '前右胎', '后左胎', '后右胎', '备胎'], '液位检查': ['冷却液', '制动液', '玻璃水'], '外观检查': ['车身外观', '漆面', '玻璃'], '车辆外观': ['整车外观'], '其他': ['其他检查项'], '随车工具': ['三角牌', '灭火器', '反光背心'], '随车证件': ['行驶证', '营运证', '保险单'], '整车': ['整车状态'], '燃料电池系统': ['氢系统', '储氢瓶'], '冷机': ['冷机运行'], '制动系统': ['制动踏板', '驻车制动'] }; function buildInspectionList() { var list = []; var categories = Object.keys(inspectionCategoryItems); for (var ci = 0; ci < categories.length; ci++) { var cat = categories[ci]; var items = inspectionCategoryItems[cat] || []; for (var ji = 0; ji < items.length; ji++) { var it = items[ji]; var isTire = cat === '轮胎'; list.push({ key: 'ins-' + ci + '-' + ji, category: cat, item: it, checked: true, treadDepth: isTire ? '6.5' : '', remark: '' }); } } return list; } var inspectionListState = useState(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: 120, 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: 180 }, { title: '检查情况', dataIndex: 'checked', key: 'checked', width: 160, render: function (_, insRecord) { var isTire = insRecord && insRecord.category === '轮胎'; return isTire ? React.createElement(Input, { value: insRecord.treadDepth, placeholder: '请输入胎纹深度', addonAfter: 'mm', disabled: readOnly, onChange: function (e) { updateInspectionRow(insRecord.key, { treadDepth: e.target.value }); } }) : React.createElement(Switch, { checked: !!insRecord.checked, disabled: readOnly, onChange: function (v) { updateInspectionRow(insRecord.key, { checked: !!v }); } }); } }, { title: '备注', dataIndex: 'remark', key: 'remark', render: function (_, insRecord) { return React.createElement(Input, { value: insRecord.remark, placeholder: '请输入', disabled: readOnly, onChange: function (e) { updateInspectionRow(insRecord.key, { remark: e.target.value }); } }); } } ]; }, [readOnly]); var photoState = useState(createEmptyPhotos); var photos = photoState[0]; var setPhotos = photoState[1]; var showSignFileSection = readOnly && record && isDeliverySignedStatus(record.deliveryStatus || record.status); var signFileName = showSignFileSection ? buildDeliverySignFileName(record) : ''; var sectionNav = [ { key: 'basic', label: '交车车辆' }, { key: 'equip', label: '车辆信息' }, { key: 'metrics', label: '交车数据' }, { key: 'inspection', label: '交车检查单' }, { key: 'photos', label: '交车照片' } ]; if (showSignFileSection) { sectionNav.push({ key: 'esign', label: 'E签宝签章' }); } var SECTION_SCROLL_MARGIN = 12; function scrollToSection(key) { setActiveSection(key); var el = document.getElementById('dv-edit-section-' + key); if (el && el.scrollIntoView) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); } function SectionCard(cardProps) { return React.createElement('div', { id: cardProps.id, style: { scrollMarginTop: SECTION_SCROLL_MARGIN, marginBottom: 16, background: '#fff', borderRadius: 12, border: '1px solid #e2e8f0', boxShadow: '0 1px 2px rgba(15,23,42,.04)', overflow: 'hidden' } }, React.createElement('div', { style: { padding: '14px 16px', borderBottom: '1px solid #f1f5f9', fontSize: 14, fontWeight: 700, color: '#0f172a', display: 'flex', alignItems: 'center', gap: 8 } }, React.createElement('span', { style: { width: 3, height: 14, borderRadius: 2, background: '#2563eb', flexShrink: 0 } }), cardProps.title ), React.createElement('div', { style: { padding: '16px 16px 8px' } }, cardProps.children) ); } function validateSubmit() { if (isEmpty(form.plateNo)) return '请选择车牌号'; if (form.hasAd) { if (!form.adPhoto.length) return '请上传广告照片'; if (!form.bigWordPhoto.length) return '请上传放大字照片'; } if (!form.trainingRecognized) return '请上传司机提车码并完成识别'; if (isEmpty(form.mileageKm)) return '请填写交车里程'; if (isEmpty(form.batteryPct)) return '请填写交车电量'; if (isEmpty(form.hydrogenAmount)) return '请填写交车氢量'; var requiredPhotoGroups = [ { key: 'vehicle', label: '车辆' }, { key: 'chassis', label: '底盘' }, { key: 'tire', label: '轮胎' } ]; for (var pi = 0; pi < requiredPhotoGroups.length; pi++) { var pg = requiredPhotoGroups[pi]; var groupMap = photos && photos[pg.key]; var keys = groupMap ? Object.keys(groupMap) : []; for (var ki = 0; ki < keys.length; ki++) { var pk = keys[ki]; if (!(groupMap[pk] && groupMap[pk].length)) return '请上传' + pg.label + '照片:' + pk; } } return ''; } function buildPatchFromForm() { return { plateNo: form.plateNo || '', vehicleType: form.vehicleType, brand: form.brand, model: form.model, vin: form.vin, deliveryMileage: form.mileageKm === '' ? null : Number(form.mileageKm), deliveryElec: form.batteryPct === '' ? null : Number(form.batteryPct), deliveryH2: form.hydrogenAmount === '' ? null : Number(form.hydrogenAmount), deliveryH2Unit: form.hydrogenUnit, deliveryStatus: '已保存' }; } function handleSaveClick() { onSave && onSave(buildPatchFromForm()); message.success('已保存'); } function handleSubmitClick() { var err = validateSubmit(); if (err) { message.error(err); return; } Modal.confirm({ title: '确认交车', content: '请确认信息填写无误,点击确认完成该车辆交车。', okText: '确认', cancelText: '取消', onOk: function () { setSubmitting(true); setTimeout(function () { setSubmitting(false); onSubmit && onSubmit(buildPatchFromForm()); message.success('交车成功'); onClose && onClose(); }, 400); } }); } function isInvalidPickupCodeFile(file) { var name = ((file && file.name) || '').toLowerCase(); return name.indexOf('invalid') >= 0 || name.indexOf('无效') >= 0; } function buildDriverInfoFromPickupCode() { return { trainingRecognized: true, driverFrontPhoto: [{ uid: 'front', name: '司机正面照', url: 'https://dummyimage.com/600x400/e2e8f0/475569&text=Driver-Front' }], driverLicenses: [ { uid: 'id1', name: '身份证(正面)', url: 'https://dummyimage.com/600x400/e2e8f0/475569&text=ID-F' }, { uid: 'id2', name: '身份证(反面)', url: 'https://dummyimage.com/600x400/e2e8f0/475569&text=ID-B' }, { uid: 'dl', name: '驾驶证', url: 'https://dummyimage.com/600x400/e2e8f0/475569&text=DL' }, { uid: 'qc', name: '从业资格证', url: 'https://dummyimage.com/600x400/e2e8f0/475569&text=QC' } ] }; } function handleTrainingPick(e) { var f = e && e.target && e.target.files && e.target.files[0]; if (!f) return; var pickedFile = f; setTimeout(function () { if (isInvalidPickupCodeFile(pickedFile)) { updateForm({ trainingRecognized: false, driverFrontPhoto: [], driverLicenses: [] }); message.error('提车码无效,请重新选择'); return; } updateForm(buildDriverInfoFromPickupCode()); message.success('提车码识别成功,已加载司机证照'); }, 500); e.target.value = ''; } function openPickupCodePicker() { trainingInputRef.current && trainingInputRef.current.click(); } if (!open) return null; var drawerTitle = readOnly ? '查看交车单' : '编辑交车单'; var summaryLabelWidth = 80; function renderSummaryField(labelText, valueText) { return React.createElement('div', { style: { display: 'flex', alignItems: 'flex-start', gap: 6, minWidth: 0 } }, React.createElement('span', { style: { color: '#94a3b8', flexShrink: 0, width: summaryLabelWidth, textAlign: 'left' } }, labelText), React.createElement('span', { style: { color: '#334155', flex: 1, minWidth: 0, textAlign: 'left', wordBreak: 'break-all', lineHeight: 1.5 } }, valueText) ); } var summaryCard = record ? React.createElement('div', { style: { marginBottom: 16, padding: '12px 14px', borderRadius: 10, background: 'linear-gradient(135deg,#f8fafc 0%,#fff 100%)', border: '1px solid #e2e8f0', fontSize: 13 } }, React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center', marginBottom: 10, paddingBottom: 10, borderBottom: '1px solid #f1f5f9' } }, React.createElement(Tag, { style: { margin: 0, border: 'none', background: '#eff6ff', color: '#2563eb', fontWeight: 600 } }, record.customerName || '-'), React.createElement('span', { style: { color: '#475569' } }, record.contractCode || '-'), (function () { var status = record.deliveryStatus || '未开始'; var signed = isDeliverySignedStatus(status); return React.createElement(Tag, { style: { margin: 0, border: 'none', background: signed ? '#16a34a' : '#f1f5f9', color: signed ? '#fff' : '#475569', fontWeight: signed ? 600 : 400, cursor: signed ? 'pointer' : 'default', textDecoration: signed ? 'underline' : 'none', textUnderlineOffset: signed ? '2px' : undefined }, title: signed ? '点击下载签章文件' : undefined, role: signed ? 'button' : undefined, tabIndex: signed ? 0 : undefined, onClick: signed ? function (e) { e.stopPropagation(); downloadDeliverySignFile(record); } : undefined, onKeyDown: signed ? function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); e.stopPropagation(); downloadDeliverySignFile(record); } } : undefined }, status); })() ), React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2,minmax(0,1fr))', gap: '8px 16px' } }, renderSummaryField('项目:', record.projectName || '-'), renderSummaryField('交车地点:', formatDeliveryRegion(record.deliveryRegion)), renderSummaryField('品牌型号:', (record.brand || '-') + ' / ' + (record.model || '-')), renderSummaryField('任务来源:', record.taskSource || '-') ) ) : null; var sectionNavEl = React.createElement('nav', { 'aria-label': '交车单分节导航', style: { width: 108, flexShrink: 0, position: 'sticky', top: 0, alignSelf: 'flex-start', paddingTop: 4, zIndex: 2 } }, React.createElement('div', { style: { fontSize: 12, color: '#94a3b8', fontWeight: 600, marginBottom: 8, paddingLeft: 12, letterSpacing: '.02em' } }, '目录'), sectionNav.map(function (s) { var active = activeSection === s.key; return React.createElement('button', { key: s.key, type: 'button', onClick: function () { scrollToSection(s.key); }, style: { display: 'block', width: '100%', textAlign: 'left', border: 'none', cursor: 'pointer', padding: '9px 12px', borderRadius: 8, fontSize: 13, lineHeight: 1.4, fontWeight: active ? 600 : 500, background: active ? '#eff6ff' : 'transparent', color: active ? '#2563eb' : '#64748b', borderLeft: active ? '3px solid #2563eb' : '3px solid transparent', marginBottom: 2, transition: 'background .2s,color .2s' } }, s.label); }) ); var sectionBasic = React.createElement(SectionCard, { id: 'dv-edit-section-basic', title: '交车车辆' }, React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2,minmax(0,1fr))', gap: '0 16px' } }, React.createElement(FormItem, { label: '车牌号', required: !readOnly }, React.createElement(Select, { value: form.plateNo, options: plateOptions, showSearch: true, filterOption: filterOption, placeholder: '请选择已备车车辆(按区域权限过滤)', allowClear: !readOnly, disabled: readOnly, style: { width: '100%' }, onChange: handlePlateChange }) ), React.createElement(FormItem, { label: '车辆类型' }, React.createElement(Input, { value: displayDisabledField(form.vehicleType), disabled: true })), React.createElement(FormItem, { label: '品牌' }, React.createElement(Input, { value: displayDisabledField(form.brand), disabled: true })), React.createElement(FormItem, { label: '型号' }, React.createElement(Input, { value: displayDisabledField(form.model), disabled: true })), React.createElement(FormItem, { label: '车辆识别代码' }, React.createElement(Input, { value: displayFormVin(form.plateNo, form.vin), disabled: true })) ) ); var sectionEquip = React.createElement(SectionCard, { id: 'dv-edit-section-equip', title: '车辆信息' }, !readOnly ? React.createElement('div', { style: { marginBottom: 12, fontSize: 12, color: '#94a3b8', lineHeight: 1.6 } }, EDIT_PHOTO_UPLOAD_HINT) : null, React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0 16px', marginBottom: form.hasAd ? 8 : 0 } }, React.createElement(FormItem, { label: '车身广告及放大字', required: !readOnly }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10 } }, React.createElement(Switch, { checked: !!form.hasAd, disabled: readOnly, onChange: function (v) { updateForm({ hasAd: !!v }); } }), React.createElement('span', { style: { fontSize: 13, color: '#64748b' } }, form.hasAd ? '有车身广告' : '无车身广告') ) ), React.createElement(FormItem, { label: '尾板', required: !readOnly }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10 } }, React.createElement(Switch, { checked: !!form.hasTailboard, disabled: readOnly, onChange: function (v) { updateForm({ hasTailboard: !!v }); } }), React.createElement('span', { style: { fontSize: 13, color: '#64748b' } }, form.hasTailboard ? '有尾板' : '无尾板') ) ) ), form.hasAd ? React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 8 } }, readOnly ? React.createElement(React.Fragment, null, React.createElement(ReadonlyPhotoBox, { label: '广告照片', value: form.adPhoto }), React.createElement(ReadonlyPhotoBox, { label: '放大字照片', value: form.bigWordPhoto }) ) : React.createElement(React.Fragment, null, React.createElement(UploadBox, { label: RequiredLabel('广告照片'), value: form.adPhoto, max: 1, disabled: readOnly, onChange: function (l) { updateForm({ adPhoto: l }); } }), React.createElement(UploadBox, { label: RequiredLabel('放大字照片'), value: form.bigWordPhoto, max: 1, disabled: readOnly, onChange: function (l) { updateForm({ bigWordPhoto: l }); } }) ) ) : null, React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } }, readOnly ? React.createElement(ReadonlyPhotoBox, { label: '备胎照片', value: form.spareTirePhoto }) : React.createElement(UploadBox, { label: '备胎照片', value: form.spareTirePhoto, max: 1, disabled: readOnly, onChange: function (l) { updateForm({ spareTirePhoto: l }); if (!readOnly && l && l.length) ocrModalState[1]({ open: true, photoUrl: l[0].url, depth: '6.50' }); } }), React.createElement(FormItem, { label: '备胎胎纹深度' }, React.createElement(Input, { value: form.spareTireDepth, placeholder: readOnly ? undefined : '请输入', addonAfter: 'mm', disabled: readOnly, onChange: function (e) { updateForm({ spareTireDepth: e.target.value }); } }) ) ), React.createElement(FormItem, { label: '驾驶培训', required: !readOnly }, form.trainingRecognized ? React.createElement('div', null, readOnly ? React.createElement('span', { style: { color: '#16a34a', fontWeight: 600, fontSize: 13 } }, '已完成视频培训') : React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } }, React.createElement('span', { style: { color: '#16a34a', fontWeight: 600, fontSize: 13 } }, '已完成视频培训'), React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0, height: 'auto' }, onClick: openPickupCodePicker }, '重新选择提车码') ), !readOnly ? React.createElement('div', { style: { marginTop: 8, fontSize: 12, color: '#64748b', lineHeight: 1.5 } }, '已根据提车码内绑定的司机信息,自动加载司机证照等照片') : null, form.driverFrontPhoto && form.driverFrontPhoto.length ? React.createElement('div', { style: { marginTop: 12 } }, React.createElement('div', { style: { fontSize: 13, color: '#475569', marginBottom: 8, fontWeight: 500 } }, '司机正面照'), makeThumb(form.driverFrontPhoto[0].url, function () { previewState[1]({ open: true, url: form.driverFrontPhoto[0].url, title: '司机正面照' }); }) ) : null ) : (readOnly ? React.createElement('span', { style: { fontSize: 13, color: '#94a3b8' } }, '-') : React.createElement(React.Fragment, null, React.createElement('input', { type: 'file', ref: trainingInputRef, accept: 'image/*', style: { display: 'none' }, onChange: handleTrainingPick }), React.createElement(Button, { onClick: openPickupCodePicker }, '上传司机提车码'), React.createElement('div', { style: { marginTop: 8, fontSize: 12, color: '#94a3b8', lineHeight: 1.5 } }, '上传后将自动识别提车码内绑定的司机信息,并展示司机证照等照片') )) ), form.driverLicenses && form.driverLicenses.length ? React.createElement(FormItem, { label: '司机证照' }, React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4,minmax(0,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 }); }), React.createElement('div', { style: { marginTop: 4, fontSize: 12, color: '#64748b', textAlign: 'center' } }, f.name) ); }) ) ) : null ); var sectionInspection = React.createElement(SectionCard, { id: 'dv-edit-section-inspection', title: '交车检查单' }, React.createElement(Table, { rowKey: 'key', size: 'small', pagination: false, bordered: true, dataSource: inspectionList, columns: inspectionColumns, scroll: { x: 640 } }) ); var sectionMetrics = React.createElement(SectionCard, { id: 'dv-edit-section-metrics', title: '交车数据' }, React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2,minmax(0,1fr))', gap: '0 16px' } }, React.createElement(FormItem, { label: '交车里程', required: !readOnly }, React.createElement(Input, { value: form.mileageKm, placeholder: '请输入', addonAfter: 'km', disabled: readOnly, onChange: function (e) { updateForm({ mileageKm: e.target.value }); } }) ), React.createElement(FormItem, { label: '交车电量', required: !readOnly }, React.createElement(Input, { value: form.batteryPct, placeholder: '请输入', addonAfter: '%', disabled: readOnly, onChange: function (e) { updateForm({ batteryPct: e.target.value }); } }) ), React.createElement(FormItem, { label: '交车氢量', required: !readOnly }, React.createElement(Input, { value: form.hydrogenAmount, placeholder: '请输入', addonAfter: form.hydrogenUnit, disabled: readOnly, onChange: function (e) { updateForm({ hydrogenAmount: e.target.value }); } }) ), React.createElement(FormItem, { label: '送车服务费' }, React.createElement(Input, { value: form.serviceFee, placeholder: '选填', addonAfter: '元', disabled: readOnly, onChange: function (e) { updateForm({ serviceFee: e.target.value }); } }) ) ) ); var deliveryPhotoGallery = readOnly ? buildDeliveryPhotoGallery(photos) : []; var sectionPhotos = React.createElement(SectionCard, { id: 'dv-edit-section-photos', title: '交车照片' }, !readOnly ? React.createElement('div', { style: { marginBottom: 12, fontSize: 12, color: '#94a3b8', lineHeight: 1.6 } }, EDIT_PHOTO_UPLOAD_HINT) : null, PhotoGridColumn('车辆', buildPhotoGridItems('vehicle'), !readOnly, deliveryPhotoGallery), PhotoGridColumn('底盘', buildPhotoGridItems('chassis'), !readOnly, deliveryPhotoGallery), PhotoGridColumn('轮胎', buildPhotoGridItems('tire'), !readOnly, deliveryPhotoGallery), PhotoGridSimple('瑕疵', photos.defect || [], function (l) { updatePhotoSlot('defect', null, l); }, deliveryPhotoGallery), PhotoGridSimple('其他', photos.other || [], function (l) { updatePhotoSlot('other', null, l); }, deliveryPhotoGallery) ); var sectionEsign = showSignFileSection ? React.createElement(SectionCard, { id: 'dv-edit-section-esign', title: 'E签宝签章文件' }, React.createElement('div', { style: { display: 'flex', alignItems: 'flex-start', gap: 14, padding: '12px 14px', borderRadius: 10, border: '1px solid #e2e8f0', background: 'linear-gradient(135deg,#f8fafc 0%,#fff 100%)' } }, React.createElement('div', { style: { width: 44, height: 44, borderRadius: 10, background: '#fee2e2', color: '#dc2626', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, fontWeight: 700, flexShrink: 0 } }, 'PDF'), React.createElement('div', { style: { flex: 1, minWidth: 0 } }, React.createElement('div', { style: { fontSize: 14, fontWeight: 600, color: '#0f172a', wordBreak: 'break-all' } }, signFileName), React.createElement('div', { style: { marginTop: 6, fontSize: 12, color: '#64748b', lineHeight: 1.6 } }, React.createElement('div', null, '签章时间:', record.deliveryTime || '-'), React.createElement('div', null, '签章方:', record.customerName || '-') ) ), React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8, flexShrink: 0 } }, React.createElement(Button, { size: 'small', onClick: function () { previewDeliverySignFile(record); } }, '预览'), React.createElement(Button, { type: 'primary', size: 'small', onClick: function () { downloadDeliverySignFile(record); } }, '下载') ) ) ) : null; return React.createElement(React.Fragment, null, readOnly ? React.createElement('style', null, DV_VIEW_READONLY_CTRL_CSS) : null, React.createElement(Drawer, { open: open, onClose: onClose, width: Math.min(960, typeof window !== 'undefined' ? window.innerWidth - 24 : 960), title: drawerTitle, destroyOnClose: true, styles: { body: { padding: '16px 20px 88px', background: '#f8fafc' }, footer: { borderTop: '1px solid #e2e8f0', padding: '12px 20px' } }, footer: readOnly ? React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end' } }, React.createElement(Button, { onClick: onClose }, '关闭') ) : React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 10 } }, React.createElement(Button, { onClick: onClose, disabled: submitting }, '取消'), React.createElement(Button, { onClick: handleSaveClick, disabled: submitting }, '保存'), React.createElement(Button, { type: 'primary', loading: submitting, onClick: handleSubmitClick }, '提交') ) }, React.createElement('div', { className: readOnly ? 'dv-edit-drawer-view' : undefined }, summaryCard, React.createElement('div', { style: { display: 'flex', gap: 16, alignItems: 'flex-start' } }, sectionNavEl, React.createElement('div', { style: { flex: 1, minWidth: 0 } }, sectionBasic, sectionEquip, sectionMetrics, sectionInspection, sectionPhotos, sectionEsign ) ) ) ), React.createElement(Modal, { open: !!previewState[0].open, title: previewState[0].title || '预览', footer: null, onCancel: closePhotoPreview, width: 920, styles: { body: { padding: '12px 20px 20px' } } }, previewState[0].url ? React.createElement('div', null, React.createElement('div', { style: { textAlign: 'center', background: '#0f172a', borderRadius: 10, padding: 12 } }, React.createElement('img', { src: previewState[0].url, alt: previewState[0].title, style: { width: '100%', maxHeight: '72vh', objectFit: 'contain' } }) ), previewState[0].gallery && previewState[0].gallery.length > 1 ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, marginTop: 16 } }, React.createElement(Button, { onClick: function () { shiftPhotoPreview(-1); } }, '上一张'), React.createElement('span', { style: { fontSize: 13, color: '#64748b', fontVariantNumeric: 'tabular-nums' } }, String(previewState[0].index + 1) + ' / ' + previewState[0].gallery.length ), React.createElement(Button, { type: 'primary', onClick: function () { shiftPhotoPreview(1); } }, '下一张') ) : null ) : null ), React.createElement(Modal, { open: !!ocrModalState[0].open, title: '备胎识别', onCancel: function () { ocrModalState[1](Object.assign({}, ocrModalState[0], { open: false })); }, onOk: function () { updateForm({ spareTireDepth: ocrModalState[0].depth }); ocrModalState[1](Object.assign({}, ocrModalState[0], { open: false })); message.success('已反写胎纹深度'); }, okText: '确认', cancelText: '取消' }, React.createElement(Input, { value: ocrModalState[0].depth, addonAfter: 'mm', onChange: function (e) { ocrModalState[1](Object.assign({}, ocrModalState[0], { depth: e.target.value })); } }) ) ); } if (typeof window !== 'undefined') { window.DeliveryEditDrawer = DeliveryEditDrawer; } function getDeliveryManageRequirementDoc() { return [ '交车管理 — 产品需求说明', '模块:数字化资产 ONEOS 运管平台 · 运维管理 · 车辆业务 · 交车管理', '版本:与当前原型一致(列表一车一行 + 抽屉编辑交车单)', '', '══════════════════════════════════════', '一、模块目标', '══════════════════════════════════════', '为运维人员提供交车任务的全流程管理能力:按车辆维度查看交车进度、筛选统计、导出报表,并在列表内通过抽屉完成单车交车单查看、编辑、保存与提交。', '', '══════════════════════════════════════', '二、页面结构', '══════════════════════════════════════', '2.1 面包屑:运维管理 / 车辆业务 / 交车管理', '2.2 右上角「查看需求说明」:打开本说明弹窗', '2.3 页面自上而下:筛选区 → KPI 统计卡片 → 列表区(含车牌快捷筛选、导出)', '', '══════════════════════════════════════', '三、数据统计(KPI 卡片)', '══════════════════════════════════════', '替代原 Tab,三张可点击卡片联动列表,统计范围均为「当前筛选条件命中后的全部车辆行」:', '3.1 全部交车任务:进行中 + 已完成', '3.2 进行中的交车任务:交车状态为「未开始」「已保存」「待客户签章」', '3.3 已完成的交车任务:交车状态为「客户已签章」(运维与客户 E 签宝均完成)', '3.4 卡片右上角问号:悬停展示指标说明;点击卡片切换列表数据,选中态高亮', '3.5 默认选中:进行中的交车任务', '', '══════════════════════════════════════', '四、筛选区', '══════════════════════════════════════', '4.1 默认展示 4 列,点击「展开」显示全部 16 项;「收起」恢复默认', '4.2 筛选项(多条件且关系,点击「搜索」生效):', ' · 合同编号、项目名称、客户名称:可搜索下拉', ' · 交车区域:省-市二级 Cascader', ' · 完成交车时间:日期段 RangePicker,精确至天', ' · 交车人、车牌号、车辆识别代码、车辆类型、品牌、型号', ' · 业务部门、业务负责人、任务来源、业务类型、是否延期', '4.3 「重置」:清空筛选至默认', '4.4 列表左上角「车牌号」:独立于筛选区,变更即生效(快捷筛选)', '', '══════════════════════════════════════', '五、列表(一车一行)', '══════════════════════════════════════', '5.1 数据粒度:一个交车任务含多车时,按车辆拆分为独立行展示', '5.1.1 区域权限过滤(列表数据范围):按当前登录运维人员区域权限过滤,无权限的交车任务不在列表展示(见十二、区域权限与车牌选择)', '5.2 列顺序与含义:', ' (1) 车辆信息:三行 — 车牌号(待选时橙色「车牌待选」,可点击)、品牌-型号、车辆识别代码(待选时不展示 VIN,显示 -);点击车牌号打开「查看交车单」抽屉', ' (2) 合同信息:客户名称+业务类型 Tag、合同编号(链接)、项目名称(链接);点击合同编号或项目名称跳转「合同管理-该合同详情页」', ' (3) 业务负责人:业务部门、业务负责人(两行)', ' (4) 任务来源:实心 Tag;「替换车」悬停 Popover 展示 旧车 → 新车', ' (5) 交车地点:第一行交车区域(省-市);第二行停车场(车牌待选时显示 -)', ' (6) 交车状态:实心 Tag(未开始/已保存/待客户签章/客户已签章等);状态为「客户已签章」时可点击下载签章文件(指针+下划线,悬停提示「点击下载签章文件」)', ' (7) 完成交车时间:单行展示', ' (8) 交车人', ' (9) 是否归还(仅车辆已交车后展示归还状态,未交车显示 -):', ' · 未归还:车辆已交车(待客户签章/客户已签章),尚未还车', ' · 已归还:车辆已交车且已还车(仅客户已签章后可标记;悬停展示还车时间、还车人)', ' · 未开始/已保存等未交车状态:显示 -', ' (10) 交车记录:三行合并展示 — 交车里程(km)、交车氢量(%或MPa)、交车电量(%)', ' (11) 交车任务创建时间:单行', ' (12) 交车任务创建人', ' (13) 操作:「查看」始终展示;「编辑」仅交车状态为「未开始」「已保存」时展示', '5.3 合同跳转:写入 sessionStorage 合同编号/交车任务 ID,跳转合同管理-该合同详情页(原型以 message 提示)', '5.4 合同信息列宽可拖拽调整', '5.5 分页:默认 10 条/页,可选 10/20/50', '', '══════════════════════════════════════', '六、导出', '══════════════════════════════════════', '6.1 导出范围:当前 KPI 标签 + 全部筛选条件(含列表左上角车牌号)下的全部命中行,不受分页限制', '6.2 导出列:与业务导出样表一致,交车里程/氢量/电量为分列(非列表「交车记录」合并列)', '6.3 列表区展示当前 KPI 标签名称及导出说明', '', '══════════════════════════════════════', '七、编辑交车单(抽屉)', '══════════════════════════════════════', '7.1 入口:列表操作列「编辑」(仅未开始/已保存)', '7.2 抽屉标题:编辑交车单', '7.3 顶部摘要卡:客户名称 Tag、合同编号、交车状态;下方展示项目、交车地点(省-市)、品牌型号、任务来源(标签固定宽度、内容左对齐,项目与品牌型号等内容列对齐);交车状态为「客户已签章」时可点击下载签章文件', '7.4 左侧固定目录锚点(点击平滑滚动,不遮挡内容):', ' · 交车车辆 · 车辆信息 · 交车数据 · 交车检查单 · 交车照片', '7.5 底部操作:取消、保存、提交', '', '--- 7.6 交车车辆 ---', ' · 车牌号(必填):可搜索选择停车场内「已备车」车辆;支持清空(详见十二、区域权限与车牌选择)', ' · 车牌待选时:车辆识别代码显示 -,不展示 VIN', ' · 车辆类型、品牌、型号、车辆识别代码:只读,随车牌联动', '', '--- 7.7 车辆信息 ---', '【备车同步规则 — 后台逻辑,页面不单独提示】', ' · 打开编辑抽屉或选择车牌后,系统自动从该车辆对应备车记录同步以下字段至交车单:', ' 车身广告开关及广告/放大字照片、尾板、备胎照片、备胎胎纹深度,以及车辆类型/品牌/型号/VIN 等基础信息', ' · 不同步字段:驾驶培训相关(提车码识别状态、司机正面照、司机证照等),须在交车环节单独上传识别', ' · 手动修改车身广告开关时,与备车「后装设备-车身广告」双向同步(安装时间以备车记录提交成功为准)', '', ' · 车身广告及放大字(必填开关):有广告时展示广告照片、放大字照片上传(各 1 张,必填)', ' · 尾板(必填开关):与车身广告同一行左右排列', ' · 备胎照片、备胎胎纹深度:非必填;上传备胎照片可 OCR 识别胎纹深度并反写', ' · 照片上传格式说明:编辑页同一条文案,在「车辆信息」「交车照片」卡片顶部展示,不在各照片位下方重复', ' · 驾驶培训(必填):', ' - 上传司机提车码;识别成功后显示「已完成视频培训」,并自动加载提车码绑定的司机正面照、身份证(正/反)、驾驶证、从业资格证', ' - 识别失败提示:「提车码无效,请重新选择」', ' - 支持「重新选择提车码」', '', '--- 7.8 交车数据 ---', ' · 交车里程(必填,km)、交车电量(必填,%)、交车氢量(必填,单位随车辆为 % 或 MPa)', ' · 送车服务费(选填,元)', '', '--- 7.9 交车检查单 ---', ' · 独立卡片内嵌表格,非抽屉', ' · 列:类别(同类合并)、检查项目、检查情况(开关或轮胎胎纹深度 mm)、备注', ' · 清单覆盖:车灯、仪表盘、驾驶室、轮胎、液位、外观、随车工具/证件、燃料电池、冷机、制动等', '', '--- 7.10 交车照片 ---', ' · 模块标题「车辆」「底盘」「轮胎」「瑕疵」「其他」独立展示(蓝色竖线 + 标题行),下方为对应照片上传/预览区', ' · 车辆(必填 6 点):仪表盘、车辆正前、车辆左前/左后、车辆右前/右后方', ' · 底盘(必填 6 点):正前/正后方位底部,左/右侧前/后方底部', ' · 轮胎(必填 6 点):左前、右前、左后内/外、右后内/外', ' · 瑕疵、其他:各独立上传区,不限制张数', ' · 编辑页照片上传说明全页仅展示一条文案,在「车辆信息」「交车照片」卡片顶部各显示一次,各上传位下方不再重复格式提示', ' · 单点照片:jpg/jpeg/png/gif/webp,不超过 5MB;支持预览、删除', '', '--- 7.11 保存与提交 ---', ' · 保存:交车状态 →「已保存」,回写车牌及交车数据等,抽屉不关闭', ' · 提交:校验必填项 → 二次确认 → 交车状态 →「待客户签章」,写入完成交车时间/交车人,关闭抽屉', ' · 提交校验含:车牌、广告(若开启)、提车码、交车里程/电量/氢量、车辆/底盘/轮胎全部必传照片点', '', '--- 7.12 区域权限与车牌选择(特别说明,后台逻辑) ---', ' · 详见「十二、区域权限与车牌选择(特别说明)」', '', '══════════════════════════════════════', '八、查看交车单(抽屉)', '══════════════════════════════════════', '8.1 入口:列表车辆信息列点击车牌号;或操作列「查看」(所有状态均可用)', '8.2 抽屉标题:查看交车单', '8.3 布局与编辑页一致:摘要卡 + 左侧目录 + 交车车辆/车辆信息/交车数据/交车检查单/交车照片各独立卡片;状态为「客户已签章」时额外展示「E签宝签章文件」卡片', '8.4 只读规则:全部表单、开关、检查单编辑禁用;照片区不展示上传按钮及格式/操作说明,直接展示照片缩略图(可点击预览),无照片显示 -', '8.4.1 查看页可读性:禁用 Input/Select 背景色 #fafafa(更淡),文字纯黑 #000,增强对比度;含带后缀的交车里程/电量等输入框', '8.5 底部仅「关闭」按钮,无保存/提交', '8.6 已提交交车单(非未开始/已保存)查看时,Mock 加载完整交车照片样例(车辆/底盘/轮胎 18 点位 + 瑕疵/其他);点击缩略图放大预览,支持「上一张/下一张」按交车点位顺序切换;驾驶培训区仅展示状态与证照图片', '8.7 摘要卡交车状态为「客户已签章」时,点击状态 Tag 可下载签章文件', '8.8 E签宝签章文件(仅查看页且状态为「客户已签章」时展示):', ' · 左侧目录增加「E签宝签章」锚点,卡片标题「E签宝签章文件」', ' · 展示签章 PDF 文件名、签章时间、签章方(客户名称)', ' · 预览:新开浏览器页签打开签章文件预览(原型 Mock HTML 预览页,联调后对接 E 签宝 PDF/预览地址)', ' · 下载:下载签章文件(文件名含交车单 ID、车辆序号、车牌号)', '', '══════════════════════════════════════', '九、交车状态流转', '══════════════════════════════════════', '未开始 → 已保存(保存)→ 待客户签章(提交)→ 客户已签章(客户 E 签宝完成,列表归入「已完成」)', '仅「未开始」「已保存」可编辑;任意状态均可查看', '', '══════════════════════════════════════', '十、指标单位约定', '══════════════════════════════════════', ' · 交车里程:km', ' · 交车电量:%', ' · 交车氢量:% 或 MPa(按车辆/合同配置)', ' · 列表「交车记录」合并展示;导出仍为三列', '', '══════════════════════════════════════', '十一、非功能说明(原型)', '══════════════════════════════════════', ' · 备车车辆、提车码识别、OCR、E 签宝、合同详情跳转、签章文件下载等为前端 Mock,联调后对接真实接口', ' · 合同详情:sessionStorage 写入 oneos_contract_code / oneos_delivery_order_id', ' · 签章文件:列表/摘要卡 Tag 点击下载;查看页「E签宝签章文件」卡片支持预览(新开页)与下载', ' · 区域权限:列表与车牌下拉按 window.DV_MOCK_OPERATOR_REGION_PERMISSIONS 过滤(默认浙江省-嘉兴市)', ' · 提车码 Mock:文件名含 invalid/无效 模拟识别失败', '', '══════════════════════════════════════', '十二、区域权限与车牌选择(特别说明)', '══════════════════════════════════════', '适用于编辑交车单及列表数据可见范围,为后台权限逻辑,页面不单独弹窗提示。', '', '12.1 交车任务可见范围(列表/执行权限)', ' · 以交车单「交车区域」(省-市)为任务所属区域', ' · 当前运维人员须具备该区域或其上级区域的运维权限,方可查看并执行该交车任务', ' · 示例:交车区域为「浙江省-嘉兴市」时,区域权限为「浙江省」(省级)或「嘉兴市」(市级)的运维人员均可执行;', ' 仅具备「四川省」「浙江省-杭州市」等不重合权限的人员,列表中看不到该条数据', ' · 无权限:整条交车任务(含其下所有车辆行)均不可见,不可编辑', '', '12.2 停车场车牌号可选范围', ' · 编辑交车单选择车牌时,下拉仅展示车辆状态为「已备车」的车辆', ' · 非「已备车」状态(如备车中、待备车等)不可选', '', '12.3 当前运维操作人与停车场区域权限', ' · 在「已备车」前提下,还需满足:车辆所在停车场区域 ⊆ 当前运维人员区域权限覆盖范围', ' · 省级权限示例:区域权限为「浙江省」→ 可选浙江省内各停车场(杭州、嘉兴、宁波等)的全部已备车车辆', ' · 市级权限示例:区域权限为「嘉兴市」→ 仅可选嘉兴市各停车场内的已备车车辆,不可选杭州市等省内其他城市停车场车辆', ' · 下拉展示建议:车牌号 + 停车场名称,便于运维确认来源', '', '12.4 权限匹配规则摘要', ' · 省级权限:匹配该省全部省-市交车区域/停车场区域', ' · 市级权限:仅匹配该市交车区域/停车场区域', ' · 省-市组合权限(如「浙江省-嘉兴市」):等同市级嘉兴市范围', '', '12.5 原型 Mock', ' · 默认当前运维人员区域权限:浙江省-嘉兴市', ' · 可在控制台设置 window.DV_MOCK_OPERATOR_REGION_PERMISSIONS = [\'浙江省\'] 验证全省可见/可选', '' ].join('\n'); } const Component = function () { var useState = React.useState; var useCallback = React.useCallback; var useMemo = React.useMemo; var useEffect = React.useEffect; var antd = window.antd; var Breadcrumb = antd.Breadcrumb; var Card = antd.Card; var DatePicker = antd.DatePicker; var Select = antd.Select; var Button = antd.Button; var Table = antd.Table; var Cascader = antd.Cascader; var Tag = antd.Tag; var Tooltip = antd.Tooltip; var Popover = antd.Popover; var Modal = antd.Modal; var message = antd.message; var RangePicker = DatePicker.RangePicker; function createEmptyFilters() { return { contractCode: undefined, projectName: undefined, customerName: undefined, deliveryRegion: undefined, dateStart: '', dateEnd: '', deliveryPerson: undefined, plateNo: undefined, vin: undefined, vehicleType: undefined, brand: undefined, model: undefined, businessDept: undefined, businessOwner: undefined, taskSource: undefined, bizType: undefined, isDelayed: undefined }; } function patchFilters(prev, patch) { var next = {}; var k; for (k in prev) next[k] = prev[k]; for (k in patch) next[k] = patch[k]; return next; } var filterState = useState(createEmptyFilters); var filters = filterState[0]; var setFilters = filterState[1]; var appliedFilterState = useState(createEmptyFilters); var appliedFilters = appliedFilterState[0]; var setAppliedFilters = appliedFilterState[1]; var filterExpandedState = useState(false); var filterExpanded = filterExpandedState[0]; var setFilterExpanded = filterExpandedState[1]; /** total | inProgress | completed */ var kpiFilterState = useState('inProgress'); var kpiFilter = kpiFilterState[0]; var setKpiFilter = kpiFilterState[1]; var pageState = useState(1); var pageSizeState = useState(10); var colWidthsState = useState({ contractInfo: 240 }); var colWidths = colWidthsState[0]; var setColWidths = colWidthsState[1]; var requirementModalOpen = useState(false); var setRequirementModalOpen = requirementModalOpen[1]; var requirementDocContent = getDeliveryManageRequirementDoc(); // 交车区域:省-市 二级 var regionOptions = [ { value: 'zhejiang', label: '浙江省', children: [{ value: 'hangzhou', label: '杭州市' }, { value: 'jiaxing', label: '嘉兴市' }, { value: 'ningbo', label: '宁波市' }] }, { value: 'shanghai', label: '上海市', children: [{ value: 'shanghai', label: '上海市' }] }, { value: 'guangdong', label: '广东省', children: [{ value: 'guangzhou', label: '广州市' }, { value: 'shenzhen', label: '深圳市' }] } ]; var contractCodeOptions = [ { value: 'LNZLHT', label: 'LNZLHT' }, { value: 'HT-ZL-2024', label: 'HT-ZL-2024' }, { value: 'HT-ZL-2025', label: 'HT-ZL-2025' } ]; var projectNameOptions = [ { value: 'p1', label: '桐乡韵达租赁4.5T*10' }, { value: 'p2', label: '洛安供应链-租赁帕力安4.5T*30' }, { value: 'p3', label: '嘉兴氢能示范项目' }, { value: 'p4', label: '杭州城配租赁项目' } ]; var customerNameOptions = [ { value: 'c1', label: '桐乡市丰韵快递有限责任公司' }, { value: 'c2', label: '武汉洛安供应链有限公司' }, { value: 'c3', label: '嘉兴某某物流有限公司' }, { value: 'c4', label: '杭州某某租赁有限公司' } ]; var deliveryPersonOptions = [ { value: '张三', label: '张三' }, { value: '李四', label: '李四' }, { value: '王五', label: '王五' }, { value: '魏山', label: '魏山' }, { value: '何苗苗', label: '何苗苗' } ]; var taskSourceOptions = [ { value: '替换车', label: '替换车' }, { value: '交车任务', label: '交车任务' } ]; var bizTypeOptions = [ { value: '租赁', label: '租赁' }, { value: '自营', label: '自营' } ]; var isDelayedOptions = [ { value: 'yes', label: '是' }, { value: 'no', label: '否' } ]; var DV_ORDER_KEY = 'oneos_delivery_order_id'; var DV_VEHICLE_KEY = 'oneos_delivery_vehicle_key'; var DV_NAV_KEY = 'oneos_delivery_navigate_target'; var DV_CONTRACT_CODE_KEY = 'oneos_contract_code'; var DV_LIST_LINK_STYLE = { color: '#165dff', cursor: 'pointer' }; /** 交车状态:进行中=未开始/已保存/待客户签章;历史=客户已签章(运维+客户 E 签宝均完成) */ var DELIVERY_STATUS_HISTORY = '客户已签章'; var DELIVERY_STATUS_IN_PROGRESS = ['未开始', '已保存', '待客户签章']; function isDeliveryHistoryStatus(status) { return status === DELIVERY_STATUS_HISTORY || status === '已签章'; } function isDeliveryInProgressStatus(status) { return DELIVERY_STATUS_IN_PROGRESS.indexOf(status || '未开始') >= 0; } /** 运维侧已完成交车提交(待签章或已签章)视为车辆已交车 */ function isVehicleDelivered(status) { return status === '待客户签章' || isDeliveryHistoryStatus(status); } function canEditDeliveryRow(record) { var s = record.deliveryStatus; return s === '未开始' || s === '已保存'; } // 交车任务(一单多车);列表按车辆拆分为独立行,表头对齐导出样表 var deliveryOrdersState = useState([ { id: 'o1', expectedDate: '2025-02-28 至 2025-03-05', contractCode: 'LNZLHT 20260104001', projectName: '桐乡韵达租赁4.5T*10', customerName: '桐乡市丰韵快递有限责任公司', businessDept: '业务二部', businessOwner: '刘念念', taskSource: '替换车', replaceOldPlate: '浙A88601F', bizType: '租赁', deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '平湖指定停车场', createTime: '2026-06-04 11:28', createBy: '赵小峰', vehicleList: [ { vehicleKey: 1, seq: 1, vehicleType: '4.5吨冷链车', brand: '现代', model: '帕力安牌4.5吨冷链车', vin: 'LNBSCPKB8RR123401', replaceOldPlate: '浙A88601F', plateNo: '', deliveryTime: '', deliveryPerson: '', deliveryStatus: '未开始', deliveryMileage: null, deliveryH2: null, deliveryElec: null }, { vehicleKey: 2, seq: 2, vehicleType: '4.5吨冷链车', brand: '现代', model: '帕力安牌4.5吨冷链车', vin: 'LNBSCPKB8RR123402', replaceOldPlate: '浙A88602F', plateNo: '', deliveryTime: '', deliveryPerson: '', deliveryStatus: '未开始', deliveryMileage: null, deliveryH2: null, deliveryElec: null } ] }, { id: 'o2', expectedDate: '2025-03-01', contractCode: 'LNZLHT2026040301-042', projectName: '洛安供应链-租赁帕力安4.5T*30', customerName: '武汉洛安供应链有限公司', businessDept: '业务三部', businessOwner: '金可鹏', taskSource: '交车任务', bizType: '租赁', deliveryRegion: '四川省-成都市', deliveryAddress: '成都龙泉驿停车场', createTime: '2026-05-31 14:07', createBy: '何苗苗', vehicleList: [ { vehicleKey: 1, seq: 1, vehicleType: '4.5吨冷链车', brand: '现代', model: '帕力安牌4.5吨冷链车', vin: 'LNBSCPKB9RR223401', plateNo: '粤AGP9827', deliveryTime: '2026-06-03 18:20', deliveryPerson: '魏山', deliveryStatus: '待客户签章', deliveryMileage: 48202, deliveryH2: 19, deliveryH2Unit: '%', deliveryElec: 82 }, { vehicleKey: 2, seq: 2, vehicleType: '4.5吨冷链车', brand: '现代', model: '帕力安牌4.5吨冷链车', vin: 'LNBSCPKB9RR223402', plateNo: '粤AGP4598', deliveryTime: '2026-06-02 11:00', deliveryPerson: '魏山', deliveryStatus: '已保存', deliveryMileage: null, deliveryH2: null, deliveryElec: null } ] }, { id: 'o3', expectedDate: '2025-03-08', contractCode: 'LNZLHT2025042201', projectName: '炽瑞-租赁现代4.5T', customerName: '东莞沙田炽瑞物流有限公司', businessDept: '业务三部', businessOwner: '金可鹏', taskSource: '替换车', replaceOldPlate: '粤B58888F', bizType: '租赁', deliveryRegion: '广东省-广州市', deliveryAddress: '广州南沙物流园停车场', createTime: '2026-05-28 20:30', createBy: '童军林', vehicleList: [ { vehicleKey: 1, seq: 1, vehicleType: '4.5吨货车', brand: '现代', model: '4.5吨货车', vin: 'LNBSCPKB7RR323401', replaceOldPlate: '粤B58888F', plateNo: '', deliveryTime: '', deliveryPerson: '', deliveryStatus: '未开始', deliveryMileage: null, deliveryH2: null, deliveryElec: null } ] }, { id: 'o4', expectedDate: '2024-11-15', contractCode: 'LNZLHT2024111401', projectName: '聚德11月新增苏龙18T*2', customerName: '沈阳聚德物流有限公司', businessDept: '业务三部', businessOwner: '金可鹏', taskSource: '交车任务', bizType: '租赁', deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '嘉兴港区氢能停车场', createTime: '2024-11-15 15:05', createBy: '何苗苗', vehicleList: [ { vehicleKey: 1, seq: 1, vehicleType: '18吨双飞翼货车', brand: '苏龙', model: '海格牌18吨双飞翼货车', vin: 'LKLG7C4E4NA774701', plateNo: '浙F80088', deliveryTime: '2026-06-02 16:00', deliveryPerson: '魏山', deliveryStatus: '待客户签章', deliveryMileage: 46200, deliveryH2: 21, deliveryH2Unit: '%', deliveryElec: 80 }, { vehicleKey: 2, seq: 2, vehicleType: '18吨双飞翼货车', brand: '苏龙', model: '海格牌18吨双飞翼货车', vin: 'LKLG7C4E4NA774702', plateNo: '沪A03802F', deliveryTime: '2025-11-20 09:30', deliveryPerson: '何苗苗', deliveryStatus: '已保存', deliveryMileage: null, deliveryH2: null, deliveryElec: null } ] }, { id: 'o5', expectedDate: '2025-02-15', contractCode: 'HT-ZL-2024-001', projectName: '嘉兴氢能示范项目', customerName: '嘉兴某某物流有限公司', businessDept: '业务一部', businessOwner: '张经理', taskSource: '交车任务', bizType: '租赁', deliveryRegion: '浙江省-嘉兴市', deliveryAddress: '南湖科技大道停车场', createTime: '2025-02-10 09:00', createBy: '系统', vehicleList: [ { vehicleKey: 1, seq: 1, vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', vin: 'LKLG7C4E4NA774759', plateNo: '京A12345', deliveryTime: '2025-02-15 10:30', deliveryPerson: '张三', deliveryStatus: '客户已签章', deliveryMileage: 12580, deliveryH2: 35, deliveryH2Unit: 'MPa', deliveryElec: 45, vehicleReturned: true, returnTime: '2025-08-20 11:30', returnPerson: '王五' }, { vehicleKey: 2, seq: 2, vehicleType: '厢式货车', brand: '福田', model: 'BJ1180', vin: 'LKLG7C4E4NA774760', plateNo: '京C11111', deliveryTime: '2025-02-15 14:00', deliveryPerson: '李四', deliveryStatus: '客户已签章', deliveryMileage: 13200, deliveryH2: 68, deliveryH2Unit: '%', deliveryElec: 38, vehicleReturned: false } ] }, { id: 'o6', expectedDate: '2025-02-18', contractCode: 'HT-ZL-2024-003', projectName: '杭州城配租赁项目', customerName: '杭州某某租赁有限公司', businessDept: '业务二部', businessOwner: '李经理', taskSource: '交车任务', bizType: '租赁', deliveryRegion: '浙江省-杭州市', deliveryAddress: '未来科技城地下停车场', createTime: '2025-02-12 08:30', createBy: '李四', vehicleList: [ { vehicleKey: 1, seq: 1, vehicleType: '城配货车', brand: '重汽', model: 'ZZ1180', vin: 'LKLG7C4E4NA774801', plateNo: '浙A10001', deliveryTime: '2025-02-18 09:15', deliveryPerson: '张三', deliveryStatus: '客户已签章', deliveryMileage: 9800, deliveryH2: 10.8, deliveryH2Unit: '%', deliveryElec: 52, vehicleReturned: true, returnTime: '2026-05-12 09:20', returnPerson: '张三' }, { vehicleKey: 2, seq: 2, vehicleType: '城配货车', brand: '东风', model: 'DFH1190', vin: 'LKLG7C4E4NA774802', plateNo: '浙A10002', deliveryTime: '2025-02-18 11:40', deliveryPerson: '李四', deliveryStatus: '客户已签章', deliveryMileage: 10120, deliveryH2: 9.6, deliveryH2Unit: '%', deliveryElec: 48, vehicleReturned: false }, { vehicleKey: 3, seq: 3, vehicleType: '城配货车', brand: '福田', model: 'BJ1190', vin: 'LKLG7C4E4NA774803', plateNo: '浙A10003', deliveryTime: '2025-02-18 16:20', deliveryPerson: '王五', deliveryStatus: '客户已签章', deliveryMileage: 11500, deliveryH2: 32, deliveryH2Unit: 'MPa', deliveryElec: 55, vehicleReturned: false } ] } ]); var deliveryOrders = deliveryOrdersState[0]; var setDeliveryOrders = deliveryOrdersState[1]; var editDrawerState = useState({ open: false, record: null, mode: 'edit' }); var editDrawer = editDrawerState[0]; var setEditDrawer = editDrawerState[1]; function resolveVehicleReturnedFields(deliveryStatus, v) { if (!isVehicleDelivered(deliveryStatus || '未开始')) { return { vehicleReturned: false, returnTime: '', returnPerson: '' }; } if (!isDeliveryHistoryStatus(deliveryStatus || '未开始')) { return { vehicleReturned: false, returnTime: '', returnPerson: '' }; } return { vehicleReturned: v.vehicleReturned === true || !!(v.returnTime && String(v.returnTime).trim()), returnTime: v.returnTime || '', returnPerson: (v.returnPerson && v.returnPerson !== '-') ? String(v.returnPerson).trim() : '' }; } function expandOrdersToVehicleRows(orders) { var rows = []; (orders || []).forEach(function (order) { (order.vehicleList || []).forEach(function (v, idx) { var vk = v.vehicleKey != null ? v.vehicleKey : idx + 1; var deliveryStatus = v.deliveryStatus || '未开始'; var returnFields = resolveVehicleReturnedFields(deliveryStatus, v); rows.push({ id: order.id + '_v' + vk, orderId: order.id, vehicleKey: vk, seq: v.seq != null ? v.seq : idx + 1, deliveryTime: v.deliveryTime || v.actualDate || '', deliveryPerson: (v.deliveryPerson && v.deliveryPerson !== '-') ? v.deliveryPerson : '', plateNo: (v.plateNo && v.plateNo !== '-') ? String(v.plateNo).trim() : '', vin: (v.plateNo && v.plateNo !== '-' && String(v.plateNo).trim()) ? (v.vin || '') : '', vehicleType: v.vehicleType || '', brand: v.brand || '', model: v.model || '', contractCode: order.contractCode, customerName: order.customerName, projectName: order.projectName, businessDept: order.businessDept || '-', businessOwner: order.businessOwner || '-', taskSource: order.taskSource || '-', replaceOldPlate: (v.replaceOldPlate || order.replaceOldPlate || '').trim(), bizType: order.bizType || '-', deliveryRegion: order.deliveryRegion || '-', deliveryAddress: order.deliveryAddress || '-', deliveryStatus: deliveryStatus, deliveryMileage: v.deliveryMileage, deliveryH2: v.deliveryH2, deliveryH2Unit: v.deliveryH2Unit === 'MPa' ? 'MPa' : '%', deliveryElec: v.deliveryElec, createTime: order.createTime, createBy: order.createBy, expectedDate: order.expectedDate, vehicleReturned: returnFields.vehicleReturned, returnTime: returnFields.returnTime, returnPerson: returnFields.returnPerson }); }); }); return rows; } function formatDeliveryMileage(v) { if (v === null || v === undefined || v === '') return '-'; return String(v) + ' km'; } function formatDeliveryH2(v, unit) { if (v === null || v === undefined || v === '') return '-'; var u = unit === 'MPa' ? 'MPa' : '%'; return String(v) + ' ' + u; } function formatDeliveryElec(v) { if (v === null || v === undefined || v === '') return '-'; return String(v) + ' %'; } function renderDeliveryRecordCell(record) { return React.createElement('div', null, React.createElement('div', { style: cellLineSubStyle }, '里程 ', formatDeliveryMileage(record.deliveryMileage)), React.createElement('div', { style: cellLineSubStyle }, '氢量 ', formatDeliveryH2(record.deliveryH2, record.deliveryH2Unit)), React.createElement('div', { style: cellLineSubStyle }, '电量 ', formatDeliveryElec(record.deliveryElec)) ); } function rowDateKey(row) { var t = row.deliveryTime || ''; if (t && t.length >= 10) return t.slice(0, 10); return (row.createTime || '').slice(0, 10); } function parseExpectedEndDate(expectedDate) { if (!expectedDate) return ''; var s = String(expectedDate).trim(); if (s.indexOf('至') >= 0) { var seg = s.split('至'); return (seg[seg.length - 1] || '').trim().slice(0, 10); } return s.slice(0, 10); } function computeRowDelayed(row) { if (isDeliveryHistoryStatus(row.deliveryStatus)) return false; var end = parseExpectedEndDate(row.expectedDate); if (!end) return false; return end < '2026-06-01'; } function filterSelectOption(input, option) { var label = (option && (option.label || option.children)) || ''; return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0; } function filterVehicleRows(rows) { var list = rows.slice(); var f = appliedFilters; if (f.contractCode) { var cc = (contractCodeOptions.find(function (o) { return o.value === f.contractCode; }) || {}).label || f.contractCode; list = list.filter(function (r) { return (r.contractCode || '').indexOf(cc) !== -1; }); } if (f.projectName) { var projLabel = (projectNameOptions.find(function (o) { return o.value === f.projectName; }) || {}).label; if (projLabel) list = list.filter(function (r) { return r.projectName === projLabel; }); } if (f.customerName) { var custLabel = (customerNameOptions.find(function (o) { return o.value === f.customerName; }) || {}).label; if (custLabel) list = list.filter(function (r) { return r.customerName === custLabel; }); } if (f.deliveryRegion) list = list.filter(function (r) { return (r.deliveryRegion || '').indexOf(f.deliveryRegion) !== -1; }); if (f.dateStart) list = list.filter(function (r) { return rowDateKey(r) >= f.dateStart; }); if (f.dateEnd) list = list.filter(function (r) { return rowDateKey(r) <= f.dateEnd; }); if (f.deliveryPerson) list = list.filter(function (r) { return (r.deliveryPerson || '').indexOf(f.deliveryPerson) !== -1; }); if (f.plateNo) list = list.filter(function (r) { return !isPlatePending(r.plateNo) && String(r.plateNo).indexOf(f.plateNo) !== -1; }); if (f.vin) list = list.filter(function (r) { return (r.vin || '').indexOf(f.vin) !== -1; }); if (f.vehicleType) list = list.filter(function (r) { return r.vehicleType === f.vehicleType; }); if (f.brand) list = list.filter(function (r) { return r.brand === f.brand; }); if (f.model) list = list.filter(function (r) { return r.model === f.model; }); if (f.businessDept) list = list.filter(function (r) { return (r.businessDept || '').indexOf(f.businessDept) !== -1; }); if (f.businessOwner) list = list.filter(function (r) { return (r.businessOwner || '').indexOf(f.businessOwner) !== -1; }); if (f.taskSource) list = list.filter(function (r) { return r.taskSource === f.taskSource; }); if (f.bizType) list = list.filter(function (r) { return r.bizType === f.bizType; }); if (f.isDelayed === 'yes') list = list.filter(function (r) { return computeRowDelayed(r); }); if (f.isDelayed === 'no') list = list.filter(function (r) { return !computeRowDelayed(r); }); return list; } var allVehicleRows = useMemo(function () { return expandOrdersToVehicleRows(deliveryOrders); }, [deliveryOrders]); var operatorVisibleRows = useMemo(function () { return filterRowsByOperatorRegion(allVehicleRows); }, [allVehicleRows]); function buildSelectOptions(values) { var seen = {}; var opts = []; (values || []).forEach(function (v) { var s = v == null ? '' : String(v).trim(); if (!s || seen[s]) return; seen[s] = true; opts.push({ value: s, label: s }); }); opts.sort(function (a, b) { return a.label.localeCompare(b.label, 'zh-CN'); }); return opts; } var dynamicFilterOptions = useMemo(function () { var plates = []; var vins = []; var vehicleTypes = []; var brands = []; var models = []; var depts = []; var owners = []; operatorVisibleRows.forEach(function (r) { if (!isPlatePending(r.plateNo)) plates.push(r.plateNo); if (r.vin) vins.push(r.vin); if (r.vehicleType) vehicleTypes.push(r.vehicleType); if (r.brand) brands.push(r.brand); if (r.model) models.push(r.model); if (r.businessDept && r.businessDept !== '-') depts.push(r.businessDept); if (r.businessOwner && r.businessOwner !== '-') owners.push(r.businessOwner); }); return { plateNoOptions: buildSelectOptions(plates), vinOptions: buildSelectOptions(vins), vehicleTypeOptions: buildSelectOptions(vehicleTypes), brandOptions: buildSelectOptions(brands), modelOptions: buildSelectOptions(models), businessDeptOptions: buildSelectOptions(depts), businessOwnerOptions: buildSelectOptions(owners) }; }, [operatorVisibleRows]); var filteredBySearch = useMemo(function () { return filterVehicleRows(operatorVisibleRows); }, [operatorVisibleRows, appliedFilters]); var kpiStats = useMemo(function () { var inProgress = 0; var completed = 0; filteredBySearch.forEach(function (r) { if (isDeliveryHistoryStatus(r.deliveryStatus)) completed++; else if (isDeliveryInProgressStatus(r.deliveryStatus)) inProgress++; }); return { total: filteredBySearch.length, inProgress: inProgress, completed: completed }; }, [filteredBySearch]); function matchKpiFilter(row, filterKey) { if (filterKey === 'total') return true; if (filterKey === 'inProgress') return isDeliveryInProgressStatus(row.deliveryStatus); if (filterKey === 'completed') return isDeliveryHistoryStatus(row.deliveryStatus); return true; } var filteredList = useMemo(function () { return filteredBySearch.filter(function (r) { return matchKpiFilter(r, kpiFilter); }); }, [filteredBySearch, kpiFilter]); function patchVehicleInOrder(orderId, vehicleKey, patch) { setDeliveryOrders(function (orders) { return orders.map(function (order) { if (order.id !== orderId) return order; return Object.assign({}, order, { vehicleList: (order.vehicleList || []).map(function (v) { var vk = v.vehicleKey != null ? v.vehicleKey : v.seq; if (vk !== vehicleKey) return v; return Object.assign({}, v, patch); }) }); }); }); } var openEditDrawer = useCallback(function (record) { setEditDrawer({ open: true, record: record, mode: 'edit' }); }, []); var openViewDrawer = useCallback(function (record) { setEditDrawer({ open: true, record: record, mode: 'view' }); }, []); var closeEditDrawer = useCallback(function () { setEditDrawer({ open: false, record: null, mode: 'edit' }); }, []); var handleEditSave = useCallback(function (patch) { var rec = editDrawer.record; if (!rec) return; patchVehicleInOrder(rec.orderId, rec.vehicleKey, Object.assign({}, patch, { deliveryStatus: '已保存' })); }, [editDrawer.record]); var handleEditSubmit = useCallback(function (patch) { var rec = editDrawer.record; if (!rec) return; patchVehicleInOrder(rec.orderId, rec.vehicleKey, Object.assign({}, patch, { deliveryStatus: '待客户签章', deliveryTime: patch.deliveryTime || '2026-06-04 10:00', deliveryPerson: patch.deliveryPerson || '魏山' })); closeEditDrawer(); }, [editDrawer.record, closeEditDrawer]); var navigateToContractDetail = useCallback(function (record) { try { sessionStorage.setItem(DV_CONTRACT_CODE_KEY, record.contractCode || ''); sessionStorage.setItem(DV_ORDER_KEY, record.orderId || ''); } catch (e) {} message.info('跳转 合同管理-该合同详情页(合同编号 ' + (record.contractCode || '-') + ')'); }, []); var navigateDelivery = useCallback(function (record, target) { try { sessionStorage.setItem(DV_ORDER_KEY, record.orderId || ''); sessionStorage.setItem(DV_VEHICLE_KEY, String(record.vehicleKey != null ? record.vehicleKey : '')); sessionStorage.setItem(DV_NAV_KEY, target || 'order'); } catch (e) {} var pageMap = { order: '交车管理-交车单', edit: '交车管理-交车单-编辑', view: '交车管理-交车单-查看' }; message.info('跳转 ' + (pageMap[target] || target) + '(交车单 ' + record.orderId + ' · 车辆 ' + record.plateNo + ')'); }, []); var page = pageState[0]; var setPage = pageState[1]; var pageSize = pageSizeState[0]; var setPageSize = pageSizeState[1]; var handleKpiCardClick = useCallback(function (key) { setKpiFilter(key); setPage(1); }, []); var totalCount = filteredList.length; var displayList = useMemo(function () { var start = (page - 1) * pageSize; return filteredList.slice(start, start + pageSize); }, [filteredList, page, pageSize]); var kpiExportLabelMap = { total: '全部交车任务', inProgress: '进行中的交车任务', completed: '已完成的交车任务' }; function normalizeRegionFilter(regionVal) { if (Array.isArray(regionVal) && regionVal.length >= 2) { var prov = regionOptions.find(function (r) { return r.value === regionVal[0]; }); var city = prov && prov.children && prov.children.find(function (c) { return c.value === regionVal[1]; }); return prov && city ? prov.label + '-' + city.label : undefined; } if (Array.isArray(regionVal) && regionVal.length === 1) { var pOnly = regionOptions.find(function (r) { return r.value === regionVal[0]; }); return pOnly ? pOnly.label : undefined; } return regionVal; } var handleQuery = useCallback(function () { var next = patchFilters(filters, { deliveryRegion: normalizeRegionFilter(filters.deliveryRegion) }); setFilters(next); setAppliedFilters(patchFilters(next, {})); setPage(1); }, [filters]); var handleReset = useCallback(function () { var empty = createEmptyFilters(); setFilters(empty); setAppliedFilters(empty); setPage(1); }, []); var handleListPlateNoChange = useCallback(function (v) { setFilters(function (f) { return patchFilters(f, { plateNo: v }); }); setAppliedFilters(function (f) { return patchFilters(f, { plateNo: v }); }); setPage(1); }, []); var getExportRowsByKpi = useCallback(function (filterKey) { return filteredBySearch.filter(function (r) { return matchKpiFilter(r, filterKey); }); }, [filteredBySearch]); var handleExport = useCallback(function () { var tabLabel = kpiExportLabelMap[kpiFilter] || '交车任务'; var rows = getExportRowsByKpi(kpiFilter); if (!rows || rows.length === 0) { message.warning('当前「' + tabLabel + '」无数据可导出'); return; } var escapeCsv = function (v) { var s = v == null ? '' : String(v); if (s.indexOf(',') !== -1 || s.indexOf('"') !== -1 || s.indexOf('\n') !== -1) return '"' + s.replace(/"/g, '""') + '"'; return s; }; var headers = ['完成交车时间', '交车人', '车牌号', '品牌', '型号', '合同编号', '客户名称', '项目名称', '业务部门', '业务负责人', '任务来源', '业务类型', '交车区域', '交车地点', '交车状态', '交车里程', '交车氢量', '交车电量', '创建时间', '创建人']; var rowToCells = function (r) { return [formatCompletedDeliveryTimeExport(r.deliveryTime), r.deliveryPerson || '-', displayPlateNo(r.plateNo), r.brand || '-', r.model || '-', r.contractCode, r.customerName, r.projectName, r.businessDept, r.businessOwner, r.taskSource, r.bizType, r.deliveryRegion, r.deliveryAddress, r.deliveryStatus, formatDeliveryMileage(r.deliveryMileage), formatDeliveryH2(r.deliveryH2, r.deliveryH2Unit), formatDeliveryElec(r.deliveryElec), r.createTime, r.createBy]; }; var csv = headers.map(escapeCsv).join(',') + '\n'; rows.forEach(function (r) { csv += rowToCells(r).map(escapeCsv).join(',') + '\n'; }); var blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = '交车管理_' + tabLabel + '_' + new Date().getTime() + '.csv'; a.click(); URL.revokeObjectURL(url); message.success('已导出「' + tabLabel + '」共 ' + rows.length + ' 条(含当前筛选条件)'); }, [kpiFilter, getExportRowsByKpi]); var dateRangeValue = useMemo(function () { if (!filters.dateStart && !filters.dateEnd) return null; try { if (typeof window !== 'undefined' && window.dayjs && filters.dateStart && filters.dateEnd) { return [window.dayjs(filters.dateStart), window.dayjs(filters.dateEnd)]; } } catch (e) {} return null; }, [filters.dateStart, filters.dateEnd]); var onDateRangeChange = useCallback(function (dates, dateStrings) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.dateStart = (dateStrings && dateStrings[0]) || ''; g.dateEnd = (dateStrings && dateStrings[1]) || ''; return g; }); }, []); // 交车区域 Cascader 的 value:用 appliedFilters 反推或存 value 数组。筛选用字符串比较,表单用 Cascader 选省-市 var deliveryRegionValue = useMemo(function () { var s = filters.deliveryRegion; if (!s || typeof s !== 'string') return undefined; var parts = s.split('-'); if (parts.length < 2) return undefined; for (var i = 0; i < regionOptions.length; i++) { var prov = regionOptions[i]; if (prov.label !== parts[0]) continue; for (var j = 0; j < (prov.children || []).length; j++) { if (prov.children[j].label === parts[1]) return [prov.value, prov.children[j].value]; } } return undefined; }, [filters.deliveryRegion]); var solidTagBaseStyle = { margin: 0, border: 'none', fontWeight: 600, color: '#fff', lineHeight: '20px', flexShrink: 0, flexGrow: 0 }; function renderSolidTag(text, bgColor) { if (!text || text === '-') return '-'; return React.createElement(Tag, { style: Object.assign({}, solidTagBaseStyle, { backgroundColor: bgColor || '#64748b' }) }, text); } function renderDeliveryStatus(status, record) { var bg = '#8c8c8c'; if (status === '客户已签章' || status === '已签章') bg = '#16a34a'; else if (status === '待客户签章') bg = '#2563eb'; else if (status === '已保存') bg = '#ea580c'; else if (status === '未开始') bg = '#64748b'; var signed = isDeliverySignedStatus(status); var style = Object.assign({}, solidTagBaseStyle, { backgroundColor: bg || '#64748b' }); if (signed) { style.cursor = 'pointer'; style.textDecoration = 'underline'; style.textUnderlineOffset = '2px'; } return React.createElement(Tag, { style: style, title: signed ? '点击下载签章文件' : undefined, role: signed ? 'button' : undefined, tabIndex: signed ? 0 : undefined, onClick: signed ? function (e) { e.stopPropagation(); downloadDeliverySignFile(record); } : undefined, onKeyDown: signed ? function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); e.stopPropagation(); downloadDeliverySignFile(record); } } : undefined }, status); } function renderBizTypeTag(bizType) { if (!bizType || bizType === '-') return null; var bg = '#64748b'; if (bizType === '租赁') bg = '#2563eb'; else if (bizType === '自营') bg = '#7c3aed'; return React.createElement(Tag, { style: Object.assign({}, solidTagBaseStyle, { backgroundColor: bg, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', textAlign: 'center', alignSelf: 'center', minWidth: 44, height: 22, padding: '0 10px', boxSizing: 'border-box' }) }, bizType); } function renderTaskSourceTag(text, bgColor) { return React.createElement(Tag, { style: Object.assign({}, solidTagBaseStyle, { backgroundColor: bgColor, cursor: 'default' }) }, text); } var handleColumnResizeStart = useCallback(function (colKey, e) { e.preventDefault(); e.stopPropagation(); var startX = e.clientX; var startW = colWidths[colKey] || 240; function onMove(ev) { var nextW = Math.max(140, Math.min(560, startW + ev.clientX - startX)); setColWidths(function (prev) { var next = {}; for (var k in prev) next[k] = prev[k]; next[colKey] = nextW; return next; }); } function onUp() { document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); } document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onUp); }, [colWidths]); function renderResizableTitle(title, colKey) { return React.createElement('div', { style: { position: 'relative', paddingRight: 10, userSelect: 'none' } }, title, React.createElement('span', { role: 'separator', 'aria-orientation': 'vertical', title: '拖动调整列宽', style: { position: 'absolute', right: 0, top: 0, bottom: 0, width: 8, cursor: 'col-resize', zIndex: 1 }, onMouseDown: function (ev) { handleColumnResizeStart(colKey, ev); } }) ); } function renderContractInfoCell(record) { var customerName = record.customerName || '-'; var showTip = customerName !== '-'; var contractCode = record.contractCode || '-'; var projectName = record.projectName || '-'; return React.createElement('div', { style: { minWidth: 0, width: '100%' } }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', flexWrap: 'nowrap', gap: 6, minWidth: 0, width: '100%', lineHeight: 1.45 } }, React.createElement(Tooltip, { title: showTip ? customerName : null, placement: 'topLeft' }, React.createElement('span', { style: { flex: '1 1 auto', minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: '#333', display: 'block' } }, customerName) ), React.createElement('span', { style: { flexShrink: 0, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' } }, renderBizTypeTag(record.bizType)) ), contractCode !== '-' ? React.createElement('div', { style: Object.assign({}, cellLineSubStyle, DV_LIST_LINK_STYLE, { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }), role: 'button', tabIndex: 0, onClick: function () { navigateToContractDetail(record); }, onKeyDown: function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); navigateToContractDetail(record); } } }, contractCode) : React.createElement('div', { style: cellLineSubStyle }, '-'), projectName !== '-' ? React.createElement('div', { style: Object.assign({}, cellLineSubStyle, DV_LIST_LINK_STYLE, { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }), role: 'button', tabIndex: 0, onClick: function () { navigateToContractDetail(record); }, onKeyDown: function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); navigateToContractDetail(record); } } }, projectName) : React.createElement('div', { style: Object.assign({}, cellLineSubStyle, { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }) }, '-') ); } function renderDeliveryPlaceCell(record) { var regionText = '交车区域:' + formatDeliveryRegion(record.deliveryRegion); var parkingText = isPlatePending(record.plateNo) ? '-' : (record.deliveryAddress || '-'); return renderCellLines(regionText, [parkingText]); } function parseCompletedDeliveryTime(raw) { if (!raw || String(raw).trim() === '' || raw === '-') return { date: '-', time: '-' }; var s = String(raw).trim(); if (s.indexOf(' ') >= 0) { var parts = s.split(/\s+/); var datePart = parts[0] || '-'; var timePart = parts[1] || '-'; if (timePart.length >= 5) timePart = timePart.slice(0, 5); return { date: datePart, time: timePart }; } return { date: s, time: '-' }; } function renderCompletedDeliveryTimeCell(record) { var parsed = parseCompletedDeliveryTime(record.deliveryTime); return renderCellLines(parsed.date, [parsed.time]); } function renderTaskCreateTimeCell(record) { var parsed = parseCompletedDeliveryTime(record.createTime); if (parsed.date === '-' && parsed.time === '-') return '-'; if (parsed.time === '-') return parsed.date; return parsed.date + ' ' + parsed.time; } function renderTaskCreateByCell(createBy) { if (createBy == null || createBy === undefined) return '-'; var name = String(createBy).trim(); return name === '' || name === '-' ? '-' : name; } function isVehicleReturned(record) { if (!record || !isVehicleDelivered(record.deliveryStatus) || !isDeliveryHistoryStatus(record.deliveryStatus)) return false; return record.vehicleReturned === true; } function renderVehicleReturnedCell(record) { if (!record || !isVehicleDelivered(record.deliveryStatus)) { return '-'; } if (!isVehicleReturned(record)) { return React.createElement('span', { style: { color: '#64748b' } }, '未归还'); } var label = React.createElement('span', { style: { color: '#16a34a', fontWeight: 600, cursor: 'default', borderBottom: '1px dashed #86efac' } }, '已归还'); return React.createElement(Popover, { content: renderReturnVehiclePopoverContent(record), trigger: 'hover', placement: 'topLeft', mouseEnterDelay: 0.15, mouseLeaveDelay: 0.1, destroyTooltipOnHide: true }, label); } function formatReturnTimeDisplay(raw) { var parsed = parseCompletedDeliveryTime(raw); if (parsed.date === '-' && parsed.time === '-') return '-'; if (parsed.time === '-') return parsed.date; return parsed.date + ' ' + parsed.time; } function renderReturnVehiclePopoverContent(record) { return React.createElement('div', { style: { minWidth: 168, fontSize: 13, lineHeight: 1.65 } }, React.createElement('div', null, React.createElement('span', { style: { color: '#64748b' } }, '还车时间:'), React.createElement('span', { style: { color: '#334155', fontWeight: 600 } }, formatReturnTimeDisplay(record.returnTime)) ), React.createElement('div', { style: { marginTop: 4 } }, React.createElement('span', { style: { color: '#64748b' } }, '还车人:'), React.createElement('span', { style: { color: '#334155', fontWeight: 600 } }, record.returnPerson || '-') ) ); } function formatCompletedDeliveryTimeExport(raw) { var parsed = parseCompletedDeliveryTime(raw); if (parsed.date === '-' && parsed.time === '-') return '-'; if (parsed.time === '-') return parsed.date; return parsed.date + ' ' + parsed.time; } var cellLineMainStyle = { lineHeight: 1.45, color: '#333', wordBreak: 'break-all' }; var cellLineSubStyle = { lineHeight: 1.4, fontSize: 12, color: '#8c8c8c', marginTop: 2, wordBreak: 'break-all' }; function renderCellLines(mainText, subLines) { var subs = subLines || []; return React.createElement('div', null, React.createElement('div', { style: cellLineMainStyle }, mainText || '-'), subs.map(function (line, i) { return React.createElement('div', { key: i, style: cellLineSubStyle }, line || '-'); }) ); } function isPlatePending(plateNo) { if (plateNo == null || plateNo === undefined) return true; var s = String(plateNo).trim(); return s === '' || s === '-'; } function displayPlateNo(plateNo) { return isPlatePending(plateNo) ? '车牌待选' : String(plateNo).trim(); } function displayBrandModel(brand, model) { var b = brand && brand !== '-' ? String(brand).trim() : ''; var m = model && model !== '-' ? String(model).trim() : ''; if (b && m) return b + '-' + m; if (b) return b; if (m) return m; return '-'; } function displayVin(vin, plateNo) { if (plateNo != null && isPlatePending(plateNo)) return '-'; var s = vin == null || vin === undefined ? '' : String(vin).trim(); return s || '-'; } function displayReplaceOldPlate(plate) { var s = plate == null ? '' : String(plate).trim(); return s || '-'; } function renderReplaceVehiclePopoverContent(record) { var oldPlate = displayReplaceOldPlate(record.replaceOldPlate); var newPlate = displayPlateNo(record.plateNo); return React.createElement('div', { style: { minWidth: 168, fontSize: 13, lineHeight: 1.5 } }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' } }, React.createElement('span', { style: { color: '#64748b' } }, '旧车'), React.createElement('span', { style: { fontWeight: 600, color: '#334155' } }, oldPlate), React.createElement('span', { style: { color: '#94a3b8', fontWeight: 600 } }, '→'), React.createElement('span', { style: { color: '#64748b' } }, '新车'), React.createElement('span', { style: { fontWeight: 600, color: isPlatePending(record.plateNo) ? '#d48806' : '#334155' } }, newPlate) ) ); } function renderTaskSourceCell(value, record) { if (value === '替换车') { return React.createElement(Popover, { content: renderReplaceVehiclePopoverContent(record), trigger: 'hover', placement: 'topLeft', mouseEnterDelay: 0.15, mouseLeaveDelay: 0.1, destroyTooltipOnHide: true }, renderTaskSourceTag('替换车', '#ea580c')); } if (value === '交车任务') return renderTaskSourceTag('交车任务', '#2563eb'); return value || '-'; } function renderVehicleInfoCell(record) { var pending = isPlatePending(record.plateNo); var plateStyle = Object.assign({}, cellLineMainStyle, { cursor: 'pointer' }, pending ? { color: '#d48806', fontWeight: 500 } : { color: '#165dff' }); return React.createElement('div', null, React.createElement('div', { style: plateStyle, role: 'button', tabIndex: 0, onClick: function () { openViewDrawer(record); }, onKeyDown: function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openViewDrawer(record); } } }, displayPlateNo(record.plateNo)), React.createElement('div', { style: cellLineSubStyle }, displayBrandModel(record.brand, record.model)), React.createElement('div', { style: cellLineSubStyle }, displayVin(record.vin, record.plateNo)) ); } // 列表列(按车辆一行;待处理/历史记录列顺序一致) var colDeliveryTime = { title: '完成交车时间', key: 'completedDeliveryTime', width: 118, render: function (_, r) { return renderCompletedDeliveryTimeCell(r); } }; var colDeliveryPerson = { title: '交车人', dataIndex: 'deliveryPerson', key: 'deliveryPerson', width: 80, render: function (v) { return v || '-'; } }; var colDeliveryStatus = { title: '交车状态', dataIndex: 'deliveryStatus', key: 'deliveryStatus', width: 108, render: function (v, r) { return renderDeliveryStatus(v, r); } }; var sharedListColumns = useMemo(function () { return [ { title: '车辆信息', key: 'vehicleInfo', width: 188, render: function (_, r) { return renderVehicleInfoCell(r); } }, { title: renderResizableTitle('合同信息', 'contractInfo'), key: 'contractInfo', width: colWidths.contractInfo, render: function (_, r) { return renderContractInfoCell(r); } }, { title: '业务负责人', key: 'businessInfo', width: 120, render: function (_, r) { return renderCellLines(r.businessDept, [r.businessOwner]); } }, { title: '任务来源', dataIndex: 'taskSource', key: 'taskSource', width: 92, ellipsis: true, render: function (v, r) { return renderTaskSourceCell(v, r); } }, { title: '交车地点', key: 'deliveryPlace', width: 168, render: function (_, r) { return renderDeliveryPlaceCell(r); } }, colDeliveryStatus, colDeliveryTime, colDeliveryPerson, { title: '是否归还', key: 'vehicleReturned', width: 88, render: function (_, r) { return renderVehicleReturnedCell(r); } }, { title: '交车记录', key: 'deliveryRecord', width: 118, render: function (_, r) { return renderDeliveryRecordCell(r); } }, { title: '交车任务创建时间', key: 'taskCreateTime', width: 150, ellipsis: true, render: function (_, r) { return renderTaskCreateTimeCell(r); } }, { title: '交车任务创建人', dataIndex: 'createBy', key: 'taskCreateBy', width: 108, ellipsis: true, render: function (v) { return renderTaskCreateByCell(v); } } ]; }, [colWidths.contractInfo, handleColumnResizeStart, navigateToContractDetail]); var listColumns = useMemo(function () { return sharedListColumns.concat([ { title: '操作', key: 'action', width: 96, fixed: 'right', render: function (_, r) { var nodes = [ React.createElement(Button, { key: 'view', type: 'link', size: 'small', onClick: function () { openViewDrawer(r); } }, '查看') ]; if (canEditDeliveryRow(r)) { nodes.push(React.createElement(Button, { key: 'edit', type: 'link', size: 'small', onClick: function () { openEditDrawer(r); } }, '编辑')); } return React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 2, flexWrap: 'nowrap' } }, nodes); } } ]); }, [sharedListColumns, openEditDrawer, openViewDrawer]); var kpiCards = useMemo(function () { return [ { key: 'total', type: 'total', title: '全部交车任务', desc: '当前筛选条件下的全部交车任务(含进行中与已完成)', val: kpiStats.total, icon: DV_KPI_ICONS.total }, { key: 'inProgress', type: 'progress', title: '进行中的交车任务', desc: '交车状态为「未开始」「已保存」「待客户签章」的任务(客户未完成最终签章)', val: kpiStats.inProgress, icon: DV_KPI_ICONS.progress }, { key: 'completed', type: 'completed', title: '已完成的交车任务', desc: '客户已完成最终签章步骤的所有交车任务(状态为「客户已签章」)', val: kpiStats.completed, icon: DV_KPI_ICONS.completed } ]; }, [kpiStats]); function renderKpiCard(card) { var active = kpiFilter === card.key; return React.createElement('div', { key: card.key, role: 'button', tabIndex: 0, className: 'lc-alert-card lc-alert-card--' + card.type + ' lc-alert-card-clickable' + (active ? ' lc-alert-card-active' : ''), onClick: function () { handleKpiCardClick(card.key); }, onKeyDown: function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleKpiCardClick(card.key); } } }, React.createElement('div', { className: 'lc-alert-card-tip-anchor' }, React.createElement(Tooltip, { title: card.desc, placement: 'topRight', overlayStyle: { maxWidth: 360 } }, React.createElement('span', { className: 'lc-alert-card-tip', role: 'img', 'aria-label': card.title + '说明', onClick: function (e) { e.stopPropagation(); }, onMouseDown: function (e) { e.stopPropagation(); } }, DV_KPI_TIP_SVG) ) ), React.createElement('div', { className: 'lc-alert-card-icon' }, card.icon), React.createElement('div', { className: 'lc-alert-card-main' }, React.createElement('div', { className: 'lc-alert-card-val' }, card.val), React.createElement('div', { className: 'lc-alert-card-title' }, card.title) ) ); } var tablePagination = useMemo(function () { return { current: page, pageSize: pageSize, total: totalCount, showSizeChanger: true, showTotal: function (t) { return '共 ' + t + ' 条'; }, pageSizeOptions: ['10', '20', '50'], onChange: function (p, size) { setPage(p); if (size !== pageSize) setPageSize(size); } }; }, [page, pageSize, totalCount]); var filterLabelStyle = { display: 'block', marginBottom: 4, color: '#333', fontSize: 14 }; var filterControlStyle = { width: '100%' }; var filterItemStyle = { minWidth: 0 }; var styles = { page: { padding: '16px 24px 48px', backgroundColor: '#f5f5f5', minHeight: '100vh', 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' }, cardBody: { padding: '20px 24px' }, filterGrid: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: '16px 24px', alignItems: 'start' }, filterActions: { display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 8, marginTop: 16, paddingTop: 16, borderTop: '1px solid #f1f5f9' } }; var filterItems = [ React.createElement('div', { key: 'contractCode', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '合同编号'), React.createElement(Select, { placeholder: '请输入或选择合同编号', allowClear: true, showSearch: true, optionFilterProp: 'label', style: filterControlStyle, value: filters.contractCode, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { contractCode: v }); }); }, options: contractCodeOptions }) ), React.createElement('div', { key: 'projectName', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '项目名称'), React.createElement(Select, { placeholder: '请输入或选择项目名称', allowClear: true, showSearch: true, optionFilterProp: 'label', style: filterControlStyle, value: filters.projectName, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { projectName: v }); }); }, options: projectNameOptions }) ), React.createElement('div', { key: 'customerName', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '客户名称'), React.createElement(Select, { placeholder: '请输入或选择客户名称', allowClear: true, showSearch: true, optionFilterProp: 'label', style: filterControlStyle, value: filters.customerName, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { customerName: v }); }); }, options: customerNameOptions }) ), React.createElement('div', { key: 'deliveryRegion', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '交车区域'), React.createElement(Cascader, { options: regionOptions, placeholder: '请选择省-市', allowClear: true, style: filterControlStyle, value: deliveryRegionValue, onChange: function (value) { var s; if (value && value.length >= 2) { var prov = regionOptions.find(function (r) { return r.value === value[0]; }); var city = prov && prov.children && prov.children.find(function (c) { return c.value === value[1]; }); s = prov && city ? prov.label + '-' + city.label : undefined; } else { s = undefined; } setFilters(function (f) { return patchFilters(f, { deliveryRegion: s }); }); }, displayRender: function (labels) { return labels && labels.length ? labels.join(' / ') : ''; } }) ), React.createElement('div', { key: 'completedTime', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '完成交车时间'), React.createElement(RangePicker, { style: filterControlStyle, placeholder: ['请选择开始时间', '请选择结束时间'], value: dateRangeValue, onChange: onDateRangeChange }) ), React.createElement('div', { key: 'deliveryPerson', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '交车人'), React.createElement(Select, { placeholder: '请输入或选择交车人', allowClear: true, showSearch: true, optionFilterProp: 'label', style: filterControlStyle, value: filters.deliveryPerson, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { deliveryPerson: v }); }); }, options: deliveryPersonOptions }) ), React.createElement('div', { key: 'vin', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '车辆识别代码'), React.createElement(Select, { placeholder: '请输入或选择车辆识别代码', allowClear: true, showSearch: true, style: filterControlStyle, value: filters.vin, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { vin: v }); }); }, options: dynamicFilterOptions.vinOptions, filterOption: filterSelectOption }) ), React.createElement('div', { key: 'vehicleType', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '车辆类型'), React.createElement(Select, { placeholder: '请选择车辆类型', allowClear: true, showSearch: true, style: filterControlStyle, value: filters.vehicleType, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { vehicleType: v }); }); }, options: dynamicFilterOptions.vehicleTypeOptions, filterOption: filterSelectOption }) ), React.createElement('div', { key: 'brand', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '品牌'), React.createElement(Select, { placeholder: '请选择品牌', allowClear: true, showSearch: true, style: filterControlStyle, value: filters.brand, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { brand: v }); }); }, options: dynamicFilterOptions.brandOptions, filterOption: filterSelectOption }) ), React.createElement('div', { key: 'model', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '型号'), React.createElement(Select, { placeholder: '请选择或输入型号', allowClear: true, showSearch: true, style: filterControlStyle, value: filters.model, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { model: v }); }); }, options: dynamicFilterOptions.modelOptions, filterOption: filterSelectOption }) ), React.createElement('div', { key: 'businessDept', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '业务部门'), React.createElement(Select, { placeholder: '请输入或选择业务部门', allowClear: true, showSearch: true, style: filterControlStyle, value: filters.businessDept, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { businessDept: v }); }); }, options: dynamicFilterOptions.businessDeptOptions, filterOption: filterSelectOption }) ), React.createElement('div', { key: 'businessOwner', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '业务负责人'), React.createElement(Select, { placeholder: '请输入或选择业务负责人', allowClear: true, showSearch: true, style: filterControlStyle, value: filters.businessOwner, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { businessOwner: v }); }); }, options: dynamicFilterOptions.businessOwnerOptions, filterOption: filterSelectOption }) ), React.createElement('div', { key: 'taskSource', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '任务来源'), React.createElement(Select, { placeholder: '请选择任务来源', allowClear: true, style: filterControlStyle, value: filters.taskSource, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { taskSource: v }); }); }, options: taskSourceOptions }) ), React.createElement('div', { key: 'bizType', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '业务类型'), React.createElement(Select, { placeholder: '请选择业务类型', allowClear: true, style: filterControlStyle, value: filters.bizType, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { bizType: v }); }); }, options: bizTypeOptions }) ), React.createElement('div', { key: 'isDelayed', style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '是否延期'), React.createElement(Select, { placeholder: '请选择是否延期', allowClear: true, style: filterControlStyle, value: filters.isDelayed, onChange: function (v) { setFilters(function (f) { return patchFilters(f, { isDelayed: v }); }); }, options: isDelayedOptions }) ) ]; var filterVisibleCount = filterExpanded ? filterItems.length : 4; var filterNodes = []; var fi; for (fi = 0; fi < filterVisibleCount && fi < filterItems.length; fi++) { filterNodes.push(filterItems[fi]); } var breadcrumbItems = [ { title: '运维管理' }, { title: '车辆业务' }, { title: '交车管理' } ]; return React.createElement('div', { style: styles.page }, React.createElement('style', null, DV_KPI_STYLE), React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 } }, React.createElement(Breadcrumb, { items: breadcrumbItems }), React.createElement(Button, { type: 'link', onClick: function () { setRequirementModalOpen(true); } }, '查看需求说明') ), React.createElement(Card, { style: { marginBottom: 16 } }, React.createElement('div', { style: styles.cardBody }, React.createElement('div', { style: styles.filterGrid }, filterNodes), React.createElement('div', { style: styles.filterActions }, React.createElement(Button, { onClick: handleReset }, '重置'), React.createElement(Button, { type: 'primary', onClick: handleQuery }, '搜索'), React.createElement(Button, { type: 'link', size: 'small', onClick: function () { setFilterExpanded(!filterExpanded); }, style: { display: 'inline-flex', alignItems: 'center', gap: 4, padding: '0 4px' } }, filterExpanded ? '收起' : '展开', React.createElement('svg', { width: 12, height: 12, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round', style: { transform: filterExpanded ? 'rotate(180deg)' : 'none', transition: 'transform 0.2s ease' } }, React.createElement('polyline', { points: '6 9 12 15 18 9' })) ) ) ) ), React.createElement(Card, null, React.createElement('div', { style: styles.cardBody }, React.createElement('div', { className: 'dv-kpi-stats-row' }, kpiCards.map(renderKpiCard)), React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, flexWrap: 'wrap', gap: 12 } }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 } }, React.createElement('span', { style: { color: '#333', fontSize: 14, whiteSpace: 'nowrap' } }, '车牌号'), React.createElement(Select, { placeholder: '请输入或选择车牌号', allowClear: true, showSearch: true, style: { width: 220 }, value: appliedFilters.plateNo, onChange: handleListPlateNoChange, options: dynamicFilterOptions.plateNoOptions, filterOption: filterSelectOption }) ), React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', marginLeft: 'auto' } }, React.createElement('span', { style: { fontSize: 13, color: '#64748b' } }, '当前标签:', React.createElement('span', { style: { color: '#334155', fontWeight: 600 } }, kpiExportLabelMap[kpiFilter] || '-'), ' · 导出与列表一致(含筛选,全部命中行)' ), React.createElement(Button, { onClick: handleExport }, '导出') ) ), React.createElement(Table, { columns: listColumns, dataSource: displayList, rowKey: 'id', pagination: tablePagination, tableLayout: 'fixed', scroll: { x: 1760 }, size: 'middle' }) ) ), React.createElement(Modal, { title: '需求说明', open: requirementModalOpen[0], onCancel: function () { setRequirementModalOpen(false); }, footer: React.createElement(Button, { onClick: function () { setRequirementModalOpen(false); } }, '关闭'), width: 800, destroyOnClose: true }, React.createElement('div', { style: { maxHeight: '72vh', overflowY: 'auto', whiteSpace: 'pre-wrap', lineHeight: 1.65, fontSize: 13, color: '#334155' } }, requirementDocContent)), React.createElement(DeliveryEditDrawer, { open: editDrawer.open, record: editDrawer.record, mode: editDrawer.mode, onClose: closeEditDrawer, onSave: handleEditSave, onSubmit: handleEditSubmit }) ); }; if (typeof window !== 'undefined') { window.Component = Component; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function () { var rootEl = document.getElementById('root'); if (rootEl && window.ReactDOM && window.React) { var root = ReactDOM.createRoot(rootEl); root.render(React.createElement(Component)); } }); } else { var rootEl = document.getElementById('root'); if (rootEl && window.ReactDOM && window.React) { var root = ReactDOM.createRoot(rootEl); root.render(React.createElement(Component)); } } }