diff --git a/web端/运维管理/车辆业务/还车管理-查看.jsx b/web端/运维管理/车辆业务/还车管理-查看.jsx new file mode 100644 index 0000000..970d164 --- /dev/null +++ b/web端/运维管理/车辆业务/还车管理-查看.jsx @@ -0,0 +1,666 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 运维管理 - 车辆业务 - 还车管理 - 查看(只读) + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + var useCallback = React.useCallback; + var useRef = React.useRef; + var useEffect = React.useEffect; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Input = antd.Input; + var Select = antd.Select; + var Switch = antd.Switch; + var DatePicker = antd.DatePicker; + var Table = antd.Table; + var Button = antd.Button; + var Drawer = antd.Drawer; + var Modal = antd.Modal; + var message = antd.message; + + // ---------- utils ---------- + function RequiredLabel(text) { + return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 4 } }, + React.createElement('span', { style: { color: '#f5222d', fontWeight: 600 } }, '*'), + React.createElement('span', null, text) + ); + } + + function filterOption(input, option) { + var label = (option && (option.label || option.children)) || ''; + return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0; + } + + function makeThumb(url, onPreview, onRemove, disabled) { + return React.createElement('div', { + style: { + width: 64, + height: 64, + borderRadius: 4, + border: '1px solid #f0f0f0', + background: '#fafafa', + position: 'relative', + overflow: 'hidden' + } + }, + url ? React.createElement('img', { + src: url, + style: { width: '100%', height: '100%', objectFit: 'cover', cursor: disabled ? 'default' : 'pointer' }, + onClick: disabled ? undefined : onPreview + }) : null, + (disabled || !onRemove) ? null : React.createElement('div', { + style: { + position: 'absolute', + right: 6, + top: 6, + width: 20, + height: 20, + borderRadius: 999, + background: 'rgba(0,0,0,0.55)', + color: '#fff', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + cursor: 'pointer', + fontSize: 12, + userSelect: 'none' + }, + onClick: function (e) { e.stopPropagation(); onRemove && onRemove(); } + }, '×') + ); + } + + function UploadBox(props) { + var label = props.label; + var value = props.value || []; // array of {uid,name,url} + var max = props.max || 1; + var disabled = !!props.disabled; + var tip = props.tip; + + return React.createElement('div', null, + label ? React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, label) : null, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } }, + (value || []).map(function (f) { + return React.createElement('div', { key: f.uid }, + makeThumb( + f.url, + function () { previewState[1]({ open: true, url: f.url, title: f.name || '预览' }); }, + null, + false + ) + ); + }), + disabled ? null : ((value || []).length >= max ? null : React.createElement('div', { + style: { + width: 64, + height: 64, + borderRadius: 4, + border: '1px dashed #d9d9d9', + background: '#fff', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + color: '#999' + } + }, '上传')) + ), + tip ? React.createElement('div', { style: { marginTop: 6, fontSize: 12, color: '#999' } }, tip) : null + ); + } + + // ---------- styles ---------- + var layoutStyle = { padding: '16px 24px 88px', background: '#f5f5f5', minHeight: '100vh' }; + var cardStyle = { marginBottom: 16 }; + var footerStyle = { + position: 'fixed', + left: 0, + right: 0, + bottom: 0, + background: '#fff', + borderTop: '1px solid #f0f0f0', + padding: '12px 24px', + display: 'flex', + justifyContent: 'flex-start', + gap: 12, + zIndex: 10 + }; + var styles = { + formRow: { display: 'flex', gap: 16, marginBottom: 12, alignItems: 'flex-start' }, + formItem: { flex: 1, minWidth: 0 }, + label: { fontSize: 12, color: '#666', marginBottom: 6 } + }; + + function FormItem(props) { + var label = props.label; + var required = !!props.required; + var fullWidth = !!props.fullWidth; + return React.createElement( + 'div', + { style: fullWidth ? Object.assign({}, styles.formItem, { flex: '0 0 100%' }) : styles.formItem }, + React.createElement('div', { style: styles.label }, required ? RequiredLabel(label) : label), + props.children + ); + } + + // ---------- mock data(与还车页一致) ---------- + var vehicleInfo = useMemo(function () { + return { + contractCode: 'HT-ZL-2025-001', + projectName: '嘉兴氢能示范项目', + customerName: '嘉兴某某物流有限公司', + businessDept: '华东业务部', + businessOwner: '李经理', + plateNo: '京A12345', + vehicleType: '重型厢式货车', + brand: '东风', + model: 'DFH1180', + deliveryTime: '2025-02-28 14:30', + deliveryRegion: '浙江省-嘉兴市', + deliveryAddress: '嘉兴市南湖区科技大道1号', + deliverySpareTireDepth: '6.50', + deliveryMileageKm: '15230.12', + deliveryBatteryKwh: '68.40', + deliveryHydrogenAmount: '28.30', + hydrogenUnit: 'MPa', + deliverySpareTirePhotoUrl: 'https://dummyimage.com/600x400/eee/666&text=交车备胎照片' + }; + }, []); + + var deliveryPhotos = useMemo(function () { + function u(txt) { return 'https://dummyimage.com/600x400/eee/666&text=' + encodeURIComponent(txt); } + return { + vehicle: { + '仪表盘': [{ uid: 'dv1', name: '仪表盘.jpg', url: u('交车-仪表盘') }], + '车辆正面': [{ uid: 'dv2', name: '车辆正面.jpg', url: u('交车-车辆正面') }], + '车辆左前方': [{ uid: 'dv3', name: '车辆左前方.jpg', url: u('交车-车辆左前方') }], + '车辆左后方': [{ uid: 'dv4', name: '车辆左后方.jpg', url: u('交车-车辆左后方') }], + '车辆右后方': [{ uid: 'dv5', name: '车辆右后方.jpg', url: u('交车-车辆右后方') }], + '车辆右前方': [{ uid: 'dv6', name: '车辆右前方.jpg', url: u('交车-车辆右前方') }] + }, + chassis: { + '正前方底部': [{ uid: 'dc1', name: '正前方底部.jpg', url: u('交车-正前方底部') }], + '左侧前方底部': [{ uid: 'dc2', name: '左侧前方底部.jpg', url: u('交车-左侧前方底部') }], + '左侧后方底部': [{ uid: 'dc3', name: '左侧后方底部.jpg', url: u('交车-左侧后方底部') }], + '正后方底部': [{ uid: 'dc4', name: '正后方底部.jpg', url: u('交车-正后方底部') }], + '右侧后方底部': [{ uid: 'dc5', name: '右侧后方底部.jpg', url: u('交车-右侧后方底部') }], + '右侧前方底部': [{ uid: 'dc6', name: '右侧前方底部.jpg', url: u('交车-右侧前方底部') }] + }, + tire: { + '左前轮': [{ uid: 'dt1', name: '左前轮.jpg', url: u('交车-左前轮') }], + '左后轮(内)': [{ uid: 'dt2', name: '左后轮(内).jpg', url: u('交车-左后轮(内)') }], + '左后轮(外)': [{ uid: 'dt3', name: '左后轮(外).jpg', url: u('交车-左后轮(外)') }], + '右前轮': [{ uid: 'dt4', name: '右前轮.jpg', url: u('交车-右前轮') }], + '右后轮(内)': [{ uid: 'dt5', name: '右后轮(内).jpg', url: u('交车-右后轮(内)') }], + '右后轮(外)': [{ uid: 'dt6', name: '右后轮(外).jpg', url: u('交车-右后轮(外)') }], + '备胎': [{ uid: 'dt7', name: '备胎.jpg', url: u('交车-备胎') }] + }, + defect: [ + { uid: 'dd1', name: '交车瑕疵1.jpg', url: u('交车-瑕疵1') }, + { uid: 'dd2', name: '交车瑕疵2.jpg', url: u('交车-瑕疵2') } + ], + other: [ + { uid: 'do1', name: '交车其他1.jpg', url: u('交车-其他1') }, + { uid: 'do2', name: '交车其他2.jpg', url: u('交车-其他2') } + ] + }; + }, []); + + // ---------- states ---------- + var previewState = useState({ open: false, url: '', title: '' }); + var requirementModalOpenState = useState(false); + var inspectionDrawerOpenState = useState(false); + + var [returnPhotos, setReturnPhotos] = useState(function () { + function u(txt) { return 'https://dummyimage.com/600x400/eee/666&text=' + encodeURIComponent(txt); } + return { + vehicle: {}, + chassis: {}, + tire: {}, + defect: [ + { uid: 'rd1', name: '还车瑕疵1.jpg', url: u('还车-瑕疵1') } + ], + other: [ + { uid: 'ro1', name: '还车其他1.jpg', url: u('还车-其他1') } + ] + }; + }); + + useEffect(function () { + setReturnPhotos(function (prev) { + var next = Object.assign({}, prev || {}); + ['vehicle', 'chassis', 'tire'].forEach(function (k) { + next[k] = Object.assign({}, next[k] || {}); + }); + Object.keys(deliveryPhotos.vehicle || {}).forEach(function (slot) { if (!next.vehicle[slot]) next.vehicle[slot] = []; }); + Object.keys(deliveryPhotos.chassis || {}).forEach(function (slot) { if (!next.chassis[slot]) next.chassis[slot] = []; }); + Object.keys(deliveryPhotos.tire || {}).forEach(function (slot) { if (!next.tire[slot]) next.tire[slot] = []; }); + return next; + }); + }, []); + + var [form] = useState(function () { + return { + returnPerson: '张三', + returnPhone: '13800138001', + returnIdCard: '320101199001018888', + returnPlaceType: 'parking', + returnPlaceName: '停车场A', + arriveTime: '2025-03-12 10:20', + spareTirePhoto: [{ uid: 'sp1', name: '还车备胎照片.jpg', url: 'https://dummyimage.com/600x400/eee/666&text=还车备胎照片' }], + spareTireDepth: '6.10', + returnMileageKm: '16280.50', + returnBatteryKwh: '60.20', + returnHydrogenAmount: '20.10', + hydrogenUnit: vehicleInfo.hydrogenUnit, + serviceFee: '120.00' + }; + }); + + // ---------- inspection checklist(只读) ---------- + var inspectionCategoryItems = useMemo(function () { + return { + '车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'], + '仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'], + '驾驶室': ['点烟器', '车窗升降', '按键开关', '雨刮器', '内后视镜是否正常', '内/外门把手', '安全带', '空调冷暖风', '仪表盘', '门锁功能', '手刹', '车钥匙功能是否正常', '喇叭', '音响功能', '遮阳板', '主副驾座椅', '方向盘', '内饰干净整洁'], + '轮胎': ['前左胎', '前右胎', '后左胎', '后右胎', '备胎'], + '液位检查': ['冷却液', '制动液', '玻璃水'], + '外观检查': ['车身外观', '漆面', '玻璃'], + '车辆外观': ['整车外观'], + '其他': ['其他检查项'], + '随车工具': ['三角牌', '灭火器', '反光背心'], + '随车证件': ['行驶证', '营运证', '保险单'], + '整车': ['整车状态'], + '燃料电池系统': ['氢系统', '储氢瓶'], + '冷机': ['冷机运行'], + '制动系统': ['制动踏板', '驻车制动'] + }; + }, []); + + function buildInspectionList() { + var list = []; + var categories = Object.keys(inspectionCategoryItems); + for (var i = 0; i < categories.length; i++) { + var cat = categories[i]; + var items = inspectionCategoryItems[cat] || []; + for (var j = 0; j < items.length; j++) { + var it = items[j]; + var isTire = cat === '轮胎'; + list.push({ + key: 'v-ins-' + i + '-' + j, + category: cat, + item: it, + checked: true, + treadDepth: isTire ? '6.50' : '', + remark: '' + }); + } + } + return list; + } + + var inspectionList = useMemo(function () { return buildInspectionList(); }, []); + var inspectionListRef = useRef(null); + inspectionListRef.current = inspectionList; + + var inspectionColumns = useMemo(function () { + return [ + { + title: '类别', + dataIndex: 'category', + key: 'category', + width: 140, + render: function (text, record, index) { + var rows = inspectionListRef.current || []; + var cat = record && record.category; + if (!cat) return { children: text, props: { rowSpan: 1 } }; + + var isFirst = true; + for (var i = index - 1; i >= 0; i--) { + if (!rows[i] || rows[i].category !== cat) break; + isFirst = false; + break; + } + if (!isFirst) return { children: null, props: { rowSpan: 0 } }; + + var span = 1; + for (var j = index + 1; j < rows.length; j++) { + if (!rows[j] || rows[j].category !== cat) break; + span++; + } + return { children: text, props: { rowSpan: span } }; + } + }, + { title: '检查项目', dataIndex: 'item', key: 'item', width: 220 }, + { + title: '检查情况', + dataIndex: 'checked', + key: 'checked', + width: 220, + render: function (_, record) { + var isTire = record && (record.category === '轮胎' || String(record.item || '').indexOf('胎纹') >= 0); + return isTire + ? React.createElement(Input, { value: record.treadDepth, disabled: true, addonAfter: 'mm' }) + : React.createElement(Switch, { checked: !!record.checked, disabled: true }); + } + }, + { + title: '备注', + dataIndex: 'remark', + key: 'remark', + render: function (_, record) { + return React.createElement(Input, { value: record.remark, disabled: true }); + } + } + ]; + }, []); + + // ---------- helpers ---------- + function PreviewDeliveryThumb(props) { + var url = props.url; + if (!url) return React.createElement('div', { style: { width: 64, height: 64, borderRadius: 4, border: '1px solid #f0f0f0', background: '#fafafa' } }); + return makeThumb(url, + function () { previewState[1]({ open: true, url: url, title: props.title || '交车照片预览' }); }, + null, + false + ); + } + + function ReturnPhotoGridColumn(title, slots, required, deliveryMap, returnMap) { + return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: 12 } }, + React.createElement('div', { style: { fontWeight: 600, marginBottom: 12 } }, required ? RequiredLabel(title) : title), + React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12 } }, + (slots || []).map(function (slot) { + var deliveryArr = (deliveryMap && deliveryMap[slot]) || []; + var deliveryUrl = deliveryArr && deliveryArr[0] ? deliveryArr[0].url : ''; + var retArr = (returnMap && returnMap[slot]) || []; + return React.createElement('div', { key: slot }, + React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, required ? RequiredLabel(slot) : slot), + React.createElement('div', { style: { display: 'flex', gap: 12, alignItems: 'flex-start', flexWrap: 'wrap' } }, + React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } }, + PreviewDeliveryThumb({ url: deliveryUrl, title: slot }), + React.createElement('div', { style: { fontSize: 11, color: '#999', marginTop: 6 } }, '交车时照片') + ), + React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } }, + retArr && retArr[0] + ? makeThumb(retArr[0].url, function () { previewState[1]({ open: true, url: retArr[0].url, title: retArr[0].name || '还车照片' }); }, null, false) + : React.createElement('div', { style: { width: 64, height: 64, borderRadius: 4, border: '1px dashed #d9d9d9', background: '#fff' } }), + React.createElement('div', { style: { fontSize: 11, color: '#999', marginTop: 6 } }, '还车照片') + ) + ) + ); + }) + ) + ); + } + + var handleBack = useCallback(function () { + message.info('返回还车管理页(原型)'); + }, []); + + // ---------- render ---------- + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { items: [{ title: '运维管理' }, { title: '车辆业务' }, { title: '还车管理' }, { title: '查看' }] }), + React.createElement(Button, { type: 'link', onClick: function () { requirementModalOpenState[1](true); } }, '查看需求说明') + ), + + React.createElement(Card, { title: '合同信息', style: cardStyle }, + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '合同编码' }, + React.createElement(Input, { value: vehicleInfo.contractCode, disabled: true }) + ), + React.createElement(FormItem, { label: '项目名称' }, + React.createElement(Input, { value: vehicleInfo.projectName, disabled: true }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '客户名称' }, + React.createElement(Input, { value: vehicleInfo.customerName, disabled: true }) + ), + React.createElement(FormItem, { label: '业务部门' }, + React.createElement(Input, { value: vehicleInfo.businessDept, disabled: true }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '业务负责人', fullWidth: true }, + React.createElement(Input, { value: vehicleInfo.businessOwner, disabled: true }) + ) + ) + ), + + React.createElement(Card, { title: '车辆信息', style: cardStyle }, + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '车牌号' }, + React.createElement(Input, { value: vehicleInfo.plateNo, disabled: true }) + ), + React.createElement(FormItem, { label: '车辆型号' }, + React.createElement(Input, { value: vehicleInfo.vehicleType, disabled: true }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '品牌' }, + React.createElement(Input, { value: vehicleInfo.brand, disabled: true }) + ), + React.createElement(FormItem, { label: '型号' }, + React.createElement(Input, { value: vehicleInfo.model, disabled: true }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '交车时间' }, + React.createElement(Input, { value: vehicleInfo.deliveryTime, disabled: true }) + ), + React.createElement(FormItem, { label: '交车地点(省-市)' }, + React.createElement(Input, { value: vehicleInfo.deliveryRegion, disabled: true }) + ) + ) + ), + + React.createElement(Card, { title: '还车明细', style: cardStyle }, + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '还车人', required: true }, + React.createElement(Input, { value: form.returnPerson, disabled: true }) + ), + React.createElement(FormItem, { label: '还车人电话', required: true }, + React.createElement(Input, { value: form.returnPhone, disabled: true }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '还车人身份证', required: true }, + React.createElement(Input, { value: form.returnIdCard, disabled: true }) + ), + React.createElement(FormItem, { label: '还车地点', required: true }, + React.createElement(Select, { + placeholder: '请选择还车地点', + value: form.returnPlaceType, + disabled: true, + options: [ + { value: 'parking', label: '停车场' }, + { value: 'repair', label: '维修站' } + ] + }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: form.returnPlaceType === 'parking' ? '停车场' : '维修站', required: true }, + React.createElement(Select, { + placeholder: '请选择', + value: form.returnPlaceName, + disabled: true, + showSearch: true, + filterOption: filterOption, + options: [{ value: form.returnPlaceName, label: form.returnPlaceName }] + }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '到达时间', required: true, fullWidth: true }, + React.createElement(DatePicker, { + showTime: { format: 'HH:mm' }, + format: 'YYYY-MM-DD HH:mm', + style: { width: '100%' }, + disabled: true, + value: (function () { + var dayjs = window.dayjs; + return form.arriveTime && dayjs ? dayjs(form.arriveTime) : null; + })() + }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '备胎照片', required: true, fullWidth: true }, + React.createElement('div', { style: { display: 'flex', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' } }, + React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } }, + makeThumb( + vehicleInfo.deliverySpareTirePhotoUrl, + function () { previewState[1]({ open: true, url: vehicleInfo.deliverySpareTirePhotoUrl, title: '交车时备胎照片' }); }, + null, + false + ), + React.createElement('div', { style: { fontSize: 11, color: '#999', marginTop: 6 } }, '交车时照片') + ), + React.createElement('div', { style: { flex: '1 1 240px', minWidth: 240 } }, + UploadBox({ label: null, value: form.spareTirePhoto, max: 1, disabled: true }) + ) + ) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '交车时备胎胎纹深度' }, + React.createElement(Input, { value: vehicleInfo.deliverySpareTireDepth, disabled: true, addonAfter: 'mm' }) + ), + React.createElement(FormItem, { label: '还车时备胎胎纹深度', required: true }, + React.createElement(Input, { value: form.spareTireDepth, disabled: true, addonAfter: 'mm' }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '交车里程(km)' }, + React.createElement(Input, { value: vehicleInfo.deliveryMileageKm, disabled: true, addonAfter: 'km' }) + ), + React.createElement(FormItem, { label: '还车里程(km)', required: true }, + React.createElement(Input, { value: form.returnMileageKm, disabled: true, addonAfter: 'km' }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '交车电量(kWh)' }, + React.createElement(Input, { value: vehicleInfo.deliveryBatteryKwh, disabled: true, addonAfter: 'kWh' }) + ), + React.createElement(FormItem, { label: '还车电量(kWh)', required: true }, + React.createElement(Input, { value: form.returnBatteryKwh, disabled: true, addonAfter: 'kWh' }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '交车氢量(' + form.hydrogenUnit + ')' }, + React.createElement(Input, { value: vehicleInfo.deliveryHydrogenAmount, disabled: true, addonAfter: form.hydrogenUnit }) + ), + React.createElement(FormItem, { label: '还车氢量(' + form.hydrogenUnit + ')', required: true }, + React.createElement(Input, { value: form.returnHydrogenAmount, disabled: true, addonAfter: form.hydrogenUnit }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '接车服务费' }, + React.createElement(Input, { value: form.serviceFee, disabled: true, addonAfter: '元' }) + ), + React.createElement('div', { style: styles.formItem }) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '还车检查单', fullWidth: true }, + React.createElement(Button, { onClick: function () { inspectionDrawerOpenState[1](true); } }, '还车检查单') + ) + ) + ), + + React.createElement(Card, { title: '还车照片', style: cardStyle }, + React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12 } }, + ReturnPhotoGridColumn('车辆', Object.keys(deliveryPhotos.vehicle || {}), true, deliveryPhotos.vehicle, returnPhotos.vehicle), + ReturnPhotoGridColumn('底盘', Object.keys(deliveryPhotos.chassis || {}), true, deliveryPhotos.chassis, returnPhotos.chassis), + React.createElement('div', { style: { gridColumn: 'span 2' } }, + ReturnPhotoGridColumn('轮胎', Object.keys(deliveryPhotos.tire || {}), true, deliveryPhotos.tire, returnPhotos.tire) + ) + ), + React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12, marginTop: 12 } }, + (function () { + var left = (deliveryPhotos.defect || []).slice(0, 4); + return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: 12 } }, + React.createElement('div', { style: { fontWeight: 600, marginBottom: 12 } }, '瑕疵'), + React.createElement('div', { style: { display: 'flex', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' } }, + React.createElement('div', { style: { width: 260 } }, + React.createElement('div', { style: { display: 'flex', gap: 12, flexWrap: 'wrap' } }, left.map(function (f) { + return React.createElement('div', { key: f.uid }, PreviewDeliveryThumb({ url: f.url, title: '交车瑕疵' })); + })), + React.createElement('div', { style: { fontSize: 12, color: '#999', marginTop: 6 } }, '交车时照片') + ), + React.createElement('div', { style: { flex: '1 1 220px', minWidth: 220 } }, + UploadBox({ label: null, value: returnPhotos.defect, max: 4, disabled: true, tip: '还车照片' }) + ) + ) + ); + })(), + (function () { + var left = (deliveryPhotos.other || []).slice(0, 4); + return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: 12 } }, + React.createElement('div', { style: { fontWeight: 600, marginBottom: 12 } }, '其他'), + React.createElement('div', { style: { display: 'flex', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' } }, + React.createElement('div', { style: { width: 260 } }, + React.createElement('div', { style: { display: 'flex', gap: 12, flexWrap: 'wrap' } }, left.map(function (f) { + return React.createElement('div', { key: f.uid }, PreviewDeliveryThumb({ url: f.url, title: '交车其他' })); + })), + React.createElement('div', { style: { fontSize: 12, color: '#999', marginTop: 6 } }, '交车时照片') + ), + React.createElement('div', { style: { flex: '1 1 220px', minWidth: 220 } }, + UploadBox({ label: null, value: returnPhotos.other, max: 4, disabled: true, tip: '还车照片' }) + ) + ) + ); + })() + ) + ), + + React.createElement('div', { style: footerStyle }, + React.createElement(Button, { onClick: handleBack }, '返回') + ), + + React.createElement(Modal, { + open: !!previewState[0].open, + title: previewState[0].title || '预览', + footer: null, + onCancel: function () { previewState[1]({ open: false, url: '', title: '' }); }, + width: 860 + }, + previewState[0].url ? React.createElement('img', { src: previewState[0].url, style: { width: '100%', maxHeight: '70vh', objectFit: 'contain' } }) : null + ), + + React.createElement(Drawer, { + open: inspectionDrawerOpenState[0], + title: '还车检查单', + width: 920, + placement: 'right', + onClose: function () { inspectionDrawerOpenState[1](false); }, + styles: { body: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', paddingBottom: 0 } }, + footer: React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-start', gap: 8 } }, + React.createElement(Button, { type: 'primary', disabled: true }, '提交'), + React.createElement(Button, { onClick: function () { inspectionDrawerOpenState[1](false); } }, '返回') + ) + }, + React.createElement('div', { style: { flex: 1, minHeight: 0, overflow: 'auto' } }, + React.createElement(Table, { rowKey: 'key', columns: inspectionColumns, dataSource: inspectionList, pagination: false, bordered: true, size: 'small' }) + ) + ), + + React.createElement(Modal, { + open: requirementModalOpenState[0], + onCancel: function () { requirementModalOpenState[1](false); }, + onOk: function () { requirementModalOpenState[1](false); }, + title: '需求说明', + width: 860, + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, + React.createElement('div', { style: { whiteSpace: 'pre-wrap', lineHeight: 1.7 } }, + '还车管理-查看\\n一个「数字化资产ONEOS运管平台」中的「还车管理」「查看」模块\\n\\n说明:本页为只读查看态,所有控件禁用,仅用于查看还车单信息与照片。\\n\\n1.面包屑:\\n1.1.运维管理-车辆业务-还车管理-查看\\n\\n2.合同信息:合同编码、项目名称、客户名称、业务部门、业务负责人\\n3.车辆信息:车牌号、车辆型号、品牌、型号、交车时间、交车地点(省-市)\\n4.还车明细:展示还车人/电话/身份证、还车地点、停车场/维修站、到达时间、备胎照片与胎纹深度、里程/电量/氢量、接车服务费、还车检查单(可展开查看)\\n5.还车照片:左侧展示交车时照片(含“交车时照片”提示),右侧展示还车照片(只读可预览)\\n6.底部:返回按钮,返回还车管理页' + ) + ) + ); +}; + diff --git a/web端/运维管理/车辆业务/还车管理-还车.jsx b/web端/运维管理/车辆业务/还车管理-还车.jsx new file mode 100644 index 0000000..64aa868 --- /dev/null +++ b/web端/运维管理/车辆业务/还车管理-还车.jsx @@ -0,0 +1,1006 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 运维管理 - 车辆业务 - 还车管理 - 还车 + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + var useCallback = React.useCallback; + var useRef = React.useRef; + var useEffect = React.useEffect; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Input = antd.Input; + var Select = antd.Select; + var Switch = antd.Switch; + var DatePicker = antd.DatePicker; + var Table = antd.Table; + var Button = antd.Button; + var Drawer = antd.Drawer; + var Modal = antd.Modal; + var message = antd.message; + + var TextArea = Input.TextArea; + + // ---------- utils ---------- + function RequiredLabel(text) { + return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 4 } }, + React.createElement('span', { style: { color: '#f5222d', fontWeight: 600 } }, '*'), + React.createElement('span', null, text) + ); + } + + function isEmpty(v) { + return v === null || v === undefined || String(v).trim() === ''; + } + + function filterOption(input, option) { + var label = (option && (option.label || option.children)) || ''; + return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0; + } + + function fileToDataUrl(file, cb) { + try { + var reader = new FileReader(); + reader.onload = function (e) { cb(null, (e && e.target && e.target.result) || ''); }; + reader.onerror = function () { cb(new Error('read error')); }; + reader.readAsDataURL(file); + } catch (e) { + cb(e); + } + } + + function makeThumb(url, onPreview, onRemove, disabled) { + return React.createElement('div', { + style: { + width: 64, + height: 64, + borderRadius: 4, + border: '1px solid #f0f0f0', + background: '#fafafa', + position: 'relative', + overflow: 'hidden' + } + }, + React.createElement('img', { + src: url, + style: { width: '100%', height: '100%', objectFit: 'cover', cursor: disabled ? 'default' : 'pointer' }, + onClick: disabled ? undefined : onPreview + }), + (disabled || !onRemove) ? null : React.createElement('div', { + style: { + position: 'absolute', + right: 6, + top: 6, + width: 20, + height: 20, + borderRadius: 999, + background: 'rgba(0,0,0,0.55)', + color: '#fff', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + cursor: 'pointer', + fontSize: 12, + userSelect: 'none' + }, + onClick: function (e) { e.stopPropagation(); onRemove && onRemove(); } + }, '×') + ); + } + + function sanitize2(v) { + if (v === null || v === undefined) return ''; + var s = String(v); + if (s === '') return ''; + // 只保留数字和一个小数点 + if (!/^\d*\.?\d*$/.test(s)) return ''; + var parts = s.split('.'); + if (parts.length === 1) return parts[0]; + return parts[0] + '.' + (parts[1].slice(0, 2)); + } + + function UploadBox(props) { + var label = props.label; + var value = props.value || []; // array of {uid,name,url} + var max = props.max || 1; + var onChange = props.onChange; + var disabled = !!props.disabled; + var tip = props.tip; + + function handlePick(e) { + var f = e && e.target && e.target.files && e.target.files[0]; + if (!f) return; + fileToDataUrl(f, function (err, url) { + if (err) { message.error('上传失败(原型)'); return; } + var next = (value || []).slice(); + next.push({ uid: String(Date.now()) + '_' + Math.random(), name: f.name || 'image', url: url }); + if (next.length > max) next = next.slice(next.length - max); + onChange && onChange(next); + }); + e.target.value = ''; + } + + return React.createElement('div', null, + label ? React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, label) : null, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' } }, + (value || []).map(function (f) { + return React.createElement('div', { key: f.uid }, + makeThumb( + f.url, + function () { previewState[1]({ open: true, url: f.url, title: f.name || '预览' }); }, + function () { onChange && onChange((value || []).filter(function (x) { return x.uid !== f.uid; })); }, + disabled + ) + ); + }), + disabled ? null : ((value || []).length >= max ? null : React.createElement('label', { + style: { + width: 64, + height: 64, + borderRadius: 4, + border: '1px dashed #d9d9d9', + background: '#fff', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + color: '#999', + cursor: 'pointer' + } + }, + React.createElement('input', { type: 'file', accept: 'image/*', style: { display: 'none' }, onChange: handlePick }), + '上传' + )) + ), + tip ? React.createElement('div', { style: { marginTop: 6, fontSize: 12, color: '#999' } }, tip) : null + ); + } + + var layoutStyle = { padding: '16px 24px 88px', background: '#f5f5f5', minHeight: '100vh' }; + var cardStyle = { marginBottom: 16 }; + var footerStyle = { + position: 'fixed', + left: 0, + right: 0, + bottom: 0, + background: '#fff', + borderTop: '1px solid #f0f0f0', + padding: '12px 24px', + display: 'flex', + justifyContent: 'flex-start', + gap: 12, + zIndex: 10 + }; + + var styles = { + formRow: { display: 'flex', gap: 16, marginBottom: 12, alignItems: 'flex-start' }, + formItem: { flex: 1, minWidth: 0 }, + label: { fontSize: 12, color: '#666', marginBottom: 6 } + }; + + function FormItem(props) { + var label = props.label; + var required = !!props.required; + var fullWidth = !!props.fullWidth; + return React.createElement( + 'div', + { style: fullWidth ? Object.assign({}, styles.formItem, { flex: '0 0 100%' }) : styles.formItem }, + React.createElement('div', { style: styles.label }, required ? RequiredLabel(label) : label), + props.children + ); + } + + // ---------- mock data ---------- + var vehicleInfo = useMemo(function () { + return { + contractCode: 'HT-ZL-2025-001', + projectName: '嘉兴氢能示范项目', + customerName: '嘉兴某某物流有限公司', + businessDept: '华东业务部', + businessOwner: '李经理', + plateNo: '京A12345', + vehicleType: '重型厢式货车', + brand: '东风', + model: 'DFH1180', + deliveryTime: '2025-02-28 14:30', + deliveryRegion: '浙江省-嘉兴市', + deliveryAddress: '嘉兴市南湖区科技大道1号', + deliverySpareTireDepth: '6.50', + deliveryMileageKm: '15230.12', + deliveryBatteryKwh: '68.40', + deliveryHydrogenAmount: '28.30', + hydrogenUnit: 'MPa', + deliverySpareTirePhotoUrl: 'https://dummyimage.com/600x400/eee/666&text=交车备胎照片' + }; + }, []); + + var returnLocationOptions = useMemo(function () { + return { + parking: [ + { value: '停车场A', label: '停车场A' }, + { value: '停车场B', label: '停车场B' } + ], + repair: [ + { value: '维修站A', label: '维修站A' }, + { value: '维修站B', label: '维修站B' } + ] + }; + }, []); + + var deliveryDriver = useMemo(function () { + return { + name: '张三', + phone: '13800138001', + idCard: '320101199001018888' + }; + }, []); + + // 交车时照片(用于展示,仅查看) + var deliveryPhotos = useMemo(function () { + function u(txt) { return 'https://dummyimage.com/600x400/eee/666&text=' + encodeURIComponent(txt); } + return { + vehicle: { + '仪表盘': [{ uid: 'dv1', name: '仪表盘.jpg', url: u('交车-仪表盘') }], + '车辆正面': [{ uid: 'dv2', name: '车辆正面.jpg', url: u('交车-车辆正面') }], + '车辆左前方': [{ uid: 'dv3', name: '车辆左前方.jpg', url: u('交车-车辆左前方') }], + '车辆左后方': [{ uid: 'dv4', name: '车辆左后方.jpg', url: u('交车-车辆左后方') }], + '车辆右后方': [{ uid: 'dv5', name: '车辆右后方.jpg', url: u('交车-车辆右后方') }], + '车辆右前方': [{ uid: 'dv6', name: '车辆右前方.jpg', url: u('交车-车辆右前方') }] + }, + chassis: { + '正前方底部': [{ uid: 'dc1', name: '正前方底部.jpg', url: u('交车-正前方底部') }], + '左侧前方底部': [{ uid: 'dc2', name: '左侧前方底部.jpg', url: u('交车-左侧前方底部') }], + '左侧后方底部': [{ uid: 'dc3', name: '左侧后方底部.jpg', url: u('交车-左侧后方底部') }], + '正后方底部': [{ uid: 'dc4', name: '正后方底部.jpg', url: u('交车-正后方底部') }], + '右侧后方底部': [{ uid: 'dc5', name: '右侧后方底部.jpg', url: u('交车-右侧后方底部') }], + '右侧前方底部': [{ uid: 'dc6', name: '右侧前方底部.jpg', url: u('交车-右侧前方底部') }] + }, + tire: { + '左前轮': [{ uid: 'dt1', name: '左前轮.jpg', url: u('交车-左前轮') }], + '左后轮(内)': [{ uid: 'dt2', name: '左后轮(内).jpg', url: u('交车-左后轮(内)') }], + '左后轮(外)': [{ uid: 'dt3', name: '左后轮(外).jpg', url: u('交车-左后轮(外)') }], + '右前轮': [{ uid: 'dt4', name: '右前轮.jpg', url: u('交车-右前轮') }], + '右后轮(内)': [{ uid: 'dt5', name: '右后轮(内).jpg', url: u('交车-右后轮(内)') }], + '右后轮(外)': [{ uid: 'dt6', name: '右后轮(外).jpg', url: u('交车-右后轮(外)') }], + '备胎': [{ uid: 'dt7', name: '备胎.jpg', url: u('交车-备胎') }] + }, + defect: [ + { uid: 'dd1', name: '交车瑕疵1.jpg', url: u('交车-瑕疵1') }, + { uid: 'dd2', name: '交车瑕疵2.jpg', url: u('交车-瑕疵2') } + ], + other: [ + { uid: 'do1', name: '交车其他1.jpg', url: u('交车-其他1') }, + { uid: 'do2', name: '交车其他2.jpg', url: u('交车-其他2') } + ] + }; + }, []); + + // ---------- states ---------- + var previewState = useState({ open: false, url: '', title: '' }); + var ocrModalState = useState({ open: false, photoUrl: '', depth: '' }); + + var requirementModalOpenState = useState(false); + + var inspectionDrawerOpenState = useState(false); + + var [returnPhotos, setReturnPhotos] = useState(function () { + return { + vehicle: {}, + chassis: {}, + tire: {}, + defect: [], + other: [] + }; + }); + + // 初始化 returnPhotos 的各 slot + useEffect(function () { + setReturnPhotos(function (prev) { + var next = Object.assign({}, prev || {}); + ['vehicle', 'chassis', 'tire'].forEach(function (k) { + next[k] = Object.assign({}, next[k] || {}); + }); + Object.keys(deliveryPhotos.vehicle || {}).forEach(function (slot) { if (!next.vehicle[slot]) next.vehicle[slot] = []; }); + Object.keys(deliveryPhotos.chassis || {}).forEach(function (slot) { if (!next.chassis[slot]) next.chassis[slot] = []; }); + Object.keys(deliveryPhotos.tire || {}).forEach(function (slot) { if (!next.tire[slot]) next.tire[slot] = []; }); + return next; + }); + }, []); + + var [form, setForm] = useState(function () { + return { + // 还车人信息(默认来自交车司机) + returnPerson: deliveryDriver.name, + returnPhone: deliveryDriver.phone, + returnIdCard: deliveryDriver.idCard, + + // 还车地点 + returnPlaceType: undefined, // 'parking' | 'repair' + returnPlaceName: undefined, + arriveTime: '', + + // 备胎照片与胎纹深度 + spareTirePhoto: [], // 上传 + spareTireDepth: '', // OCR/手填 + + // 里程/电量/氢量 + returnMileageKm: '', + returnBatteryKwh: '', + returnHydrogenAmount: '', + hydrogenUnit: vehicleInfo.hydrogenUnit, + + // 接车服务费(可选) + serviceFee: '' + }; + }); + + function updateForm(patch) { + setForm(function (prev) { return Object.assign({}, prev, patch); }); + } + + function handleSpareTirePhotoChange(list) { + updateForm({ spareTirePhoto: list || [] }); + if ((list || []).length > 0) { + ocrModalState[1]({ open: true, photoUrl: list[0].url, depth: '6.50' }); + } + } + + function confirmOcr() { + updateForm({ spareTireDepth: ocrModalState[0].depth }); + ocrModalState[1](Object.assign({}, ocrModalState[0], { open: false })); + message.success('已反写还车备胎胎纹深度(原型)'); + } + + // ---------- inspection checklist ---------- + // 参照交车检查单生成“还车检查项”(原型:默认全开/正常) + var inspectionCategoryItems = useMemo(function () { + return { + '车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'], + '仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'], + '驾驶室': ['点烟器', '车窗升降', '按键开关', '雨刮器', '内后视镜是否正常', '内/外门把手', '安全带', '空调冷暖风', '仪表盘', '门锁功能', '手刹', '车钥匙功能是否正常', '喇叭', '音响功能', '遮阳板', '主副驾座椅', '方向盘', '内饰干净整洁'], + '轮胎': ['前左胎', '前右胎', '后左胎', '后右胎', '备胎'], + '液位检查': ['冷却液', '制动液', '玻璃水'], + '外观检查': ['车身外观', '漆面', '玻璃'], + '车辆外观': ['整车外观'], + '其他': ['其他检查项'], + '随车工具': ['三角牌', '灭火器', '反光背心'], + '随车证件': ['行驶证', '营运证', '保险单'], + '整车': ['整车状态'], + '燃料电池系统': ['氢系统', '储氢瓶'], + '冷机': ['冷机运行'], + '制动系统': ['制动踏板', '驻车制动'] + }; + }, []); + + function buildInspectionList() { + var list = []; + var categories = Object.keys(inspectionCategoryItems); + for (var i = 0; i < categories.length; i++) { + var cat = categories[i]; + var items = inspectionCategoryItems[cat] || []; + for (var j = 0; j < items.length; j++) { + var it = items[j]; + var isTire = cat === '轮胎'; + list.push({ + key: 'r-ins-' + i + '-' + j, + category: cat, + item: it, + checked: true, + treadDepth: isTire ? '6.50' : '', + remark: '' + }); + } + } + return list; + } + + var inspectionListState = useState(function () { return buildInspectionList(); }); + var inspectionList = inspectionListState[0]; + var setInspectionList = inspectionListState[1]; + var inspectionListRef = useRef(null); + inspectionListRef.current = inspectionList; + + function updateInspectionRow(key, patch) { + setInspectionList(function (prev) { + return (prev || []).map(function (r) { + if (r.key !== key) return r; + return Object.assign({}, r, patch); + }); + }); + } + + var inspectionColumns = useMemo(function () { + return [ + { + title: '类别', + dataIndex: 'category', + key: 'category', + width: 140, + render: function (text, record, index) { + var rows = inspectionListRef.current || []; + var cat = record && record.category; + if (!cat) return { children: text, props: { rowSpan: 1 } }; + + // 仅合并同类别的连续行 + var isFirst = true; + for (var i = index - 1; i >= 0; i--) { + if (!rows[i] || rows[i].category !== cat) break; + isFirst = false; + break; + } + if (!isFirst) return { children: null, props: { rowSpan: 0 } }; + + var span = 1; + for (var j = index + 1; j < rows.length; j++) { + if (!rows[j] || rows[j].category !== cat) break; + span++; + } + return { children: text, props: { rowSpan: span } }; + } + }, + { title: '检查项目', dataIndex: 'item', key: 'item', width: 220 }, + { + title: '检查情况', + dataIndex: 'checked', + key: 'checked', + width: 220, + render: function (_, record) { + var isTire = record && (record.category === '轮胎' || String(record.item || '').indexOf('胎纹') >= 0); + return isTire + ? React.createElement(Input, { + value: record.treadDepth, + placeholder: '请输入胎纹深度', + addonAfter: 'mm', + onChange: function (e) { updateInspectionRow(record.key, { treadDepth: e.target.value }); } + }) + : React.createElement(Switch, { + checked: !!record.checked, + onChange: function (v) { updateInspectionRow(record.key, { checked: !!v }); } + }); + } + }, + { + title: '备注', + dataIndex: 'remark', + key: 'remark', + render: function (_, record) { + return React.createElement(Input, { + value: record.remark, + placeholder: '请输入', + onChange: function (e) { updateInspectionRow(record.key, { remark: e.target.value }); } + }); + } + } + ]; + }, []); + + // ---------- validation & actions ---------- + function validateSubmit() { + if (isEmpty(form.returnPerson)) return '请填写还车人'; + if (isEmpty(form.returnPhone)) return '请填写还车人电话'; + if (isEmpty(form.returnIdCard)) return '请填写还车人身份证'; + if (!form.returnPlaceType) return '请选择还车地点'; + if (isEmpty(form.returnPlaceName)) return '请选择' + (form.returnPlaceType === 'parking' ? '停车场' : '维修站'); + if (isEmpty(form.arriveTime)) return '请选择到达时间'; + if (!form.spareTirePhoto || form.spareTirePhoto.length === 0) return '请上传备胎照片'; + if (isEmpty(form.spareTireDepth)) return '请填写还车时备胎胎纹深度'; + if (isEmpty(form.returnMileageKm)) return '请填写还车里程'; + if (isEmpty(form.returnBatteryKwh)) return '请填写还车电量'; + if (isEmpty(form.returnHydrogenAmount)) return '请填写还车氢量'; + return ''; + } + + function handleSubmit() { + var err = validateSubmit(); + if (err) { message.error(err); return; } + Modal.confirm({ + title: '确认还车', + content: '请确认信息填写无误,点击确认完成该车辆还车,并将该记录加入历史记录。', + okText: '确认', + cancelText: '取消', + onOk: function () { + message.success('还车成功(原型)'); + message.success('已加入历史记录(原型)'); + } + }); + } + + function handleSave() { + message.success('已暂存还车单(原型)'); + } + + function handleCancel() { + message.info('返回还车管理页(原型)'); + } + + // ---------- photo render helpers ---------- + function PreviewDeliveryThumb(props) { + var url = props.url; + if (!url) return React.createElement('div', { style: { width: 64, height: 64, borderRadius: 4, border: '1px solid #f0f0f0', background: '#fafafa' } }); + return makeThumb(url, + function () { previewState[1]({ open: true, url: url, title: props.title || '交车照片预览' }); }, + null, + false + ); + } + + function ReturnPhotoGridColumn(title, slots, required, deliveryMap, returnMap, onReturnMapChange) { + return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: 12 } }, + React.createElement('div', { style: { fontWeight: 600, marginBottom: 12 } }, required ? RequiredLabel(title) : title), + React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12 } }, + (slots || []).map(function (slot) { + var deliveryArr = (deliveryMap && deliveryMap[slot]) || []; + var deliveryUrl = deliveryArr && deliveryArr[0] ? deliveryArr[0].url : ''; + return React.createElement('div', { key: slot }, + React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, required ? RequiredLabel(slot) : slot), + React.createElement('div', { style: { display: 'flex', gap: 12, alignItems: 'flex-start', flexWrap: 'wrap' } }, + React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } }, + PreviewDeliveryThumb({ url: deliveryUrl, title: slot }), + React.createElement('div', { style: { fontSize: 11, color: '#999', marginTop: 6 } }, '交车时照片') + ), + React.createElement('div', { style: { flex: '1 1 120px', minWidth: 120 } }, + UploadBox({ + label: null, + value: (returnMap && returnMap[slot]) || [], + max: 1, + onChange: function (l) { + onReturnMapChange(slot, l || []); + } + }) + ) + ) + ); + }) + ) + ); + } + + // ---------- render ---------- + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { items: [{ title: '运维管理' }, { title: '车辆业务' }, { title: '还车管理' }] }), + React.createElement(Button, { type: 'link', onClick: function () { requirementModalOpenState[1](true); } }, '查看需求说明') + ), + + React.createElement(Card, { title: '合同信息', style: cardStyle }, + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '合同编码' }, + React.createElement(Input, { value: vehicleInfo.contractCode, disabled: true }) + ), + React.createElement(FormItem, { label: '项目名称' }, + React.createElement(Input, { value: vehicleInfo.projectName, disabled: true }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '客户名称' }, + React.createElement(Input, { value: vehicleInfo.customerName, disabled: true }) + ), + React.createElement(FormItem, { label: '业务部门' }, + React.createElement(Input, { value: vehicleInfo.businessDept, disabled: true }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '业务负责人', fullWidth: true }, + React.createElement(Input, { value: vehicleInfo.businessOwner, disabled: true }) + ) + ) + ), + + React.createElement(Card, { title: '车辆信息', style: cardStyle }, + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '车牌号' }, + React.createElement(Input, { value: vehicleInfo.plateNo, disabled: true }) + ), + React.createElement(FormItem, { label: '车辆型号' }, + React.createElement(Input, { value: vehicleInfo.vehicleType, disabled: true }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '品牌' }, + React.createElement(Input, { value: vehicleInfo.brand, disabled: true }) + ), + React.createElement(FormItem, { label: '型号' }, + React.createElement(Input, { value: vehicleInfo.model, disabled: true }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '交车时间' }, + React.createElement(Input, { value: vehicleInfo.deliveryTime, disabled: true, placeholder: '' }) + ), + React.createElement(FormItem, { label: '交车地点(省-市)' }, + React.createElement(Input, { value: vehicleInfo.deliveryRegion, disabled: true }) + ) + ) + ), + + React.createElement(Card, { title: '还车明细', style: cardStyle }, + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '还车人', required: true }, + React.createElement(Input, { value: form.returnPerson, placeholder: '请输入还车人', onChange: function (e) { updateForm({ returnPerson: e.target.value }); } }) + ), + React.createElement(FormItem, { label: '还车人电话', required: true }, + React.createElement(Input, { value: form.returnPhone, placeholder: '请输入还车人电话', onChange: function (e) { updateForm({ returnPhone: e.target.value }); } }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '还车人身份证', required: true }, + React.createElement(Input, { value: form.returnIdCard, placeholder: '请输入还车人身份证', onChange: function (e) { updateForm({ returnIdCard: e.target.value }); } }) + ), + React.createElement(FormItem, { label: '还车地点', required: true }, + React.createElement(Select, { + placeholder: '请选择还车地点', + value: form.returnPlaceType, + allowClear: true, + options: [ + { value: 'parking', label: '停车场' }, + { value: 'repair', label: '维修站' } + ], + onChange: function (v) { updateForm({ returnPlaceType: v, returnPlaceName: undefined }); } + }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { + label: form.returnPlaceType === 'parking' ? '停车场' : form.returnPlaceType === 'repair' ? '维修站' : '停车场/维修站', + required: true + }, + React.createElement(Select, { + placeholder: '请选择' + (form.returnPlaceType === 'parking' ? '停车场' : form.returnPlaceType === 'repair' ? '维修站' : '停车场或维修站'), + value: form.returnPlaceName, + allowClear: true, + showSearch: true, + filterOption: filterOption, + options: form.returnPlaceType === 'parking' ? returnLocationOptions.parking : (form.returnPlaceType === 'repair' ? returnLocationOptions.repair : []), + onChange: function (v) { updateForm({ returnPlaceName: v }); } + }) + ) + ), + + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '到达时间', required: true, fullWidth: true }, + React.createElement(DatePicker, { + showTime: { format: 'HH:mm' }, + format: 'YYYY-MM-DD HH:mm', + placeholder: '请选择到达时间', + style: { width: '100%' }, + value: (function () { + var dayjs = window.dayjs; + return form.arriveTime && dayjs ? dayjs(form.arriveTime) : null; + })(), + onChange: function (date, dateString) { + updateForm({ arriveTime: dateString || '' }); + } + }) + ) + ), + + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '备胎照片', required: true, fullWidth: true }, + React.createElement('div', { style: { display: 'flex', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' } }, + React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' } }, + makeThumb( + vehicleInfo.deliverySpareTirePhotoUrl, + function () { previewState[1]({ open: true, url: vehicleInfo.deliverySpareTirePhotoUrl, title: '交车时备胎照片' }); }, + null, + false + ), + React.createElement('div', { style: { fontSize: 11, color: '#999', marginTop: 6 } }, '交车时照片') + ), + React.createElement('div', { style: { flex: '1 1 240px', minWidth: 240 } }, + UploadBox({ + label: null, + value: form.spareTirePhoto, + max: 1, + onChange: function (l) { handleSpareTirePhotoChange(l); }, + tip: '上传后将自动识别胎纹深度(原型)' + }) + ) + ) + ) + ), + + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '交车时备胎胎纹深度', fullWidth: false }, + React.createElement(Input, { value: vehicleInfo.deliverySpareTireDepth, disabled: true, addonAfter: 'mm' }) + ), + React.createElement(FormItem, { label: '还车时备胎胎纹深度', required: true }, + React.createElement(Input, { + value: form.spareTireDepth, + placeholder: '请输入备胎胎纹深度', + addonAfter: 'mm', + onChange: function (e) { updateForm({ spareTireDepth: e.target.value }); } + }) + ) + ), + + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '交车里程(km)' }, + React.createElement(Input, { value: vehicleInfo.deliveryMileageKm, disabled: true, addonAfter: 'km' }) + ), + React.createElement(FormItem, { label: '还车里程(km)', required: true }, + React.createElement(Input, { + value: form.returnMileageKm, + placeholder: '请输入还车里程', + addonAfter: 'km', + onChange: function (e) { updateForm({ returnMileageKm: sanitize2(e.target.value) }); } + }) + ) + ), + + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '交车电量(kWh)' }, + React.createElement(Input, { value: vehicleInfo.deliveryBatteryKwh, disabled: true, addonAfter: 'kWh' }) + ), + React.createElement(FormItem, { label: '还车电量(kWh)', required: true }, + React.createElement(Input, { + value: form.returnBatteryKwh, + placeholder: '请输入还车电量', + addonAfter: 'kWh', + onChange: function (e) { updateForm({ returnBatteryKwh: sanitize2(e.target.value) }); } + }) + ) + ), + + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '交车氢量(' + form.hydrogenUnit + ')' }, + React.createElement(Input, { value: vehicleInfo.deliveryHydrogenAmount, disabled: true, addonAfter: form.hydrogenUnit }) + ), + React.createElement(FormItem, { label: '还车氢量(' + form.hydrogenUnit + ')', required: true }, + React.createElement(Input, { + value: form.returnHydrogenAmount, + placeholder: '请输入还车氢量', + addonAfter: form.hydrogenUnit, + onChange: function (e) { updateForm({ returnHydrogenAmount: sanitize2(e.target.value) }); } + }) + ) + ), + + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '接车服务费' }, + React.createElement(Input, { + value: form.serviceFee, + placeholder: '请输入接车服务费金额', + addonAfter: '元', + onChange: function (e) { updateForm({ serviceFee: sanitize2(e.target.value) }); } + }) + ) + ), + + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '还车检查单', fullWidth: true }, + React.createElement(Button, { onClick: function () { inspectionDrawerOpenState[1](true); } }, '还车检查单') + ) + ) + ), + + React.createElement(Card, { title: '还车照片', style: cardStyle }, + React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12 } }, + ReturnPhotoGridColumn( + '车辆', + Object.keys(deliveryPhotos.vehicle || {}), + true, + deliveryPhotos.vehicle, + returnPhotos.vehicle, + function (slot, list) { + setReturnPhotos(function (prev) { + var next = Object.assign({}, prev); + next.vehicle = Object.assign({}, prev.vehicle); + next.vehicle[slot] = list; + return next; + }); + } + ), + ReturnPhotoGridColumn( + '底盘', + Object.keys(deliveryPhotos.chassis || {}), + true, + deliveryPhotos.chassis, + returnPhotos.chassis, + function (slot, list) { + setReturnPhotos(function (prev) { + var next = Object.assign({}, prev); + next.chassis = Object.assign({}, prev.chassis); + next.chassis[slot] = list; + return next; + }); + } + ), + React.createElement('div', { style: { gridColumn: 'span 2' } }, + ReturnPhotoGridColumn( + '轮胎', + Object.keys(deliveryPhotos.tire || {}), + true, + deliveryPhotos.tire, + returnPhotos.tire, + function (slot, list) { + setReturnPhotos(function (prev) { + var next = Object.assign({}, prev); + next.tire = Object.assign({}, prev.tire); + next.tire[slot] = list; + return next; + }); + } + ) + ) + ), + React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12, marginTop: 12 } }, + (function () { + // 瑕疵:左侧展示交车时照片(仅查看),右侧上传还车照片(最多4张) + var left = (deliveryPhotos.defect || []).slice(0, 4); + return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: 12 } }, + React.createElement('div', { style: { fontWeight: 600, marginBottom: 12 } }, '瑕疵'), + React.createElement('div', { style: { display: 'flex', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' } }, + React.createElement('div', { style: { width: 260 } }, + React.createElement('div', { style: { display: 'flex', gap: 12, flexWrap: 'wrap' } }, left.map(function (f) { + return React.createElement('div', { key: f.uid }, + PreviewDeliveryThumb({ url: f.url, title: '交车瑕疵' }), + null + ); + })), + React.createElement('div', { style: { fontSize: 12, color: '#999', marginTop: 6 } }, '交车时照片') + ), + React.createElement('div', { style: { flex: '1 1 220px', minWidth: 220 } }, + React.createElement(UploadBox, { + label: null, + value: returnPhotos.defect, + max: 4, + onChange: function (l) { setReturnPhotos(function (prev) { return Object.assign({}, prev, { defect: l || [] }); }); }, + tip: '最多支持上传4张' + }) + ) + ) + ); + })(), + (function () { + // 其他:同瑕疵结构 + var left = (deliveryPhotos.other || []).slice(0, 4); + return React.createElement('div', { style: { background: '#fff', border: '1px solid #f0f0f0', borderRadius: 8, padding: 12 } }, + React.createElement('div', { style: { fontWeight: 600, marginBottom: 12 } }, '其他'), + React.createElement('div', { style: { display: 'flex', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' } }, + React.createElement('div', { style: { width: 260 } }, + React.createElement('div', { style: { display: 'flex', gap: 12, flexWrap: 'wrap' } }, left.map(function (f) { + return React.createElement('div', { key: f.uid }, + PreviewDeliveryThumb({ url: f.url, title: '交车其他' }) + ); + })), + React.createElement('div', { style: { fontSize: 12, color: '#999', marginTop: 6 } }, '交车时照片') + ), + React.createElement('div', { style: { flex: '1 1 220px', minWidth: 220 } }, + React.createElement(UploadBox, { + label: null, + value: returnPhotos.other, + max: 4, + onChange: function (l) { setReturnPhotos(function (prev) { return Object.assign({}, prev, { other: l || [] }); }); }, + tip: '最多支持上传4张' + }) + ) + ) + ); + })() + ) + ), + + React.createElement('div', { style: footerStyle }, + React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'), + React.createElement(Button, { onClick: handleSave }, '保存'), + React.createElement(Button, { onClick: handleCancel }, '取消') + ), + + // 预览 Modal + React.createElement(Modal, { + open: !!previewState[0].open, + title: previewState[0].title || '预览', + footer: null, + onCancel: function () { previewState[1]({ open: false, url: '', title: '' }); }, + width: 860 + }, + previewState[0].url ? React.createElement('img', { src: previewState[0].url, style: { width: '100%', maxHeight: '70vh', objectFit: 'contain' } }) : null + ), + + // OCR Modal(备胎胎纹深度识别) + React.createElement(Modal, { + open: !!ocrModalState[0].open, + title: '正在识别中(原型)', + onCancel: function () { ocrModalState[1](Object.assign({}, ocrModalState[0], { open: false })); }, + onOk: confirmOcr, + okText: '确认', + cancelText: '取消', + width: 860 + }, + React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 320px', gap: 16, alignItems: 'start' } }, + React.createElement('div', { style: { border: '1px solid #f0f0f0', borderRadius: 8, overflow: 'hidden', background: '#fafafa' } }, + ocrModalState[0].photoUrl ? React.createElement('img', { src: ocrModalState[0].photoUrl, style: { width: '100%', maxHeight: 420, objectFit: 'contain', display: 'block' } }) : null + ), + React.createElement('div', null, + React.createElement('div', { style: { fontSize: 12, color: '#666', marginBottom: 6 } }, '识别出的胎纹深度'), + React.createElement(Input, { + value: ocrModalState[0].depth, + addonAfter: 'mm', + onChange: function (e) { ocrModalState[1](Object.assign({}, ocrModalState[0], { depth: e.target.value })); } + }), + React.createElement('div', { style: { marginTop: 8, fontSize: 12, color: '#999', lineHeight: 1.7 } }, + '提示:识别中请勿关闭页面;点击确认后将反写至“还车时备胎胎纹深度”。' + ) + ) + ) + ), + + // 还车检查抽屉 + React.createElement(Drawer, { + open: inspectionDrawerOpenState[0], + title: '还车检查单', + width: 920, + placement: 'right', + onClose: function () { inspectionDrawerOpenState[1](false); }, + styles: { body: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', paddingBottom: 0 } }, + footer: React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-start', gap: 8 } }, + React.createElement(Button, { + type: 'primary', + onClick: function () { + message.success('提交成功(原型)'); + inspectionDrawerOpenState[1](false); + } + }, '提交'), + React.createElement(Button, { onClick: function () { inspectionDrawerOpenState[1](false); } }, '返回') + ) + }, + React.createElement('div', { style: { flex: 1, minHeight: 0, overflow: 'auto' } }, + React.createElement(Table, { rowKey: 'key', columns: inspectionColumns, dataSource: inspectionList, pagination: false, bordered: true, size: 'small' }) + ) + ), + + React.createElement(Modal, { + open: requirementModalOpenState[0], + onCancel: function () { requirementModalOpenState[1](false); }, + onOk: function () { requirementModalOpenState[1](false); }, + title: '需求说明', + width: 860, + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, + React.createElement('div', { style: { whiteSpace: 'pre-wrap', lineHeight: 1.7 } }, + '一个「数字化资产ONEOS运管平台」中的「还车管理」「还车」模块\\n' + + '1.面包屑:\\n' + + '1.1.运维管理-车辆业务-还车管理-还车\\n\\n' + + '2.合同信息:\\n' + + '2.1.合同编码:显示该车辆对应合同编码;\\n' + + '2.2.项目名称:显示该车辆对应项目名称;\\n' + + '2.3.客户名称:显示该车辆对应客户名称;\\n' + + '2.4.业务部门:显示该车辆合同对应业务部门;\\n' + + '2.5.业务负责人:显示该车辆合同对应业务负责人;\\n\\n' + + '3.车辆信息:\\n' + + '3.1.车牌号:显示该车辆车牌号;\\n' + + '3.2.车辆型号:显示该车辆型号;\\n' + + '3.3.品牌:显示该车辆品牌;\\n' + + '3.4.型号:显示该车辆型号;\\n' + + '3.5.交车时间:显示该车辆交车时间,格式为:YYYY-MM-DD HH:MM;\\n' + + '3.6.交车地点:显示该车辆交车地点,格式为:省-市;\\n\\n' + + '4.还车明细:\\n' + + '4.1.还车人:必填项,输入框,默认显示交车时司机姓名(从OCR自动识别身份证);\\n' + + '4.2.还车人电话:必填项,输入框,默认显示交车时司机手机号(从司机扫码培训时验证的手机号);\\n' + + '4.3.还车人身份证:必填项,输入框,默认显示还车时司机身份证号(从OCR自动识别身份证);\\n' + + '4.4.还车地点:必填项,选择器,选项为停车场、维修站,默认提示:请选择还车地点;\\n' + + '4.5.停车场/维修站:必填项,选择器,选择停车场或维修站;当还车地点为停车场时,字段名称为停车场,当还车地点为维修站时,字段名称为维修站;\\n' + + '4.6.到达时间:必填项,日期选择器,输入框单日历,支持选择年月日,精确到分钟,格式为:YYYY-MM-DD HH:MM;\\n' + + '4.7.备胎照片:左侧为交车时备胎照片(仅查看,支持点击预览),右侧为图片上传按钮,支持单张照片上传,上传后按钮变为图片,支持点击图片预览和右上角删除,图片上传后提示识别中,然后弹出卡片,左侧为图片,右侧为胎纹深度:输入框(自动反写胎纹深度),后缀为mm;\\n' + + '4.8.交车时备胎胎纹深度:输入框(禁用),显示交车时胎纹深度,后缀为mm;\\n' + + '4.9.还车时备胎胎纹深度:输入框,默认提示为请输入备胎胎纹深度,如上传备胎照片并确认后则自动反写OCR识别结果;\\n' + + '4.10.交车里程:输入框(禁用),显示交车时交车里程,后缀为km;\\n' + + '4.11.还车里程:必填项,输入框,支持2位小数输入,后缀为km;\\n' + + '4.12.交车电量:输入框(禁用),显示交车时交车电量,后缀为kWh;\\n' + + '4.13.还车电量:必填项,输入框,支持2位小数输入,后缀为kWh;\\n' + + '4.14.交车氢量:输入框(禁用),显示交车时交车氢量,后缀为MPa或%(根据车辆型号参数列表中仪表盘显示单位显示);\\n' + + '4.15.还车氢量:必填项,输入框,支持2位小数输入,后缀为MPa或%(根据车辆型号参数列表中仪表盘显示单位显示);\\n' + + '4.16.接车服务费:输入框,支持2位小数输入,后缀为元;\\n' + + '4.17.还车检查单:点击弹出抽屉,抽屉显示还车检查项(默认为交车时状态),下方为提交和返回按钮,参照交车管理-交车单-编辑中交车检查单;\\n\\n' + + '5.还车照片:参照交车管理-交车单-编辑中交车照片,只是所有上传按钮左侧增加交车照片显示(仅查看,支持点击预览);\\n\\n' + + '所有交车时照片下方增加文字提示:交车时照片\\n\\n' + + '4.底部为提交、取消按钮;\\n' + + ' 4.1.提交:点击进行二次确认,点击确认完成该车辆还车,并将该记录加入历史记录;\\n' + + ' 4.2.保存:点击暂存还车单(不做校验);\\n' + + ' 4.2.取消:点击返回还车管理页;' + ) + ) + ); +}; + diff --git a/web端/运维管理/车辆业务/还车管理.jsx b/web端/运维管理/车辆业务/还车管理.jsx new file mode 100644 index 0000000..34b9c3b --- /dev/null +++ b/web端/运维管理/车辆业务/还车管理.jsx @@ -0,0 +1,479 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 运维管理 - 车辆业务 - 还车管理 + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + var useCallback = React.useCallback; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Select = antd.Select; + var Button = antd.Button; + var Table = antd.Table; + var Tabs = antd.Tabs; + var Modal = antd.Modal; + var message = antd.message; + var App = antd.App; + + function filterOption(input, option) { + var label = (option && (option.label || option.children)) || ''; + return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0; + } + + var contractOptions = [ + { value: 'HT-ZL-2025-001', label: 'HT-ZL-2025-001' }, + { value: 'HT-ZL-2025-002', label: 'HT-ZL-2025-002' }, + { value: 'HT-ZL-2025-003', label: 'HT-ZL-2025-003' }, + { value: 'HT-ZL-2025-004', label: 'HT-ZL-2025-004' } + ]; + var projectOptions = [ + { value: '嘉兴氢能示范项目', label: '嘉兴氢能示范项目' }, + { value: '上海物流租赁项目', label: '上海物流租赁项目' }, + { value: '杭州城配租赁项目', label: '杭州城配租赁项目' } + ]; + var customerOptions = [ + { value: '嘉兴某某物流有限公司', label: '嘉兴某某物流有限公司' }, + { value: '上海某某运输公司', label: '上海某某运输公司' }, + { value: '杭州某某租赁有限公司', label: '杭州某某租赁有限公司' } + ]; + var plateOptions = [ + { value: '京A12345', label: '京A12345' }, + { value: '沪B20001', label: '沪B20001' }, + { value: '浙A88888', label: '浙A88888' }, + { value: '浙F66666', label: '浙F66666' } + ]; + var vehicleTypeOptions = [ + { value: '轻型厢式货车', label: '轻型厢式货车' }, + { value: '重型厢式货车', label: '重型厢式货车' }, + { value: '厢式货车', label: '厢式货车' }, + { value: '平板货车', label: '平板货车' }, + { value: '栏板货车', label: '栏板货车' }, + { value: '小型普通客车', label: '小型普通客车' } + ]; + var brandOptions = [ + { value: '东风', label: '东风' }, + { value: '福田', label: '福田' }, + { value: '江淮', label: '江淮' }, + { value: '重汽', label: '重汽' }, + { value: '陕汽', label: '陕汽' }, + { value: '苏龙', label: '苏龙' } + ]; + var modelOptions = [ + { value: 'DFH1180', label: 'DFH1180' }, + { value: 'BJ1180', label: 'BJ1180' }, + { value: 'HFC1180', label: 'HFC1180' }, + { value: 'ZZ1180', label: 'ZZ1180' }, + { value: 'KLQ6129', label: 'KLQ6129' } + ]; + + var defaultPending = [ + { id: 'p1', deliveryTime: '2025-02-20 09:30', deliveryPerson: '张三', plateNo: '京A12345', vehicleType: '重型厢式货车', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', contractCode: 'HT-ZL-2025-001', customerName: '嘉兴某某物流有限公司', projectName: '嘉兴氢能示范项目', dept: '华东业务部', bizOwner: '李经理', vehicleArrived: false }, + { id: 'p2', deliveryTime: '2025-02-21 14:00', deliveryPerson: '李四', plateNo: '沪B20001', vehicleType: '厢式货车', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', contractCode: 'HT-ZL-2025-002', customerName: '上海某某运输公司', projectName: '上海物流租赁项目', dept: '华东业务部', bizOwner: '王经理', vehicleArrived: true }, + { id: 'p3', deliveryTime: '2025-02-22 10:15', deliveryPerson: '王五', plateNo: '浙A88888', vehicleType: '轻型厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M8888888', contractCode: 'HT-ZL-2025-003', customerName: '杭州某某租赁有限公司', projectName: '杭州城配租赁项目', dept: '浙江业务部', bizOwner: '赵经理', vehicleArrived: false }, + { id: 'p4', deliveryTime: '2025-02-23 16:45', deliveryPerson: '张三', plateNo: '浙F66666', vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M7777777', contractCode: 'HT-ZL-2025-001', customerName: '嘉兴某某物流有限公司', projectName: '嘉兴氢能示范项目', dept: '华东业务部', bizOwner: '李经理', vehicleArrived: false } + ]; + + var defaultHistory = [ + { id: 'h1', deliveryTime: '2025-01-10 08:00', deliveryPerson: '张三', returnTime: '2025-02-01 17:30', plateNo: '京C11111', vehicleType: '重型厢式货车', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1111111', contractCode: 'HT-ZL-2024-001', customerName: '嘉兴某某物流有限公司', projectName: '嘉兴氢能示范项目', dept: '华东业务部', bizOwner: '李经理' }, + { id: 'h2', deliveryTime: '2025-01-15 11:20', deliveryPerson: '李四', returnTime: '2025-02-05 09:00', plateNo: '沪A10001', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M2222222', contractCode: 'HT-ZL-2024-002', customerName: '上海某某运输公司', projectName: '上海物流租赁项目', dept: '华东业务部', bizOwner: '王经理' }, + { id: 'h3', deliveryTime: '2025-01-20 13:00', deliveryPerson: '王五', returnTime: '2025-02-10 15:45', plateNo: '浙B99999', vehicleType: '平板货车', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M3333333', contractCode: 'HT-ZL-2024-003', customerName: '杭州某某租赁有限公司', projectName: '杭州城配租赁项目', dept: '浙江业务部', bizOwner: '赵经理' } + ]; + + var filterDraftState = useState({ + contractCode: undefined, + projectName: undefined, + customerName: undefined, + plateNo: undefined, + vehicleTypes: [], + brands: [], + model: undefined + }); + var fd = filterDraftState[0]; + var setFd = filterDraftState[1]; + + var appliedState = useState({ + contractCode: undefined, + projectName: undefined, + customerName: undefined, + plateNo: undefined, + vehicleTypes: [], + brands: [], + model: undefined + }); + var applied = appliedState[0]; + var setApplied = appliedState[1]; + + var pendingDataState = useState(defaultPending.slice()); + var pendingData = pendingDataState[0]; + var setPendingData = pendingDataState[1]; + + var historyDataState = useState(defaultHistory.slice()); + var historyData = historyDataState[0]; + + var tabState = useState('pending'); + var tab = tabState[0]; + var setTab = tabState[1]; + + var pageState = useState(1); + var page = pageState[0]; + var setPage = pageState[1]; + var pageSizeState = useState(10); + var pageSize = pageSizeState[0]; + var setPageSize = pageSizeState[1]; + + var filterExpandedState = useState(false); + var filterExpanded = filterExpandedState[0]; + var setFilterExpanded = filterExpandedState[1]; + var requirementModalVisibleState = useState(false); + var requirementModalVisible = requirementModalVisibleState[0]; + var setRequirementModalVisible = requirementModalVisibleState[1]; + + var handleQuery = useCallback(function () { + setApplied(Object.assign({}, fd)); + setPage(1); + message.success('已查询(原型)'); + }, [fd]); + + var handleReset = useCallback(function () { + var empty = { contractCode: undefined, projectName: undefined, customerName: undefined, plateNo: undefined, vehicleTypes: [], brands: [], model: undefined }; + setFd(empty); + setApplied(empty); + setPage(1); + }, []); + + function matchFilters(row) { + if (applied.contractCode && row.contractCode !== applied.contractCode) return false; + if (applied.projectName && row.projectName !== applied.projectName) return false; + if (applied.customerName && row.customerName !== applied.customerName) return false; + if (applied.plateNo && row.plateNo !== applied.plateNo) return false; + if (applied.vehicleTypes && applied.vehicleTypes.length > 0 && applied.vehicleTypes.indexOf(row.vehicleType) === -1) return false; + if (applied.brands && applied.brands.length > 0 && applied.brands.indexOf(row.brand) === -1) return false; + if (applied.model && row.model !== applied.model) return false; + return true; + } + + var filteredPending = useMemo(function () { + return pendingData.filter(matchFilters); + }, [pendingData, applied]); + + var filteredHistory = useMemo(function () { + return historyData.filter(matchFilters); + }, [historyData, applied]); + + var pagedPending = useMemo(function () { + var start = (page - 1) * pageSize; + return filteredPending.slice(start, start + pageSize); + }, [filteredPending, page, pageSize]); + + var pagedHistory = useMemo(function () { + var start = (page - 1) * pageSize; + return filteredHistory.slice(start, start + pageSize); + }, [filteredHistory, page, pageSize]); + + var handleVehicleArrived = useCallback(function (record) { + Modal.confirm({ + title: '确认', + content: '请确认车辆是否到达', + okText: '确认', + cancelText: '取消', + onOk: function () { + setPendingData(function (prev) { + return prev.map(function (r) { + if (r.id !== record.id) return r; + return Object.assign({}, r, { vehicleArrived: true }); + }); + }); + message.success('已确认车辆到达(原型)'); + } + }); + }, []); + + var handleReturnCar = useCallback(function () { + message.info('跳转还车管理-还车页(原型)'); + }, []); + + var handleViewHistory = useCallback(function () { + message.info('跳转还车管理-查看(原型)'); + }, []); + + var handleExport = useCallback(function () { + message.success('导出(原型)'); + }, []); + + var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var filterItemStyle = { marginBottom: 12 }; + var filterControlStyle = { width: '100%' }; + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + var tableSingleLineStyle = '.return-car-list-table .ant-table-thead th,.return-car-list-table .ant-table-tbody td{white-space:nowrap;}'; + var reqTitleStyle = { fontSize: 18, fontWeight: 600, marginBottom: 16, color: 'rgba(0,0,0,0.85)' }; + var reqSectionStyle = { fontSize: 15, fontWeight: 600, marginTop: 16, marginBottom: 8, color: 'rgba(0,0,0,0.85)' }; + var reqItemStyle = { fontSize: 13, marginLeft: 16, marginTop: 4, marginBottom: 2, lineHeight: 1.6, color: 'rgba(0,0,0,0.75)' }; + + var pendingColumns = [ + { title: '交车时间', dataIndex: 'deliveryTime', key: 'deliveryTime', width: 160 }, + { title: '交车人', dataIndex: 'deliveryPerson', key: 'deliveryPerson', width: 90 }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 }, + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 120, ellipsis: true }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 }, + { title: '型号', dataIndex: 'model', key: 'model', width: 110, ellipsis: true }, + { title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 160, ellipsis: true }, + { title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 130, ellipsis: true }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160, ellipsis: true }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 140, ellipsis: true }, + { title: '业务部门', dataIndex: 'dept', key: 'dept', width: 110, ellipsis: true }, + { title: '业务负责人', dataIndex: 'bizOwner', key: 'bizOwner', width: 100 }, + { + title: '操作', + key: 'action', + width: 160, + fixed: 'right', + render: function (_, record) { + return React.createElement('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } }, + record.vehicleArrived + ? React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 }, onClick: handleReturnCar }, '还车') + : React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 }, onClick: function () { handleVehicleArrived(record); } }, '车辆到达') + ); + } + } + ]; + + var historyColumns = [ + { title: '交车时间', dataIndex: 'deliveryTime', key: 'deliveryTime', width: 160 }, + { title: '交车人', dataIndex: 'deliveryPerson', key: 'deliveryPerson', width: 90 }, + { title: '还车时间', dataIndex: 'returnTime', key: 'returnTime', width: 160 }, + { title: '还车人', dataIndex: 'deliveryPerson', key: 'returnPersonAsDelivery', width: 90 }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 }, + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 120, ellipsis: true }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 }, + { title: '型号', dataIndex: 'model', key: 'model', width: 110, ellipsis: true }, + { title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 160, ellipsis: true }, + { title: '合同编码', dataIndex: 'contractCode', key: 'contractCode', width: 130, ellipsis: true }, + { title: '客户名称', dataIndex: 'customerName', key: 'customerName', width: 160, ellipsis: true }, + { title: '项目名称', dataIndex: 'projectName', key: 'projectName', width: 140, ellipsis: true }, + { title: '业务部门', dataIndex: 'dept', key: 'dept', width: 110, ellipsis: true }, + { title: '业务负责人', dataIndex: 'bizOwner', key: 'bizOwner', width: 100 }, + { + title: '操作', + key: 'action', + width: 80, + fixed: 'right', + render: function () { + return React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 }, onClick: handleViewHistory }, '查看'); + } + } + ]; + + var filterItems = [ + React.createElement('div', { key: 'contractCode', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '合同编码'), + React.createElement(Select, { + placeholder: '请选择或输入合同编码', + style: filterControlStyle, + value: fd.contractCode, + onChange: function (v) { setFd(function (p) { return Object.assign({}, p, { contractCode: v }); }); }, + allowClear: true, + showSearch: true, + options: contractOptions, + filterOption: filterOption + })), + React.createElement('div', { key: 'projectName', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '项目名称'), + React.createElement(Select, { + placeholder: '请选择或输入项目名称', + style: filterControlStyle, + value: fd.projectName, + onChange: function (v) { setFd(function (p) { return Object.assign({}, p, { projectName: v }); }); }, + allowClear: true, + showSearch: true, + options: projectOptions, + filterOption: filterOption + })), + React.createElement('div', { key: 'customerName', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '客户名称'), + React.createElement(Select, { + placeholder: '请选择或输入客户名称', + style: filterControlStyle, + value: fd.customerName, + onChange: function (v) { setFd(function (p) { return Object.assign({}, p, { customerName: v }); }); }, + allowClear: true, + showSearch: true, + options: customerOptions, + filterOption: filterOption + })), + React.createElement('div', { key: 'plateNo', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '车牌号'), + React.createElement(Select, { + placeholder: '请选择或输入车牌号', + style: filterControlStyle, + value: fd.plateNo, + onChange: function (v) { setFd(function (p) { return Object.assign({}, p, { plateNo: v }); }); }, + allowClear: true, + showSearch: true, + options: plateOptions, + filterOption: filterOption + })), + React.createElement('div', { key: 'vehicleTypes', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '车辆类型'), + React.createElement(Select, { + mode: 'multiple', + placeholder: '请选择(可多选)', + style: filterControlStyle, + value: fd.vehicleTypes, + onChange: function (v) { setFd(function (p) { return Object.assign({}, p, { vehicleTypes: v || [] }); }); }, + allowClear: true, + options: vehicleTypeOptions + })), + React.createElement('div', { key: 'brands', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '品牌'), + React.createElement(Select, { + mode: 'multiple', + placeholder: '请选择(可多选)', + style: filterControlStyle, + value: fd.brands, + onChange: function (v) { setFd(function (p) { return Object.assign({}, p, { brands: v || [] }); }); }, + allowClear: true, + options: brandOptions + })), + React.createElement('div', { key: 'model', style: filterItemStyle }, + React.createElement('div', { style: filterLabelStyle }, '型号'), + React.createElement(Select, { + placeholder: '请选择或输入型号', + style: filterControlStyle, + value: fd.model, + onChange: function (v) { setFd(function (p) { return Object.assign({}, p, { model: v }); }); }, + allowClear: true, + showSearch: true, + options: modelOptions, + filterOption: filterOption + })) + ]; + + var filterCount = filterExpanded ? 7 : 3; + var filterNodes = []; + for (var fi = 0; fi < filterCount && fi < filterItems.length; fi++) { + filterNodes.push(filterItems[fi]); + } + + var tabItems = [ + { + key: 'pending', + label: '待处理', + children: React.createElement(Table, { + rowKey: 'id', + columns: pendingColumns, + dataSource: pagedPending, + scroll: { x: 1680 }, + size: 'small', + pagination: { + current: page, + pageSize: pageSize, + total: filteredPending.length, + showSizeChanger: true, + showQuickJumper: true, + showTotal: function (t) { return '共 ' + t + ' 条'; }, + onChange: function (pg, ps) { setPage(pg); if (ps) setPageSize(ps); } + } + }) + }, + { + key: 'history', + label: '历史记录', + children: React.createElement(Table, { + rowKey: 'id', + columns: historyColumns, + dataSource: pagedHistory, + scroll: { x: 1880 }, + size: 'small', + pagination: { + current: page, + pageSize: pageSize, + total: filteredHistory.length, + showSizeChanger: true, + showQuickJumper: true, + showTotal: function (t) { return '共 ' + t + ' 条'; }, + onChange: function (pg, ps) { setPage(pg); if (ps) setPageSize(ps); } + } + }) + } + ]; + + return React.createElement(App, null, + React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { + items: [ + { title: '运维管理' }, + { title: '车辆业务' }, + { title: '还车管理' } + ] + }), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { setRequirementModalVisible(true); } }, '查看需求说明') + ), + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement('div', { + style: { + display: 'grid', + gridTemplateColumns: '1fr 1fr 1fr', + gap: '16px 24px', + alignItems: 'start', + flex: 1, + minWidth: 0 + } + }, filterNodes), + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { onClick: handleReset }, '重置'), + React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { setFilterExpanded(!filterExpanded); } }, filterExpanded ? '收起' : '展开') + ) + ), + React.createElement(Card, null, + React.createElement(React.Fragment, null, + React.createElement('style', null, tableSingleLineStyle), + React.createElement('div', { className: 'return-car-list-table' }, + React.createElement(Tabs, { + activeKey: tab, + onChange: function (k) { setTab(k); setPage(1); }, + tabBarExtraContent: React.createElement(Button, { onClick: handleExport }, '导出'), + items: tabItems + }) + ) + ) + ), + React.createElement(Modal, { + title: '需求说明', + open: requirementModalVisible, + onCancel: function () { setRequirementModalVisible(false); }, + width: 720, + footer: React.createElement(Button, { onClick: function () { setRequirementModalVisible(false); } }, '关闭'), + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, React.createElement('div', { style: { padding: '8px 0' } }, + React.createElement('div', { style: reqTitleStyle }, '还车管理'), + React.createElement('div', { style: { fontSize: 14, marginBottom: 12, color: 'rgba(0,0,0,0.85)' } }, '数字化资产 ONEOS 运管平台 · 运维管理 - 车辆业务 - 还车管理'), + React.createElement('div', { style: reqSectionStyle }, '筛选与列表'), + React.createElement('div', { style: reqItemStyle }, '面包屑:运维管理 - 车辆业务 - 还车管理;筛选区为三列栅格,默认一行(合同编码、项目名称、客户名称),展开后显示车牌号、车辆类型(多选)、品牌(多选)、型号;重置、查询、展开/收起与列表联动。'), + React.createElement('div', { style: reqItemStyle }, '列表分「待处理」「历史记录」Tab,Tab 右侧为导出;待处理支持车辆到达确认后显示还车;历史记录支持查看。') + )) + ) + ); +}; + +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)); + } + } +}