diff --git a/web端/.DS_Store b/web端/.DS_Store index 20ff8d6..f564d86 100644 Binary files a/web端/.DS_Store and b/web端/.DS_Store differ diff --git a/web端/业务管理/车辆成本维护.jsx b/web端/业务管理/车辆成本维护.jsx index 9ff7dc0..0ba1481 100644 --- a/web端/业务管理/车辆成本维护.jsx +++ b/web端/业务管理/车辆成本维护.jsx @@ -3,7 +3,6 @@ const Component = function () { var useState = React.useState; - var useMemo = React.useMemo; var antd = window.antd; var Breadcrumb = antd.Breadcrumb; @@ -13,11 +12,9 @@ const Component = function () { var Input = antd.Input; var Space = antd.Space; var Modal = antd.Modal; - var Cascader = antd.Cascader; var message = antd.message; var requirementModalVisible = useState(false); - var filterCascaderValue = useState(undefined); // [brand] 或 [brand, model] var listDataState = useState([ { id: 1, brand: '东风', model: '18吨双飞翼', rentalCostPerDay: 260.00, selfOperatedCostPerDay: 250.00 }, { id: 2, brand: '福田', model: '18吨厢式', rentalCostPerDay: 255.00, selfOperatedCostPerDay: 245.00 }, @@ -42,28 +39,6 @@ const Component = function () { var editSelfOperated = editSelfOperatedState[0]; var setEditSelfOperated = editSelfOperatedState[1]; - var cascaderOptions = useMemo(function () { - var map = {}; - listData.forEach(function (r) { - if (!r.brand) return; - if (!map[r.brand]) map[r.brand] = { value: r.brand, label: r.brand, children: [] }; - var children = map[r.brand].children; - var model = r.model || ''; - if (model && children.every(function (c) { return c.value !== model; })) { - children.push({ value: model, label: model }); - } - }); - return Object.keys(map).map(function (k) { return map[k]; }); - }, [listData]); - - var filteredList = useMemo(function () { - var val = filterCascaderValue[0]; - if (!val || !Array.isArray(val) || val.length === 0) return listData; - var brand = val[0]; - var model = val[1]; - if (model) return listData.filter(function (r) { return r.brand === brand && r.model === model; }); - return listData.filter(function (r) { return r.brand === brand; }); - }, [listData, filterCascaderValue[0]]); function fmtCost(v) { var n = typeof v === 'number' ? v : parseFloat(v); @@ -100,9 +75,6 @@ const Component = function () { var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; var cardStyle = { marginBottom: 16 }; - var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; - var filterItemStyle = { marginBottom: 12 }; - var filterControlStyle = { width: '100%', maxWidth: 320 }; var columns = [ { title: '序号', key: 'idx', width: 72, align: 'center', render: function (_, __, index) { return index + 1; } }, @@ -179,7 +151,7 @@ const Component = function () { } ]; - var requirementContent = '车辆成本维护(2026年3月12日版本)\n一个「数字化资产ONEOS运管平台」中的「车辆成本维护」模块\n#面包屑:业务管理-车辆成本维护;\n\n页面分为2个卡片;\n1.筛选:\n#支持车辆品牌、型号筛选;\n1.1.型号:级联选择器,支持品牌、型号级联;\n\n2.车辆成本列表:列表默认显示当前系统中所有品牌和型号记录,如型号参数表有新增,则该处同步新增一条数据;\n2.1.品牌:显示车辆品牌;\n2.2.型号:显示车辆型号(如18吨双飞翼,而不是公告型号);\n2.3.租赁车辆日成本:显示格式为:xx.xx元,支持两位小数;\n2.4.自营车辆日成本:显示格式为:xx.xx元,支持两位小数;\n2.5.操作:编辑;'; + var requirementContent = '车辆成本维护(2026年3月12日版本)\n一个「数字化资产ONEOS运管平台」中的「车辆成本维护」模块\n#面包屑:业务管理-车辆成本维护;\n\n页面为1个卡片:车辆成本列表。列表默认显示当前系统中所有品牌和型号记录,如型号参数表有新增,则该处同步新增一条数据;\n1.品牌:显示车辆品牌;\n2.型号:显示车辆型号(如18吨双飞翼,而不是公告型号);\n3.租赁车辆日成本:显示格式为:xx.xx元,支持两位小数;\n4.自营车辆日成本:显示格式为:xx.xx元,支持两位小数;\n5.操作:编辑;'; return React.createElement('div', { style: layoutStyle }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, @@ -191,31 +163,11 @@ const Component = function () { }), React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { requirementModalVisible[1](true); } }, '查看需求说明') ), - React.createElement(Card, { title: '筛选', style: cardStyle }, - React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'flex-end', gap: 16 } }, - React.createElement('div', { style: filterItemStyle }, - React.createElement('div', { style: filterLabelStyle }, '型号'), - React.createElement(Cascader, { - options: cascaderOptions, - value: filterCascaderValue[0], - onChange: function (v) { filterCascaderValue[1](v); }, - placeholder: '请选择品牌 / 型号', - allowClear: true, - style: filterControlStyle, - changeOnSelect: true - }) - ), - React.createElement('div', { style: { display: 'flex', gap: 8 } }, - React.createElement(Button, { onClick: function () { filterCascaderValue[1](undefined); } }, '重置'), - React.createElement(Button, { type: 'primary' }, '查询') - ) - ) - ), React.createElement(Card, { title: '车辆成本列表', style: cardStyle }, React.createElement(Table, { rowKey: 'id', columns: columns, - dataSource: filteredList, + dataSource: listData, pagination: { pageSize: 10, showSizeChanger: true, showTotal: function (t) { return '共 ' + t + ' 条'; } }, size: 'middle', bordered: true diff --git a/web端/加氢站管理/加氢订单.jsx b/web端/加氢站管理/加氢订单.jsx index c80d150..e5a9ab3 100644 --- a/web端/加氢站管理/加氢订单.jsx +++ b/web端/加氢站管理/加氢订单.jsx @@ -1,11 +1,12 @@ // 【重要】必须使用 const Component 作为组件变量名 -// 加氢记录(列表 + 筛选)(2026年3月版) +// 加氢站管理 - 加氢订单(列表 + 筛选)(2026年3月版) const Component = function () { var useState = React.useState; var useMemo = React.useMemo; var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; var Card = antd.Card; var Table = antd.Table; var Button = antd.Button; @@ -13,9 +14,9 @@ const Component = function () { var Input = antd.Input; var DatePicker = antd.DatePicker; var Space = antd.Space; + var Modal = antd.Modal; var message = antd.message; - var Option = Select.Option; var RangePicker = DatePicker.RangePicker || DatePicker.RangePicker; // 筛选状态 @@ -29,6 +30,8 @@ const Component = function () { var plateNo = plateNoState[0]; var setPlateNo = plateNoState[1]; + var requirementModalVisible = useState(false); + // 模拟:加氢站与加氢记录数据 var stationList = useMemo(function () { return [ @@ -97,28 +100,35 @@ const Component = function () { } function handleExport() { - // 实际项目中这里调用导出接口;此处仅做提示 - message.info('导出当前筛选条件下的加氢记录(示例)'); + message.info('导出当前筛选条件下的加氢订单(示例)'); } + var requirementContent = '加氢订单:一个「数字化资产ONEOS运管平台」中的「加氢站管理-加氢订单」模块。\n\n#1.面包屑:加氢站管理-加氢订单\n\n#2.筛选:支持加氢站、加氢时间、车牌号等筛选;\n2.1.加氢站:选择器,选择加氢站;\n2.2.加氢时间:日期时间范围选择器,支持开始时间-结束时间;\n2.3.车牌号:输入框,支持模糊匹配;\n右侧为查询、重置按钮。\n\n#3.列表:加氢订单列表,展示序号、加氢站、加氢时间、车牌号、加氢量(kg)、金额(元);支持分页与导出。'; + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement(Breadcrumb, { + items: [ + { title: '加氢站管理' }, + { title: '加氢订单' } + ] + }), + React.createElement(Button, { type: 'link', style: { padding: 0 }, onClick: function () { requirementModalVisible[1](true); } }, '查看需求说明') + ), React.createElement(Card, { title: '筛选', style: cardStyle }, React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px 24px', alignItems: 'flex-end' } }, React.createElement('div', { style: filterItemStyle }, - React.createElement('div', { style: filterLabelStyle }, '站点筛选'), + React.createElement('div', { style: filterLabelStyle }, '加氢站'), React.createElement(Select, { placeholder: '请选择加氢站', allowClear: true, style: { width: '100%' }, value: station, - onChange: function (v) { setStation(v); } - }, - stationList.map(function (s) { - return React.createElement(Option, { key: s.value, value: s.value }, s.label); - }) - ) + onChange: function (v) { setStation(v); }, + options: stationList + }) ), React.createElement('div', { style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '加氢时间'), @@ -139,15 +149,15 @@ const Component = function () { }) ) ), - React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', marginTop: 16 } }, - React.createElement(Space, null, - React.createElement(Button, { type: 'primary' }, '查询'), - React.createElement(Button, { onClick: handleReset }, '重置') - ), - React.createElement(Button, { onClick: handleExport }, '导出') + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 } }, + React.createElement(Button, { onClick: handleReset }, '重置'), + React.createElement(Button, { type: 'primary' }, '查询') ) ), - React.createElement(Card, { title: '加氢记录', style: cardStyle }, + React.createElement(Card, { title: '加氢订单列表', style: cardStyle }, + React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', marginBottom: 16 } }, + React.createElement(Button, { onClick: handleExport }, '导出') + ), React.createElement(Table, { rowKey: 'id', columns: columns, @@ -157,7 +167,15 @@ const Component = function () { size: 'middle', scroll: { x: 700 } }) - ) + ), + React.createElement(Modal, { + title: '需求说明', + open: requirementModalVisible[0], + onCancel: function () { requirementModalVisible[1](false); }, + width: 560, + footer: React.createElement(Button, { onClick: function () { requirementModalVisible[1](false); } }, '关闭'), + bodyStyle: { maxHeight: '70vh', overflow: 'auto' } + }, React.createElement('div', { style: { padding: '8px 0', whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6 } }, requirementContent)) ); }; diff --git a/web端/运维管理/基本数据维护/型号参数-新增.jsx b/web端/运维管理/基本数据维护/型号参数-新增.jsx new file mode 100644 index 0000000..98ccbc2 --- /dev/null +++ b/web端/运维管理/基本数据维护/型号参数-新增.jsx @@ -0,0 +1,309 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 基本数据维护 - 型号参数-新增(按图片设计,保养项操作列仅保留删除,无上移下移) + +const Component = function () { + var useState = React.useState; + var useCallback = React.useCallback; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Input = antd.Input; + var Select = antd.Select; + var Button = antd.Button; + var Radio = antd.Radio; + var Table = antd.Table; + var message = antd.message; + + // 基本情况 + var brand = useState(''); + var model = useState(''); + var vehicleType = useState(undefined); + var fuelType = useState(undefined); + var plateColor = useState('绿牌'); + var sizeLength = useState(''); + var sizeWidth = useState(''); + var sizeHeight = useState(''); + // 轮胎情况 + var tireCount = useState(''); + var tireSpec = useState(''); + // 电气情况 + var batteryType = useState('磷酸铁锂'); + var batteryVendor = useState(''); + var capacityKwh = useState(''); + var electricRange = useState(''); + // 供氢系统情况 + var cylinderCapacity = useState(''); + var gaugeMode = useState(undefined); + var hydrogenRange = useState(''); + var hydrogenVendor = useState(''); + // 其他系统情况 + var coldMachineVendor = useState(''); + var stackVendor = useState(''); + // 保养项列表(无上移下移,操作列仅删除) + var maintenanceList = useState([{ key: '1', item: '', kmCycle: '', monthCycle: '', laborCost: '', materialCost: '', total: '' }]); + var formErrors = useState({}); + + // 车辆类型选项与备车管理.jsx 保持一致 + var vehicleTypeOptions = [ + { value: '轻型厢式货车', label: '轻型厢式货车' }, + { value: '重型厢式货车', label: '重型厢式货车' }, + { value: '重型半挂牵引车', label: '重型半挂牵引车' }, + { value: '重型集装箱半挂车', label: '重型集装箱半挂车' }, + { value: '小型普通客车', label: '小型普通客车' }, + { value: '重型平板半挂车', label: '重型平板半挂车' }, + { value: '叉车', label: '叉车' }, + { value: '油车', label: '油车' }, + { value: '观光车', label: '观光车' } + ]; + var fuelTypeOptions = [ + { value: '氢', label: '氢' }, + { value: '电', label: '电' }, + { value: '油', label: '油' } + ]; + var gaugeModeOptions = [ + { value: 'MPa', label: 'MPa' }, + { value: 'bar', label: 'bar' } + ]; + + function calcTotal(labor, material) { + var l = parseFloat(labor); + var m = parseFloat(material); + if (isNaN(l)) l = 0; + if (isNaN(m)) m = 0; + return (l + m).toFixed(2); + } + + function updateMaintenance(index, field, value) { + maintenanceList[1](function (prev) { + var list = prev.slice(); + var row = Object.assign({}, list[index] || {}); + row[field] = value; + if (field === 'laborCost' || field === 'materialCost') { + row.total = calcTotal(row.laborCost, row.materialCost); + } + list[index] = row; + return list; + }); + } + + function addMaintenanceRow() { + maintenanceList[1](function (prev) { + var key = String((prev.length + 1) + Date.now()); + return prev.concat([{ key: key, item: '', kmCycle: '', monthCycle: '', laborCost: '', materialCost: '', total: '' }]); + }); + } + + function removeMaintenanceRow(index) { + maintenanceList[1](function (prev) { + var list = prev.slice(); + list.splice(index, 1); + if (list.length === 0) list = [{ key: '1', item: '', kmCycle: '', monthCycle: '', laborCost: '', materialCost: '', total: '' }]; + return list; + }); + } + + function validate() { + var err = {}; + if (!String(brand[0] || '').trim()) err.brand = '请输入品牌名称'; + if (!String(model[0] || '').trim()) err.model = '请输入型号名称'; + if (vehicleType[0] == null || vehicleType[0] === '') err.vehicleType = '请选择车辆类型'; + formErrors[1](err); + return Object.keys(err).length === 0; + } + + function handleSubmit() { + if (!validate()) { + message.warning('请完善必填项'); + return; + } + message.success('提交成功(原型)'); + } + + function handleCancel() { + if (window.history && window.history.back) window.history.back(); + else message.info('取消'); + } + + var layoutStyle = { padding: '16px 24px 80px', backgroundColor: '#f5f5f5', minHeight: '100vh' }; + var cardStyle = { marginBottom: 16 }; + var formRowStyle = { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }; + var formColStyle = { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8, boxSizing: 'border-box' }; + var formColFullStyle = { flex: '0 0 100%', marginBottom: 8 }; + var labelStyle = { display: 'block', marginBottom: 6, color: 'rgba(0,0,0,0.85)' }; + var labelRequiredStyle = { color: '#ff4d4f', marginRight: 4 }; + var errMsgStyle = { color: '#ff4d4f', fontSize: 12, marginTop: 4 }; + var inputStyle = { width: '100%' }; + var footerStyle = { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, zIndex: 99 }; + var cardTitleBarStyle = { display: 'flex', alignItems: 'center', justifyContent: 'flex-start', width: '100%', fontSize: 15, fontWeight: 500, lineHeight: 1.5 }; + var cardTitleBarLineStyle = { width: 4, height: 16, backgroundColor: '#1890ff', borderRadius: 2, marginRight: 8, flexShrink: 0 }; + + function FormItem(props) { + var colStyle = props.fullWidth ? formColFullStyle : formColStyle; + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: labelStyle }, + props.required ? React.createElement('span', { style: labelRequiredStyle }, '*') : null, + props.label + ), + props.children, + props.error ? React.createElement('div', { style: errMsgStyle }, props.error) : null + ); + } + + function CardTitleWithBar(title) { + return React.createElement('div', { style: cardTitleBarStyle }, + React.createElement('span', { style: cardTitleBarLineStyle }), + React.createElement('span', null, title) + ); + } + + var maintenanceColumns = [ + { title: '养护项目', dataIndex: 'item', key: 'item', width: 160, render: function (v, record, i) { return React.createElement(Input, { placeholder: '请输入', value: v, onChange: function (e) { updateMaintenance(i, 'item', e.target.value); }, size: 'small' }); } }, + { title: '保养公里周期(KM)', dataIndex: 'kmCycle', key: 'kmCycle', width: 140, render: function (v, record, i) { return React.createElement(Input, { placeholder: '请输入', value: v, onChange: function (e) { updateMaintenance(i, 'kmCycle', e.target.value); }, size: 'small' }); } }, + { title: '保养时间周期(月)', dataIndex: 'monthCycle', key: 'monthCycle', width: 140, render: function (v, record, i) { return React.createElement(Input, { placeholder: '请输入', value: v, onChange: function (e) { updateMaintenance(i, 'monthCycle', e.target.value); }, size: 'small' }); } }, + { title: '工时费(元)', dataIndex: 'laborCost', key: 'laborCost', width: 120, render: function (v, record, i) { return React.createElement(Input, { placeholder: '请输入', value: v, onChange: function (e) { updateMaintenance(i, 'laborCost', e.target.value); }, size: 'small' }); } }, + { title: '材料费(元)', dataIndex: 'materialCost', key: 'materialCost', width: 120, render: function (v, record, i) { return React.createElement(Input, { placeholder: '请输入', value: v, onChange: function (e) { updateMaintenance(i, 'materialCost', e.target.value); }, size: 'small' }); } }, + { title: '合计', dataIndex: 'total', key: 'total', width: 100, render: function (v) { return v || '—'; } }, + { + title: '操作', + key: 'action', + width: 80, + render: function (_, record, i) { + return React.createElement(Button, { type: 'link', danger: true, size: 'small', onClick: function () { removeMaintenanceRow(i); } }, '删除'); + } + } + ]; + + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement(Breadcrumb, { + items: [ + { title: '基本数据维护' }, + { title: '型号参数' }, + { title: '新增型号参数' } + ] + }) + ), + React.createElement(Card, { title: CardTitleWithBar('型号参数'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '品牌', required: true, error: formErrors[0].brand }, + React.createElement(Input, { placeholder: '请输入品牌名称', value: brand[0], onChange: function (e) { brand[1](e.target.value); }, style: inputStyle, status: formErrors[0].brand ? 'error' : undefined }) + ), + React.createElement(FormItem, { label: '型号', required: true, error: formErrors[0].model }, + React.createElement(Input, { placeholder: '请输入型号名称', value: model[0], onChange: function (e) { model[1](e.target.value); }, style: inputStyle, status: formErrors[0].model ? 'error' : undefined }) + ), + React.createElement(FormItem, { label: '车辆类型', required: true, error: formErrors[0].vehicleType }, + React.createElement(Select, { placeholder: '请选择车辆类型', style: inputStyle, value: vehicleType[0], onChange: function (v) { vehicleType[1](v); }, allowClear: true, options: vehicleTypeOptions, status: formErrors[0].vehicleType ? 'error' : undefined }) + ), + React.createElement(FormItem, { label: '燃料种类' }, + React.createElement(Select, { placeholder: '请选择燃料种类', style: inputStyle, value: fuelType[0], onChange: function (v) { fuelType[1](v); }, allowClear: true, options: fuelTypeOptions }) + ), + React.createElement(FormItem, { label: '车厢颜色' }, + React.createElement(Radio.Group, { value: plateColor[0], onChange: function (e) { plateColor[1](e.target.value); } }, + React.createElement(Radio, { value: '绿牌' }, '绿牌'), + React.createElement(Radio, { value: '黄牌' }, '黄牌'), + React.createElement(Radio, { value: '黄绿牌' }, '黄绿牌') + ) + ), + React.createElement(FormItem, { label: '整车尺寸', fullWidth: true }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, + React.createElement(Input, { placeholder: '长', value: sizeLength[0], onChange: function (e) { sizeLength[1](e.target.value); }, style: { flex: 1, minWidth: 0 } }), + React.createElement('span', { style: { flexShrink: 0 } }, '×'), + React.createElement(Input, { placeholder: '宽', value: sizeWidth[0], onChange: function (e) { sizeWidth[1](e.target.value); }, style: { flex: 1, minWidth: 0 } }), + React.createElement('span', { style: { flexShrink: 0 } }, '×'), + React.createElement(Input, { placeholder: '高', value: sizeHeight[0], onChange: function (e) { sizeHeight[1](e.target.value); }, style: { flex: 1, minWidth: 0 } }) + ) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('轮胎情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '轮胎数量' }, + React.createElement(Input, { placeholder: '请输入轮胎数量', value: tireCount[0], onChange: function (e) { tireCount[1](e.target.value); }, style: inputStyle, addonAfter: '条' }) + ), + React.createElement(FormItem, { label: '轮胎规格' }, + React.createElement(Input, { placeholder: '请输入轮胎规格', value: tireSpec[0], onChange: function (e) { tireSpec[1](e.target.value); }, style: inputStyle }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('电气情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '电池类型' }, + React.createElement(Radio.Group, { value: batteryType[0], onChange: function (e) { batteryType[1](e.target.value); } }, + React.createElement(Radio, { value: '磷酸铁锂' }, '磷酸铁锂'), + React.createElement(Radio, { value: '三元锂' }, '三元锂') + ) + ), + React.createElement(FormItem, { label: '电池厂家' }, + React.createElement(Input, { placeholder: '请输入电池厂商名称', value: batteryVendor[0], onChange: function (e) { batteryVendor[1](e.target.value); }, style: inputStyle }) + ), + React.createElement(FormItem, { label: '储电量' }, + React.createElement(Input, { placeholder: '请输入储电量', value: capacityKwh[0], onChange: function (e) { capacityKwh[1](e.target.value); }, style: inputStyle, addonAfter: 'kWh' }) + ), + React.createElement(FormItem, { label: '电续航里程' }, + React.createElement(Input, { placeholder: '请输入电续航里程', value: electricRange[0], onChange: function (e) { electricRange[1](e.target.value); }, style: inputStyle, addonAfter: 'KM' }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('供氢系统情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '氢瓶容量' }, + React.createElement(Input, { placeholder: '请输入氢瓶容量', value: cylinderCapacity[0], onChange: function (e) { cylinderCapacity[1](e.target.value); }, style: inputStyle, addonAfter: 'L' }) + ), + React.createElement(FormItem, { label: '仪表盘显示模式' }, + React.createElement(Select, { placeholder: '请选择仪表盘显示模式', style: inputStyle, value: gaugeMode[0], onChange: function (v) { gaugeMode[1](v); }, allowClear: true, options: gaugeModeOptions }) + ), + React.createElement(FormItem, { label: '氢续航里程' }, + React.createElement(Input, { placeholder: '请输入氢续航里程', value: hydrogenRange[0], onChange: function (e) { hydrogenRange[1](e.target.value); }, style: inputStyle, addonAfter: 'KM' }) + ), + React.createElement(FormItem, { label: '供氢系统厂家' }, + React.createElement(Input, { placeholder: '请输入供氢系统厂家', value: hydrogenVendor[0], onChange: function (e) { hydrogenVendor[1](e.target.value); }, style: inputStyle }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('其他系统情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '冷机厂家' }, + React.createElement(Input, { placeholder: '请输入冷机厂家名称', value: coldMachineVendor[0], onChange: function (e) { coldMachineVendor[1](e.target.value); }, style: inputStyle }) + ), + React.createElement(FormItem, { label: '电堆厂家' }, + React.createElement(Input, { placeholder: '请输入电堆厂家名称', value: stackVendor[0], onChange: function (e) { stackVendor[1](e.target.value); }, style: inputStyle }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('保养项情况'), size: 'small', style: cardStyle }, + React.createElement(Table, { + columns: maintenanceColumns, + dataSource: maintenanceList[0], + rowKey: 'key', + pagination: false, + size: 'small', + bordered: true + }), + React.createElement(Button, { type: 'dashed', style: { width: '100%', marginTop: 8 }, onClick: addMaintenanceRow }, '+ 添加一项') + ), + React.createElement('div', { style: footerStyle }, + React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'), + React.createElement(Button, { onClick: handleCancel }, '取消') + ) + ); +}; + +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)); + } + } +} diff --git a/web端/运维管理/基本数据维护/型号参数-查看.jsx b/web端/运维管理/基本数据维护/型号参数-查看.jsx new file mode 100644 index 0000000..fe3ed25 --- /dev/null +++ b/web端/运维管理/基本数据维护/型号参数-查看.jsx @@ -0,0 +1,216 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 基本数据维护 - 型号参数-查看(布局同型号参数-新增,全部只读) + +const Component = function () { + var useState = React.useState; + var useMemo = React.useMemo; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Input = antd.Input; + var Select = antd.Select; + var Button = antd.Button; + var Radio = antd.Radio; + var Table = antd.Table; + + // 只读数据(示例,与车辆管理-查看型号参数一致) + var detail = useMemo(function () { + return { + brand: '苏龙', + model: '海格牌KLQ5180XYKFCEV', + vehicleType: '18吨双飞翼货车', + fuelType: '氢', + plateColor: '绿牌', + sizeLength: '5995', + sizeWidth: '2145', + sizeHeight: '3130', + tireCount: '8', + tireSpec: '15/80R22.5', + batteryType: '磷酸铁锂', + batteryVendor: '某某电池企业', + capacityKwh: '100000', + electricRange: '200', + cylinderCapacity: '140', + gaugeMode: 'MPa', + hydrogenRange: '1000', + hydrogenVendor: '某某供氢系统科技有限公司', + coldMachineVendor: '某某冷机企业', + stackVendor: '某某电堆企业', + maintenanceList: [ + { key: '1', item: '变速器油', kmCycle: '60000', monthCycle: '24', laborCost: '0', materialCost: '571', total: '571' }, + { key: '2', item: '机油', kmCycle: '50000', monthCycle: '12', laborCost: '100', materialCost: '300', total: '400' } + ] + }; + }, []); + + var layoutStyle = { padding: '16px 24px 80px', backgroundColor: '#f5f5f5', minHeight: '100vh' }; + var cardStyle = { marginBottom: 16 }; + var formRowStyle = { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }; + var formColStyle = { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8, boxSizing: 'border-box' }; + var formColFullStyle = { flex: '0 0 100%', marginBottom: 8 }; + var labelStyle = { display: 'block', marginBottom: 6, color: 'rgba(0,0,0,0.85)' }; + var inputStyle = { width: '100%', backgroundColor: '#f5f5f5' }; + var footerStyle = { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, zIndex: 99 }; + var cardTitleBarStyle = { display: 'flex', alignItems: 'center', justifyContent: 'flex-start', width: '100%', fontSize: 15, fontWeight: 500, lineHeight: 1.5 }; + var cardTitleBarLineStyle = { width: 4, height: 16, backgroundColor: '#1890ff', borderRadius: 2, marginRight: 8, flexShrink: 0 }; + + function FormItem(props) { + var colStyle = props.fullWidth ? formColFullStyle : formColStyle; + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: labelStyle }, props.label), + props.children + ); + } + + function CardTitleWithBar(title) { + return React.createElement('div', { style: cardTitleBarStyle }, + React.createElement('span', { style: cardTitleBarLineStyle }), + React.createElement('span', null, title) + ); + } + + var maintenanceColumns = [ + { title: '养护项目', dataIndex: 'item', key: 'item', width: 160, render: function (v) { return v || '—'; } }, + { title: '保养公里周期(KM)', dataIndex: 'kmCycle', key: 'kmCycle', width: 140, render: function (v) { return v || '—'; } }, + { title: '保养时间周期(月)', dataIndex: 'monthCycle', key: 'monthCycle', width: 140, render: function (v) { return v || '—'; } }, + { title: '工时费(元)', dataIndex: 'laborCost', key: 'laborCost', width: 120, render: function (v) { return v || '—'; } }, + { title: '材料费(元)', dataIndex: 'materialCost', key: 'materialCost', width: 120, render: function (v) { return v || '—'; } }, + { title: '合计', dataIndex: 'total', key: 'total', width: 100, render: function (v) { return v || '—'; } } + ]; + + function handleBack() { + if (window.history && window.history.back) window.history.back(); + } + + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement(Breadcrumb, { + items: [ + { title: '基本数据维护' }, + { title: '型号参数' }, + { title: '查看' } + ] + }) + ), + React.createElement(Card, { title: CardTitleWithBar('型号参数'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '品牌' }, + React.createElement(Input, { value: detail.brand, disabled: true, style: inputStyle }) + ), + React.createElement(FormItem, { label: '型号' }, + React.createElement(Input, { value: detail.model, disabled: true, style: inputStyle }) + ), + React.createElement(FormItem, { label: '车辆类型' }, + React.createElement(Input, { value: detail.vehicleType, disabled: true, style: inputStyle }) + ), + React.createElement(FormItem, { label: '燃料种类' }, + React.createElement(Input, { value: detail.fuelType, disabled: true, style: inputStyle }) + ), + React.createElement(FormItem, { label: '车厢颜色' }, + React.createElement(Radio.Group, { value: detail.plateColor, disabled: true }, + React.createElement(Radio, { value: '绿牌' }, '绿牌'), + React.createElement(Radio, { value: '黄牌' }, '黄牌'), + React.createElement(Radio, { value: '黄绿牌' }, '黄绿牌') + ) + ), + React.createElement(FormItem, { label: '整车尺寸', fullWidth: true }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, + React.createElement(Input, { value: detail.sizeLength, disabled: true, style: { flex: 1, minWidth: 0, backgroundColor: '#f5f5f5' } }), + React.createElement('span', { style: { flexShrink: 0 } }, '×'), + React.createElement(Input, { value: detail.sizeWidth, disabled: true, style: { flex: 1, minWidth: 0, backgroundColor: '#f5f5f5' } }), + React.createElement('span', { style: { flexShrink: 0 } }, '×'), + React.createElement(Input, { value: detail.sizeHeight, disabled: true, style: { flex: 1, minWidth: 0, backgroundColor: '#f5f5f5' } }) + ) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('轮胎情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '轮胎数量' }, + React.createElement(Input, { value: detail.tireCount, disabled: true, style: inputStyle, addonAfter: '条' }) + ), + React.createElement(FormItem, { label: '轮胎规格' }, + React.createElement(Input, { value: detail.tireSpec, disabled: true, style: inputStyle }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('电气情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '电池类型' }, + React.createElement(Radio.Group, { value: detail.batteryType, disabled: true }, + React.createElement(Radio, { value: '磷酸铁锂' }, '磷酸铁锂'), + React.createElement(Radio, { value: '三元锂' }, '三元锂') + ) + ), + React.createElement(FormItem, { label: '电池厂家' }, + React.createElement(Input, { value: detail.batteryVendor, disabled: true, style: inputStyle }) + ), + React.createElement(FormItem, { label: '储电量' }, + React.createElement(Input, { value: detail.capacityKwh, disabled: true, style: inputStyle, addonAfter: 'kWh' }) + ), + React.createElement(FormItem, { label: '电续航里程' }, + React.createElement(Input, { value: detail.electricRange, disabled: true, style: inputStyle, addonAfter: 'KM' }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('供氢系统情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '氢瓶容量' }, + React.createElement(Input, { value: detail.cylinderCapacity, disabled: true, style: inputStyle, addonAfter: 'L' }) + ), + React.createElement(FormItem, { label: '仪表盘显示模式' }, + React.createElement(Input, { value: detail.gaugeMode, disabled: true, style: inputStyle }) + ), + React.createElement(FormItem, { label: '氢续航里程' }, + React.createElement(Input, { value: detail.hydrogenRange, disabled: true, style: inputStyle, addonAfter: 'KM' }) + ), + React.createElement(FormItem, { label: '供氢系统厂家' }, + React.createElement(Input, { value: detail.hydrogenVendor, disabled: true, style: inputStyle }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('其他系统情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '冷机厂家' }, + React.createElement(Input, { value: detail.coldMachineVendor, disabled: true, style: inputStyle }) + ), + React.createElement(FormItem, { label: '电堆厂家' }, + React.createElement(Input, { value: detail.stackVendor, disabled: true, style: inputStyle }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('保养项情况'), size: 'small', style: cardStyle }, + React.createElement(Table, { + columns: maintenanceColumns, + dataSource: detail.maintenanceList, + rowKey: 'key', + pagination: false, + size: 'small', + bordered: true + }) + ), + React.createElement('div', { style: footerStyle }, + React.createElement(Button, { onClick: handleBack }, '返回') + ) + ); +}; + +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)); + } + } +} diff --git a/web端/运维管理/基本数据维护/型号参数-编辑.jsx b/web端/运维管理/基本数据维护/型号参数-编辑.jsx new file mode 100644 index 0000000..9f39cdd --- /dev/null +++ b/web端/运维管理/基本数据维护/型号参数-编辑.jsx @@ -0,0 +1,337 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 基本数据维护 - 型号参数-编辑(布局与型号参数-新增一致,全部可修改,预填当前记录) + +const Component = function () { + var useState = React.useState; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Input = antd.Input; + var Select = antd.Select; + var Button = antd.Button; + var Radio = antd.Radio; + var Table = antd.Table; + var message = antd.message; + + // 初始数据(编辑时带入,与查看页一致;实际可从路由/接口获取) + var initialData = { + brand: '苏龙', + model: '海格牌KLQ5180XYKFCEV', + vehicleType: '18吨双飞翼货车', + fuelType: '氢', + plateColor: '绿牌', + sizeLength: '5995', + sizeWidth: '2145', + sizeHeight: '3130', + tireCount: '8', + tireSpec: '15/80R22.5', + batteryType: '磷酸铁锂', + batteryVendor: '某某电池企业', + capacityKwh: '100000', + electricRange: '200', + cylinderCapacity: '140', + gaugeMode: 'MPa', + hydrogenRange: '1000', + hydrogenVendor: '某某供氢系统科技有限公司', + coldMachineVendor: '某某冷机企业', + stackVendor: '某某电堆企业', + maintenanceList: [ + { key: '1', item: '变速器油', kmCycle: '60000', monthCycle: '24', laborCost: '0', materialCost: '571', total: '571.00' }, + { key: '2', item: '机油', kmCycle: '50000', monthCycle: '12', laborCost: '100', materialCost: '300', total: '400.00' } + ] + }; + + // 车辆类型选项需包含初始值(18吨双飞翼货车) + var vehicleTypeOptions = [ + { value: '18吨双飞翼货车', label: '18吨双飞翼货车' }, + { value: '轻型厢式货车', label: '轻型厢式货车' }, + { value: '重型厢式货车', label: '重型厢式货车' }, + { value: '重型半挂牵引车', label: '重型半挂牵引车' }, + { value: '重型集装箱半挂车', label: '重型集装箱半挂车' }, + { value: '小型普通客车', label: '小型普通客车' }, + { value: '重型平板半挂车', label: '重型平板半挂车' }, + { value: '叉车', label: '叉车' }, + { value: '油车', label: '油车' }, + { value: '观光车', label: '观光车' } + ]; + var fuelTypeOptions = [ + { value: '氢', label: '氢' }, + { value: '电', label: '电' }, + { value: '油', label: '油' } + ]; + var gaugeModeOptions = [ + { value: 'MPa', label: 'MPa' }, + { value: 'bar', label: 'bar' } + ]; + + // 基本情况 + var brand = useState(initialData.brand); + var model = useState(initialData.model); + var vehicleType = useState(initialData.vehicleType); + var fuelType = useState(initialData.fuelType); + var plateColor = useState(initialData.plateColor); + var sizeLength = useState(initialData.sizeLength); + var sizeWidth = useState(initialData.sizeWidth); + var sizeHeight = useState(initialData.sizeHeight); + // 轮胎情况 + var tireCount = useState(initialData.tireCount); + var tireSpec = useState(initialData.tireSpec); + // 电气情况 + var batteryType = useState(initialData.batteryType); + var batteryVendor = useState(initialData.batteryVendor); + var capacityKwh = useState(initialData.capacityKwh); + var electricRange = useState(initialData.electricRange); + // 供氢系统情况 + var cylinderCapacity = useState(initialData.cylinderCapacity); + var gaugeMode = useState(initialData.gaugeMode); + var hydrogenRange = useState(initialData.hydrogenRange); + var hydrogenVendor = useState(initialData.hydrogenVendor); + // 其他系统情况 + var coldMachineVendor = useState(initialData.coldMachineVendor); + var stackVendor = useState(initialData.stackVendor); + // 保养项列表 + var maintenanceList = useState(initialData.maintenanceList.slice()); + var formErrors = useState({}); + + function calcTotal(labor, material) { + var l = parseFloat(labor); + var m = parseFloat(material); + if (isNaN(l)) l = 0; + if (isNaN(m)) m = 0; + return (l + m).toFixed(2); + } + + function updateMaintenance(index, field, value) { + maintenanceList[1](function (prev) { + var list = prev.slice(); + var row = Object.assign({}, list[index] || {}); + row[field] = value; + if (field === 'laborCost' || field === 'materialCost') { + row.total = calcTotal(row.laborCost, row.materialCost); + } + list[index] = row; + return list; + }); + } + + function addMaintenanceRow() { + maintenanceList[1](function (prev) { + var key = String((prev.length + 1) + Date.now()); + return prev.concat([{ key: key, item: '', kmCycle: '', monthCycle: '', laborCost: '', materialCost: '', total: '' }]); + }); + } + + function removeMaintenanceRow(index) { + maintenanceList[1](function (prev) { + var list = prev.slice(); + list.splice(index, 1); + if (list.length === 0) list = [{ key: '1', item: '', kmCycle: '', monthCycle: '', laborCost: '', materialCost: '', total: '' }]; + return list; + }); + } + + function validate() { + var err = {}; + if (!String(brand[0] || '').trim()) err.brand = '请输入品牌名称'; + if (!String(model[0] || '').trim()) err.model = '请输入型号名称'; + if (vehicleType[0] == null || vehicleType[0] === '') err.vehicleType = '请选择车辆类型'; + formErrors[1](err); + return Object.keys(err).length === 0; + } + + function handleSave() { + if (!validate()) { + message.warning('请完善必填项'); + return; + } + message.success('保存成功(原型)'); + } + + function handleCancel() { + if (window.history && window.history.back) window.history.back(); + else message.info('取消'); + } + + var layoutStyle = { padding: '16px 24px 80px', backgroundColor: '#f5f5f5', minHeight: '100vh' }; + var cardStyle = { marginBottom: 16 }; + var formRowStyle = { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }; + var formColStyle = { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8, boxSizing: 'border-box' }; + var formColFullStyle = { flex: '0 0 100%', marginBottom: 8 }; + var labelStyle = { display: 'block', marginBottom: 6, color: 'rgba(0,0,0,0.85)' }; + var labelRequiredStyle = { color: '#ff4d4f', marginRight: 4 }; + var errMsgStyle = { color: '#ff4d4f', fontSize: 12, marginTop: 4 }; + var inputStyle = { width: '100%' }; + var footerStyle = { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, zIndex: 99 }; + var cardTitleBarStyle = { display: 'flex', alignItems: 'center', justifyContent: 'flex-start', width: '100%', fontSize: 15, fontWeight: 500, lineHeight: 1.5 }; + var cardTitleBarLineStyle = { width: 4, height: 16, backgroundColor: '#1890ff', borderRadius: 2, marginRight: 8, flexShrink: 0 }; + + function FormItem(props) { + var colStyle = props.fullWidth ? formColFullStyle : formColStyle; + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: labelStyle }, + props.required ? React.createElement('span', { style: labelRequiredStyle }, '*') : null, + props.label + ), + props.children, + props.error ? React.createElement('div', { style: errMsgStyle }, props.error) : null + ); + } + + function CardTitleWithBar(title) { + return React.createElement('div', { style: cardTitleBarStyle }, + React.createElement('span', { style: cardTitleBarLineStyle }), + React.createElement('span', null, title) + ); + } + + var maintenanceColumns = [ + { title: '养护项目', dataIndex: 'item', key: 'item', width: 160, render: function (v, record, i) { return React.createElement(Input, { placeholder: '请输入', value: v, onChange: function (e) { updateMaintenance(i, 'item', e.target.value); }, size: 'small' }); } }, + { title: '保养公里周期(KM)', dataIndex: 'kmCycle', key: 'kmCycle', width: 140, render: function (v, record, i) { return React.createElement(Input, { placeholder: '请输入', value: v, onChange: function (e) { updateMaintenance(i, 'kmCycle', e.target.value); }, size: 'small' }); } }, + { title: '保养时间周期(月)', dataIndex: 'monthCycle', key: 'monthCycle', width: 140, render: function (v, record, i) { return React.createElement(Input, { placeholder: '请输入', value: v, onChange: function (e) { updateMaintenance(i, 'monthCycle', e.target.value); }, size: 'small' }); } }, + { title: '工时费(元)', dataIndex: 'laborCost', key: 'laborCost', width: 120, render: function (v, record, i) { return React.createElement(Input, { placeholder: '请输入', value: v, onChange: function (e) { updateMaintenance(i, 'laborCost', e.target.value); }, size: 'small' }); } }, + { title: '材料费(元)', dataIndex: 'materialCost', key: 'materialCost', width: 120, render: function (v, record, i) { return React.createElement(Input, { placeholder: '请输入', value: v, onChange: function (e) { updateMaintenance(i, 'materialCost', e.target.value); }, size: 'small' }); } }, + { title: '合计', dataIndex: 'total', key: 'total', width: 100, render: function (v) { return v || '—'; } }, + { + title: '操作', + key: 'action', + width: 80, + render: function (_, record, i) { + return React.createElement(Button, { type: 'link', danger: true, size: 'small', onClick: function () { removeMaintenanceRow(i); } }, '删除'); + } + } + ]; + + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement(Breadcrumb, { + items: [ + { title: '基本数据维护' }, + { title: '型号参数' }, + { title: '编辑' } + ] + }) + ), + React.createElement(Card, { title: CardTitleWithBar('型号参数'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '品牌', required: true, error: formErrors[0].brand }, + React.createElement(Input, { placeholder: '请输入品牌名称', value: brand[0], onChange: function (e) { brand[1](e.target.value); }, style: inputStyle, status: formErrors[0].brand ? 'error' : undefined }) + ), + React.createElement(FormItem, { label: '型号', required: true, error: formErrors[0].model }, + React.createElement(Input, { placeholder: '请输入型号名称', value: model[0], onChange: function (e) { model[1](e.target.value); }, style: inputStyle, status: formErrors[0].model ? 'error' : undefined }) + ), + React.createElement(FormItem, { label: '车辆类型', required: true, error: formErrors[0].vehicleType }, + React.createElement(Select, { placeholder: '请选择车辆类型', style: inputStyle, value: vehicleType[0], onChange: function (v) { vehicleType[1](v); }, allowClear: true, options: vehicleTypeOptions, status: formErrors[0].vehicleType ? 'error' : undefined }) + ), + React.createElement(FormItem, { label: '燃料种类' }, + React.createElement(Select, { placeholder: '请选择燃料种类', style: inputStyle, value: fuelType[0], onChange: function (v) { fuelType[1](v); }, allowClear: true, options: fuelTypeOptions }) + ), + React.createElement(FormItem, { label: '车厢颜色' }, + React.createElement(Radio.Group, { value: plateColor[0], onChange: function (e) { plateColor[1](e.target.value); } }, + React.createElement(Radio, { value: '绿牌' }, '绿牌'), + React.createElement(Radio, { value: '黄牌' }, '黄牌'), + React.createElement(Radio, { value: '黄绿牌' }, '黄绿牌') + ) + ), + React.createElement(FormItem, { label: '整车尺寸', fullWidth: true }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, + React.createElement(Input, { placeholder: '长', value: sizeLength[0], onChange: function (e) { sizeLength[1](e.target.value); }, style: { flex: 1, minWidth: 0 } }), + React.createElement('span', { style: { flexShrink: 0 } }, '×'), + React.createElement(Input, { placeholder: '宽', value: sizeWidth[0], onChange: function (e) { sizeWidth[1](e.target.value); }, style: { flex: 1, minWidth: 0 } }), + React.createElement('span', { style: { flexShrink: 0 } }, '×'), + React.createElement(Input, { placeholder: '高', value: sizeHeight[0], onChange: function (e) { sizeHeight[1](e.target.value); }, style: { flex: 1, minWidth: 0 } }) + ) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('轮胎情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '轮胎数量' }, + React.createElement(Input, { placeholder: '请输入轮胎数量', value: tireCount[0], onChange: function (e) { tireCount[1](e.target.value); }, style: inputStyle, addonAfter: '条' }) + ), + React.createElement(FormItem, { label: '轮胎规格' }, + React.createElement(Input, { placeholder: '请输入轮胎规格', value: tireSpec[0], onChange: function (e) { tireSpec[1](e.target.value); }, style: inputStyle }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('电气情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '电池类型' }, + React.createElement(Radio.Group, { value: batteryType[0], onChange: function (e) { batteryType[1](e.target.value); } }, + React.createElement(Radio, { value: '磷酸铁锂' }, '磷酸铁锂'), + React.createElement(Radio, { value: '三元锂' }, '三元锂') + ) + ), + React.createElement(FormItem, { label: '电池厂家' }, + React.createElement(Input, { placeholder: '请输入电池厂商名称', value: batteryVendor[0], onChange: function (e) { batteryVendor[1](e.target.value); }, style: inputStyle }) + ), + React.createElement(FormItem, { label: '储电量' }, + React.createElement(Input, { placeholder: '请输入储电量', value: capacityKwh[0], onChange: function (e) { capacityKwh[1](e.target.value); }, style: inputStyle, addonAfter: 'kWh' }) + ), + React.createElement(FormItem, { label: '电续航里程' }, + React.createElement(Input, { placeholder: '请输入电续航里程', value: electricRange[0], onChange: function (e) { electricRange[1](e.target.value); }, style: inputStyle, addonAfter: 'KM' }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('供氢系统情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '氢瓶容量' }, + React.createElement(Input, { placeholder: '请输入氢瓶容量', value: cylinderCapacity[0], onChange: function (e) { cylinderCapacity[1](e.target.value); }, style: inputStyle, addonAfter: 'L' }) + ), + React.createElement(FormItem, { label: '仪表盘显示模式' }, + React.createElement(Select, { placeholder: '请选择仪表盘显示模式', style: inputStyle, value: gaugeMode[0], onChange: function (v) { gaugeMode[1](v); }, allowClear: true, options: gaugeModeOptions }) + ), + React.createElement(FormItem, { label: '氢续航里程' }, + React.createElement(Input, { placeholder: '请输入氢续航里程', value: hydrogenRange[0], onChange: function (e) { hydrogenRange[1](e.target.value); }, style: inputStyle, addonAfter: 'KM' }) + ), + React.createElement(FormItem, { label: '供氢系统厂家' }, + React.createElement(Input, { placeholder: '请输入供氢系统厂家', value: hydrogenVendor[0], onChange: function (e) { hydrogenVendor[1](e.target.value); }, style: inputStyle }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('其他系统情况'), size: 'small', style: cardStyle }, + React.createElement('div', { style: formRowStyle }, + React.createElement(FormItem, { label: '冷机厂家' }, + React.createElement(Input, { placeholder: '请输入冷机厂家名称', value: coldMachineVendor[0], onChange: function (e) { coldMachineVendor[1](e.target.value); }, style: inputStyle }) + ), + React.createElement(FormItem, { label: '电堆厂家' }, + React.createElement(Input, { placeholder: '请输入电堆厂家名称', value: stackVendor[0], onChange: function (e) { stackVendor[1](e.target.value); }, style: inputStyle }) + ) + ) + ), + React.createElement(Card, { title: CardTitleWithBar('保养项情况'), size: 'small', style: cardStyle }, + React.createElement(Table, { + columns: maintenanceColumns, + dataSource: maintenanceList[0], + rowKey: 'key', + pagination: false, + size: 'small', + bordered: true + }), + React.createElement(Button, { type: 'dashed', style: { width: '100%', marginTop: 8 }, onClick: addMaintenanceRow }, '+ 添加一项') + ), + React.createElement('div', { style: footerStyle }, + React.createElement(Button, { type: 'primary', onClick: handleSave }, '保存'), + React.createElement(Button, { onClick: handleCancel }, '取消') + ) + ); +}; + +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)); + } + } +} diff --git a/web端/运维管理/基本数据维护/型号参数.jsx b/web端/运维管理/基本数据维护/型号参数.jsx new file mode 100644 index 0000000..a7dabf7 --- /dev/null +++ b/web端/运维管理/基本数据维护/型号参数.jsx @@ -0,0 +1,256 @@ +// 【重要】必须使用 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 Space = antd.Space; + var message = antd.message; + var Modal = antd.Modal; + + // 筛选 + var filterVehicleType = useState(undefined); + var filterBrand = useState(undefined); + var filterModel = useState(undefined); + var appliedFilter = useState({ vehicleType: undefined, brand: undefined, model: undefined }); + + // 下拉选项:车辆类型、品牌 + var vehicleTypeOptions = [ + { value: '18吨双飞翼货车', label: '18吨双飞翼货车' }, + { value: '重型厢式货车', label: '重型厢式货车' }, + { value: '轻型厢式货车', label: '轻型厢式货车' }, + { value: '小型普通客车', label: '小型普通客车' }, + { value: '重型栏板货车', label: '重型栏板货车' } + ]; + var brandOptions = [ + { value: '苏龙', label: '苏龙' }, + { value: '东风', label: '东风' }, + { value: '福田', label: '福田' }, + { value: '江淮', label: '江淮' }, + { value: '重汽', label: '重汽' }, + { value: '陕汽', label: '陕汽' } + ]; + // 品牌 -> 型号联动:先选品牌,型号下拉只展示该品牌下型号 + var brandModelMap = { + '苏龙': [ + { value: '海格牌KLQ5180XYKFCEV', label: '海格牌KLQ5180XYKFCEV' }, + { value: 'KLQ6129', label: 'KLQ6129' }, + { value: 'KLQ6106', label: 'KLQ6106' }, + { value: '海格牌KLQ5090', label: '海格牌KLQ5090' } + ], + '东风': [ + { value: 'DFH1180', label: 'DFH1180' }, + { value: 'DFH1250', label: 'DFH1250' }, + { value: 'DFH5180', label: 'DFH5180' } + ], + '福田': [ + { value: 'BJ1180', label: 'BJ1180' }, + { value: 'BJ5180', label: 'BJ5180' }, + { value: '欧曼EST', label: '欧曼EST' } + ], + '江淮': [ + { value: 'HFC1180', label: 'HFC1180' }, + { value: 'HFC5180', label: 'HFC5180' }, + { value: '帅铃Q6', label: '帅铃Q6' } + ], + '重汽': [ + { value: 'ZZ5180', label: 'ZZ5180' }, + { value: 'ZZ1250', label: 'ZZ1250' }, + { value: '豪沃T7H', label: '豪沃T7H' } + ], + '陕汽': [ + { value: 'SX1180', label: 'SX1180' }, + { value: 'SX5180', label: 'SX5180' }, + { value: '德龙X3000', label: '德龙X3000' } + ] + }; + var modelOptions = useMemo(function () { + var brand = filterBrand[0]; + if (!brand || !brandModelMap[brand]) return []; + return brandModelMap[brand]; + }, [filterBrand[0]]); + + // 列表数据(样例与车辆管理-查看.jsx 型号参数一致,并扩展多品牌多型号) + var rawList = [ + { id: '1', vehicleType: '18吨双飞翼货车', brand: '苏龙', model: '海格牌KLQ5180XYKFCEV', fuelType: '氢', plateColor: '绿牌', wholeSize: '5995mm x 2145mm x 3130mm', tireCount: '8', tireSpec: '15/80R22.5', batteryType: '磷酸铁锂', capacityKwh: '100000', cylinderCapacity: 'xxx L' }, + { id: '2', vehicleType: '重型厢式货车', brand: '东风', model: 'DFH1180', fuelType: '氢', plateColor: '绿牌', wholeSize: '5990mm x 2150mm x 3080mm', tireCount: '8', tireSpec: '295/80R22.5', batteryType: '磷酸铁锂', capacityKwh: '96000', cylinderCapacity: '140 L' }, + { id: '3', vehicleType: '重型厢式货车', brand: '福田', model: 'BJ1180', fuelType: '氢', plateColor: '绿牌', wholeSize: '5985mm x 2140mm x 3120mm', tireCount: '8', tireSpec: '295/80R22.5', batteryType: '磷酸铁锂', capacityKwh: '98000', cylinderCapacity: '135 L' }, + { id: '4', vehicleType: '轻型厢式货车', brand: '江淮', model: 'HFC1180', fuelType: '氢', plateColor: '绿牌', wholeSize: '5995mm x 2100mm x 2980mm', tireCount: '6', tireSpec: '225/70R19.5', batteryType: '磷酸铁锂', capacityKwh: '72000', cylinderCapacity: '80 L' }, + { id: '5', vehicleType: '18吨双飞翼货车', brand: '重汽', model: 'ZZ5180', fuelType: '氢', plateColor: '绿牌', wholeSize: '5995mm x 2145mm x 3140mm', tireCount: '8', tireSpec: '15/80R22.5', batteryType: '磷酸铁锂', capacityKwh: '102000', cylinderCapacity: '150 L' }, + { id: '6', vehicleType: '小型普通客车', brand: '苏龙', model: 'KLQ6129', fuelType: '氢', plateColor: '绿牌', wholeSize: '11980mm x 2550mm x 3580mm', tireCount: '6', tireSpec: '275/70R22.5', batteryType: '磷酸铁锂', capacityKwh: '85000', cylinderCapacity: '100 L' }, + { id: '7', vehicleType: '重型栏板货车', brand: '陕汽', model: 'SX1180', fuelType: '氢', plateColor: '绿牌', wholeSize: '5995mm x 2150mm x 3100mm', tireCount: '8', tireSpec: '295/80R22.5', batteryType: '磷酸铁锂', capacityKwh: '95000', cylinderCapacity: '130 L' } + ]; + + var filteredList = useMemo(function () { + var list = rawList.slice(); + var f = appliedFilter[0]; + 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; }); + return list; + }, [rawList, appliedFilter[0]]); + + var handleQuery = useCallback(function () { + appliedFilter[1]({ + vehicleType: filterVehicleType[0], + brand: filterBrand[0], + model: filterModel[0] + }); + }, [filterVehicleType[0], filterBrand[0], filterModel[0]]); + + var handleReset = useCallback(function () { + filterVehicleType[1](undefined); + filterBrand[1](undefined); + filterModel[1](undefined); + appliedFilter[1]({ vehicleType: undefined, brand: undefined, model: undefined }); + }, []); + + var deleteModalState = useState({ open: false, record: null }); + var openDeleteConfirm = useCallback(function (record) { + deleteModalState[1]({ open: true, record: record }); + }, []); + var confirmDelete = useCallback(function () { + message.success('删除成功(原型)'); + deleteModalState[1]({ open: false, record: null }); + }, []); + + var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; + + var columns = [ + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 120, ellipsis: true }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 100, ellipsis: true }, + { title: '型号', dataIndex: 'model', key: 'model', width: 180, ellipsis: true }, + { title: '燃料种类', dataIndex: 'fuelType', key: 'fuelType', width: 100, ellipsis: true }, + { title: '车牌颜色', dataIndex: 'plateColor', key: 'plateColor', width: 100, ellipsis: true }, + { title: '整车尺寸', dataIndex: 'wholeSize', key: 'wholeSize', width: 140, ellipsis: true }, + { title: '轮胎数量', dataIndex: 'tireCount', key: 'tireCount', width: 90, ellipsis: true }, + { title: '轮胎规格', dataIndex: 'tireSpec', key: 'tireSpec', width: 120, ellipsis: true }, + { title: '电池类型', dataIndex: 'batteryType', key: 'batteryType', width: 100, ellipsis: true }, + { title: '储电量(kWh)', dataIndex: 'capacityKwh', key: 'capacityKwh', width: 120, ellipsis: true }, + { title: '氢瓶容量', dataIndex: 'cylinderCapacity', key: 'cylinderCapacity', width: 100, ellipsis: true }, + { + title: '操作', + key: 'action', + width: 160, + fixed: 'right', + render: function (_, record) { + return React.createElement(Space, { size: 4 }, + React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 }, onClick: function () { message.info('查看(原型)'); } }, '查看'), + React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 }, onClick: function () { message.info('编辑(原型)'); } }, '编辑'), + React.createElement(Button, { type: 'link', size: 'small', danger: true, style: { padding: 0 }, onClick: function () { openDeleteConfirm(record); } }, '删除') + ); + } + } + ]; + + return React.createElement('div', { style: layoutStyle }, + React.createElement('div', { style: { marginBottom: 16 } }, + React.createElement(Breadcrumb, { + items: [ + { title: '基本数据维护' }, + { title: '型号参数' } + ] + }) + ), + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement('div', { + style: { + display: 'grid', + gridTemplateColumns: '1fr 1fr 1fr', + gap: '16px 24px', + alignItems: 'start', + minWidth: 0 + } + }, + React.createElement('div', null, + React.createElement('div', { style: filterLabelStyle }, '车辆类型'), + React.createElement(Select, { + placeholder: '请选择车系类型', + style: { width: '100%' }, + value: filterVehicleType[0], + onChange: function (v) { filterVehicleType[1](v); }, + allowClear: true, + options: vehicleTypeOptions + }) + ), + React.createElement('div', null, + React.createElement('div', { style: filterLabelStyle }, '品牌'), + React.createElement(Select, { + placeholder: '请选择品牌', + style: { width: '100%' }, + value: filterBrand[0], + onChange: function (v) { filterBrand[1](v); filterModel[1](undefined); }, + allowClear: true, + options: brandOptions + }) + ), + React.createElement('div', null, + React.createElement('div', { style: filterLabelStyle }, '型号'), + React.createElement(Select, { + placeholder: filterBrand[0] ? '请选择型号' : '请先选择品牌', + style: { width: '100%' }, + value: filterModel[0], + onChange: function (v) { filterModel[1](v); }, + allowClear: true, + options: modelOptions, + disabled: !filterBrand[0] + }) + ) + ), + 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('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 8 } }, + React.createElement(Button, { type: 'primary', onClick: function () { message.info('新建(原型)'); } }, '新建'), + React.createElement(Button, { onClick: function () { message.info('导出(原型)'); } }, '导出') + ), + React.createElement(Card, null, + React.createElement(Table, { + rowKey: 'id', + columns: columns, + dataSource: filteredList, + scroll: { x: 1400 }, + size: 'small', + pagination: { showSizeChanger: true, showQuickJumper: true, showTotal: function (t) { return '共 ' + t + ' 条'; } } + }) + ), + React.createElement(Modal, { + title: '确认删除', + open: deleteModalState[0].open, + onCancel: function () { deleteModalState[1]({ open: false, record: null }); }, + onOk: confirmDelete, + okText: '确定', + cancelText: '取消' + }, '确定要删除该型号参数吗?') + ); +}; + +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)); + } + } +} diff --git a/web端/运维管理/车辆业务/备车-编辑.jsx b/web端/运维管理/车辆业务/备车-编辑.jsx new file mode 100644 index 0000000..ddb052e --- /dev/null +++ b/web端/运维管理/车辆业务/备车-编辑.jsx @@ -0,0 +1,602 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 车辆业务 - 备车-编辑(布局同新增备车,车辆数据不可修改,其余字段可修改) + +const Component = function () { + var useState = React.useState; + var useCallback = React.useCallback; + var useMemo = React.useMemo; + var useRef = React.useRef; + var useEffect = React.useEffect; + + var antd = window.antd; + var Card = antd.Card; + var Input = antd.Input; + var Button = antd.Button; + var Switch = antd.Switch; + var Drawer = antd.Drawer; + var Table = antd.Table; + var message = antd.message; + var Modal = antd.Modal; + + var plateManageList = [ + { plateNo: '京A12345', vehicleType: '厢式货车', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', bodyAd: true, tailboard: true }, + { plateNo: '京C11111', vehicleType: '平板货车', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', bodyAd: false, tailboard: false }, + { plateNo: '京D22222', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M8888888', bodyAd: true, tailboard: false }, + { plateNo: '京E33333', vehicleType: '栏板货车', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M7777777', bodyAd: false, tailboard: true }, + { plateNo: '京F10001', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M9990001', bodyAd: true, tailboard: true } + ]; + + var inspectionCategoryItems = { + '车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'], + '仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'], + '驾驶室': ['点烟器', '车窗升降', '按键开关', '雨刮器', '内后视镜是否正常', '内/外门把手', '安全带', '空调冷暖风', '仪表盘', '门锁功能', '手刹', '车钥匙功能是否正常', '喇叭', '音响功能', '遮阳板', '主副驾座椅', '方向盘', '内饰干净整洁'], + '轮胎': ['前左胎', '前右胎', '后左胎', '后右胎'], + '液位检查': ['冷却液', '制动液', '玻璃水'], + '外观检查': ['车身外观', '漆面', '玻璃'], + '车辆外观': ['整车外观'], + '其他': ['其他检查项'], + '随车工具': ['三角牌', '灭火器', '反光背心'], + '随车证件': ['行驶证', '营运证', '保险单'], + '整车': ['整车状态'], + '燃料电池系统': ['氢系统', '储氢瓶'], + '冷机': ['冷机运行'], + '制动系统': ['制动踏板', '驻车制动'] + }; + + function buildInspectionList() { + var list = []; + var categories = Object.keys(inspectionCategoryItems); + for (var i = 0; i < categories.length; i++) { + var cat = categories[i]; + var items = inspectionCategoryItems[cat]; + for (var j = 0; j < items.length; j++) { + list.push({ category: cat, checkItem: items[j], checked: true, remark: '', treadDepth: '' }); + } + } + return list; + } + + // 编辑时带入的当前记录(车辆数据不可改,其余可改;已上传图片支持预览、删除) + var initialData = { + plateNo: '京A12345', + vehicleType: '厢式货车', + brand: '东风', + model: 'DFH1180', + vin: 'LGHXCAE28M1234567', + bodyAd: true, + adPhotos: ['https://picsum.photos/seed/ad1/200/200'], + enlargePhotos: ['https://picsum.photos/seed/en1/200/200'], + tailboard: true, + spareTirePhotos: ['https://picsum.photos/seed/sp1/200/200'], + spareTireTreadDepth: '5.2', + flawPhotos: [ + 'https://picsum.photos/seed/flaw1/200/200', + 'https://picsum.photos/seed/flaw2/200/200', + 'https://picsum.photos/seed/flaw3/200/200' + ], + inspectionList: buildInspectionList() + }; + + var formState = useState({ + plateNo: initialData.plateNo, + vehicleType: initialData.vehicleType, + brand: initialData.brand, + model: initialData.model, + vin: initialData.vin, + bodyAd: initialData.bodyAd, + adPhotos: initialData.adPhotos.slice(), + enlargePhotos: initialData.enlargePhotos.slice(), + tailboard: initialData.tailboard, + spareTirePhotos: initialData.spareTirePhotos.slice(), + spareTireTreadDepth: initialData.spareTireTreadDepth, + flawPhotos: initialData.flawPhotos.slice(), + inspectionList: initialData.inspectionList.map(function (r) { return Object.assign({}, r); }) + }); + var form = formState[0]; + var setForm = formState[1]; + + var drawerOpenState = useState(false); + var photoPreviewState = useState({ open: false, url: null }); + var reqDocOpenState = useState(false); + var adPhotoInputRef = useRef(null); + var enlargePhotoInputRef = useRef(null); + var spareTirePhotoInputRef = useRef(null); + var flawPhotoInputRef = useRef(null); + var spareTireOcrModalState = useState({ open: false, tempUrl: null, tempDepth: '', phase: 'recognizing' }); + var spareTireOcrPhaseRef = useRef(null); + + var updateInspection = useCallback(function (index, field, value) { + setForm(function (prev) { + var n = {}; + for (var k in prev) n[k] = prev[k]; + var list = (prev.inspectionList || []).slice(); + var row = list[index] || {}; + list[index] = Object.assign({}, row, { [field]: value }); + n.inspectionList = list; + return n; + }); + }, []); + + var addAdPhoto = useCallback(function () { + if ((form.adPhotos || []).length >= 1) { message.warning('最多上传1张'); return; } + if (adPhotoInputRef.current) adPhotoInputRef.current.click(); + }, [form.adPhotos]); + var onAdPhotoFileChange = useCallback(function (e) { + var files = e.target.files; + if (!files || files.length === 0) return; + var file = files[0]; + if (!file || file.type.indexOf('image') === -1) { e.target.value = ''; return; } + var url = URL.createObjectURL(file); + setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.adPhotos = [url]; return n; }); + e.target.value = ''; + }, []); + var removeAdPhoto = useCallback(function () { + setForm(function (p) { + var n = {}; for (var k in p) n[k] = p[k]; + var arr = (p.adPhotos || [])[0]; + if (arr && typeof arr === 'string' && arr.indexOf('blob:') === 0) URL.revokeObjectURL(arr); + n.adPhotos = []; + return n; + }); + }, []); + + var addEnlargePhoto = useCallback(function () { + if ((form.enlargePhotos || []).length >= 1) { message.warning('最多上传1张'); return; } + if (enlargePhotoInputRef.current) enlargePhotoInputRef.current.click(); + }, [form.enlargePhotos]); + var onEnlargePhotoFileChange = useCallback(function (e) { + var files = e.target.files; + if (!files || files.length === 0) return; + var file = files[0]; + if (!file || file.type.indexOf('image') === -1) { e.target.value = ''; return; } + var url = URL.createObjectURL(file); + setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.enlargePhotos = [url]; return n; }); + e.target.value = ''; + }, []); + var removeEnlargePhoto = useCallback(function () { + setForm(function (p) { + var n = {}; for (var k in p) n[k] = p[k]; + var u = (p.enlargePhotos || [])[0]; + if (u && typeof u === 'string' && u.indexOf('blob:') === 0) URL.revokeObjectURL(u); + n.enlargePhotos = []; + return n; + }); + }, []); + + var addSpareTirePhoto = useCallback(function () { + if ((form.spareTirePhotos || []).length >= 1) { message.warning('最多上传1张'); return; } + if (spareTirePhotoInputRef.current) spareTirePhotoInputRef.current.click(); + }, [form.spareTirePhotos]); + var onSpareTirePhotoFileChange = useCallback(function (e) { + var files = e.target.files; + if (!files || files.length === 0) return; + var file = files[0]; + if (!file || file.type.indexOf('image') === -1) { e.target.value = ''; return; } + var url = URL.createObjectURL(file); + spareTireOcrModalState[1]({ open: true, tempUrl: url, tempDepth: '', phase: 'recognizing' }); + e.target.value = ''; + }, []); + useEffect(function () { + var s = spareTireOcrModalState[0]; + if (!s.open || s.phase !== 'recognizing') return; + if (spareTireOcrPhaseRef.current) clearTimeout(spareTireOcrPhaseRef.current); + spareTireOcrPhaseRef.current = setTimeout(function () { + spareTireOcrModalState[1](function (prev) { + return prev.open ? { open: true, tempUrl: prev.tempUrl, tempDepth: '5.2', phase: 'result' } : prev; + }); + }, 1500); + return function () { + if (spareTireOcrPhaseRef.current) clearTimeout(spareTireOcrPhaseRef.current); + }; + }, [spareTireOcrModalState[0].open, spareTireOcrModalState[0].phase]); + var confirmSpareTireOcr = useCallback(function () { + var s = spareTireOcrModalState[0]; + var depth = (s.tempDepth || '').trim(); + setForm(function (p) { + var n = {}; for (var k in p) n[k] = p[k]; + n.spareTireTreadDepth = depth; + n.spareTirePhotos = s.tempUrl ? [s.tempUrl] : []; + return n; + }); + spareTireOcrModalState[1]({ open: false, tempUrl: null, tempDepth: '', phase: 'recognizing' }); + }, []); + var closeSpareTireOcrWithoutSave = useCallback(function () { + var s = spareTireOcrModalState[0]; + if (s.tempUrl && s.tempUrl.indexOf('blob:') === 0) URL.revokeObjectURL(s.tempUrl); + spareTireOcrModalState[1]({ open: false, tempUrl: null, tempDepth: '', phase: 'recognizing' }); + }, []); + var setSpareTireOcrDepth = useCallback(function (v) { + spareTireOcrModalState[1](function (prev) { return { open: prev.open, tempUrl: prev.tempUrl, tempDepth: v, phase: prev.phase }; }); + }, []); + var removeSpareTirePhoto = useCallback(function () { + setForm(function (p) { + var n = {}; for (var k in p) n[k] = p[k]; + var u = (p.spareTirePhotos || [])[0]; + if (u && typeof u === 'string' && u.indexOf('blob:') === 0) URL.revokeObjectURL(u); + n.spareTirePhotos = []; return n; + }); + }, []); + + var addFlawPhoto = useCallback(function () { + if ((form.flawPhotos || []).length >= 4) { message.warning('最多上传4张'); return; } + if (flawPhotoInputRef.current) flawPhotoInputRef.current.click(); + }, [form.flawPhotos]); + var onFlawPhotoFileChange = useCallback(function (e) { + var files = e.target.files; + if (!files || files.length === 0) return; + setForm(function (p) { + var current = p.flawPhotos || []; + var remain = 4 - current.length; + if (remain <= 0) return p; + var urls = []; + for (var i = 0; i < files.length && urls.length < remain; i++) { + if (files[i].type.indexOf('image') !== -1) urls.push(URL.createObjectURL(files[i])); + } + if (urls.length === 0) return p; + var n = {}; for (var k in p) n[k] = p[k]; n.flawPhotos = current.concat(urls); return n; + }); + e.target.value = ''; + }, []); + var removeFlawPhoto = useCallback(function (idx) { + setForm(function (p) { + var n = {}; + for (var k in p) n[k] = p[k]; + var arr = (p.flawPhotos || []).slice(); + var url = arr[idx]; + if (url && url.indexOf('blob:') === 0) URL.revokeObjectURL(url); + arr.splice(idx, 1); + n.flawPhotos = arr; + return n; + }); + }, []); + + var handleSave = useCallback(function () { + message.success('提交成功(原型)'); + if (typeof window !== 'undefined' && window.history) window.history.back(); + }, []); + var handleSaveDraft = useCallback(function () { + message.success('保存成功(原型)'); + }, []); + var handleCancel = useCallback(function () { + if (typeof window !== 'undefined' && window.history) window.history.back(); + else message.info('取消'); + }, []); + + var styles = { + page: { padding: '16px 24px 80px', 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' }, + formRow: { display: 'flex', flexWrap: 'wrap', marginBottom: 16 }, + formCol: { flex: '0 0 33.33%', minWidth: 200, paddingRight: 16, marginBottom: 8, boxSizing: 'border-box' }, + formColFull: { flex: '0 0 100%', marginBottom: 8, boxSizing: 'border-box' }, + label: { display: 'block', marginBottom: 6, color: '#333' }, + labelRequired: { color: '#ff4d4f', marginRight: 4 }, + footer: { position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 24px', backgroundColor: '#fff', borderTop: '1px solid #e8e8e8', display: 'flex', gap: 12, zIndex: 99 } + }; + + var FormItem = function (props) { + var colStyle = props.fullWidth ? styles.formColFull : styles.formCol; + return React.createElement('div', { style: colStyle }, + React.createElement('label', { style: styles.label }, + props.required ? React.createElement('span', { style: styles.labelRequired }, '*') : null, + props.label + ), + props.children + ); + }; + + var inspectionList = form.inspectionList || []; + var categoryRowSpans = useMemo(function () { + var list = inspectionList; + var spans = []; + var i = 0; + while (i < list.length) { + var cat = list[i].category; + var start = i; + while (i < list.length && list[i].category === cat) i++; + spans[start] = i - start; + for (var j = start + 1; j < i; j++) spans[j] = 0; + } + return spans; + }, [form.inspectionList]); + + var inspectionColumns = [ + { + title: '类别', + dataIndex: 'category', + key: 'category', + width: 120, + onCell: function (_, index) { return { rowSpan: categoryRowSpans[index] !== undefined ? categoryRowSpans[index] : 1 }; } + }, + { title: '检查项目', dataIndex: 'checkItem', key: 'checkItem', width: 180 }, + { + title: '检查情况', + key: 'checked', + width: 140, + render: function (_, record, index) { + if (record.category === '轮胎') { + return React.createElement(Input, { + value: record.treadDepth, + onChange: function (e) { updateInspection(index, 'treadDepth', e.target.value); }, + placeholder: '请输入胎纹深度', + style: { width: '100%' } + }); + } + return React.createElement(Switch, { + checked: record.checked !== false, + onChange: function (checked) { updateInspection(index, 'checked', checked); } + }); + } + }, + { + title: '备注', + dataIndex: 'remark', + key: 'remark', + render: function (val, record, index) { + return React.createElement(Input, { + value: val, + onChange: function (e) { updateInspection(index, 'remark', e.target.value); }, + placeholder: '备注' + }); + } + } + ]; + + var cameraIconBox = function (onClick) { + return React.createElement('div', { + role: 'button', + tabIndex: 0, + onClick: onClick, + style: { width: 64, height: 64, border: '1px dashed #d9d9d9', borderRadius: 4, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', background: '#fafafa', boxSizing: 'border-box' } + }, React.createElement('svg', { width: 28, height: 28, viewBox: '0 0 24 24', fill: 'none', stroke: '#999', strokeWidth: 2 }, + React.createElement('path', { d: 'M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z' }), + React.createElement('circle', { cx: 12, cy: 13, r: 4 }) + )); + }; + + var breadcrumbNodes = [ + React.createElement('span', { key: '1' }, '运维管理'), + React.createElement('span', { key: '2', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '3' }, '车辆业务'), + React.createElement('span', { key: '4', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '5' }, '备车管理'), + React.createElement('span', { key: '6', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '7', style: { color: '#1890ff' } }, '编辑') + ]; + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 } }, + React.createElement('div', { style: styles.breadcrumb }, breadcrumbNodes) + ), + React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardBody }, + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '车牌号', required: true }, + React.createElement(Input, { value: form.plateNo, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '车辆类型' }, + React.createElement(Input, { value: form.vehicleType, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '品牌' }, + React.createElement(Input, { value: form.brand, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '型号' }, + React.createElement(Input, { value: form.model, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ), + React.createElement(FormItem, { label: '车辆识别代码' }, + React.createElement(Input, { value: form.vin, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '车身广告及放大字' }, + React.createElement(Switch, { + checked: form.bodyAd, + checkedChildren: '开', + unCheckedChildren: '关', + onChange: function (checked) { + setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.bodyAd = checked; return n; }); + } + }) + ), + React.createElement(FormItem, { label: '广告照片' }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + React.createElement('input', { type: 'file', ref: adPhotoInputRef, accept: 'image/*', style: { display: 'none' }, onChange: onAdPhotoFileChange }), + (form.adPhotos || []).length < 1 ? cameraIconBox(addAdPhoto) : null, + (form.adPhotos || []).map(function (url, i) { + return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } }, + React.createElement('img', { + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }), + React.createElement('span', { + style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }, + onClick: function () { removeAdPhoto(); } + }, '×') + ); + }) + ) + ), + React.createElement(FormItem, { label: '放大字照片' }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + React.createElement('input', { type: 'file', ref: enlargePhotoInputRef, accept: 'image/*', style: { display: 'none' }, onChange: onEnlargePhotoFileChange }), + (form.enlargePhotos || []).length < 1 ? cameraIconBox(addEnlargePhoto) : null, + (form.enlargePhotos || []).map(function (url, i) { + return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } }, + React.createElement('img', { + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }), + React.createElement('span', { + style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }, + onClick: function () { removeEnlargePhoto(); } + }, '×') + ); + }) + ) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '尾板' }, + React.createElement(Switch, { + checked: form.tailboard, + checkedChildren: '开', + unCheckedChildren: '关', + onChange: function (checked) { + setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.tailboard = checked; return n; }); + } + }) + ), + React.createElement(FormItem, { label: '备胎照片' }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + React.createElement('input', { type: 'file', ref: spareTirePhotoInputRef, accept: 'image/*', style: { display: 'none' }, onChange: onSpareTirePhotoFileChange }), + (form.spareTirePhotos || []).length < 1 ? cameraIconBox(addSpareTirePhoto) : null, + (form.spareTirePhotos || []).map(function (url, i) { + return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } }, + React.createElement('img', { + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }), + React.createElement('span', { + style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }, + onClick: function () { removeSpareTirePhoto(); } + }, '×') + ); + }) + ) + ), + React.createElement(FormItem, { label: '备胎胎纹深度' }, + React.createElement(Input, { + value: form.spareTireTreadDepth, + onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.spareTireTreadDepth = e.target.value; return n; }); }, + placeholder: 'OCR识别结果可修改', + style: { width: '100%' }, + addonAfter: 'mm' + }) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '瑕疵', fullWidth: true }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + React.createElement('input', { type: 'file', ref: flawPhotoInputRef, accept: 'image/*', multiple: true, style: { display: 'none' }, onChange: onFlawPhotoFileChange }), + (form.flawPhotos || []).length < 4 ? cameraIconBox(addFlawPhoto) : null, + (form.flawPhotos || []).map(function (url, i) { + return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } }, + React.createElement('img', { + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }), + React.createElement('span', { + style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }, + onClick: function () { removeFlawPhoto(i); } + }, '×') + ); + }) + ) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '车辆检查', fullWidth: true }, + React.createElement(Button, { type: 'default', onClick: function () { drawerOpenState[1](true); } }, '备车检查单') + ) + ) + ) + ), + React.createElement('div', { style: styles.footer }, + React.createElement(Button, { type: 'primary', onClick: handleSave }, '提交'), + React.createElement(Button, { onClick: handleSaveDraft }, '保存'), + React.createElement(Button, { onClick: handleCancel }, '取消') + ), + React.createElement(Drawer, { + title: '备车检查单', + placement: 'right', + width: 640, + open: drawerOpenState[0], + onClose: function () { drawerOpenState[1](false); }, + styles: { body: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', paddingBottom: 0 } }, + footer: React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } }, + React.createElement(Button, { onClick: function () { drawerOpenState[1](false); } }, '返回'), + React.createElement(Button, { type: 'primary', onClick: function () { drawerOpenState[1](false); message.success('检查单已保存'); } }, '提交') + ) + }, + React.createElement('div', { style: { flex: 1, minHeight: 0, overflow: 'auto' } }, + React.createElement(Table, { + columns: inspectionColumns, + dataSource: form.inspectionList || [], + rowKey: function (_, i) { return i; }, + pagination: false, + size: 'small' + }) + ) + ), + spareTireOcrModalState[0].open + ? React.createElement(Modal, { + title: '备胎照片识别', + open: true, + onCancel: spareTireOcrModalState[0].phase === 'result' ? closeSpareTireOcrWithoutSave : undefined, + width: 560, + maskClosable: spareTireOcrModalState[0].phase === 'result', + footer: spareTireOcrModalState[0].phase === 'result' + ? React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } }, + React.createElement(Button, { onClick: closeSpareTireOcrWithoutSave }, '取消'), + React.createElement(Button, { type: 'primary', onClick: confirmSpareTireOcr }, '确认') + ) + : null, + closable: spareTireOcrModalState[0].phase === 'result' + }, spareTireOcrModalState[0].phase === 'recognizing' + ? React.createElement('div', { style: { padding: '24px 0', textAlign: 'center', fontSize: 15, color: '#666' } }, '正在识别中,请勿关闭页面') + : React.createElement('div', { style: { display: 'flex', gap: 24, alignItems: 'flex-start' } }, + React.createElement('div', { style: { flex: '0 0 200px' } }, + React.createElement('img', { src: spareTireOcrModalState[0].tempUrl, alt: '', style: { width: '100%', display: 'block', borderRadius: 4 } }) + ), + React.createElement('div', { style: { flex: 1 } }, + React.createElement('div', { style: { marginBottom: 8, color: '#333' } }, '识别胎纹深度(mm)'), + React.createElement(Input, { + value: spareTireOcrModalState[0].tempDepth, + onChange: function (e) { setSpareTireOcrDepth(e.target.value); }, + placeholder: '请输入', + addonAfter: 'mm', + style: { width: '100%', maxWidth: 160 } + }) + ) + ) + ) + : null, + photoPreviewState[0].open && photoPreviewState[0].url + ? React.createElement(Modal, { + title: '预览', + open: true, + footer: null, + onCancel: function () { photoPreviewState[1]({ open: false, url: null }); } + }, React.createElement('img', { src: photoPreviewState[0].url, alt: '', style: { width: '100%' } })) + : null + ); +}; + +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)); + } + } +} diff --git a/web端/运维管理/车辆业务/备车管理.jsx b/web端/运维管理/车辆业务/备车管理.jsx index f2adfdb..1b51db6 100755 --- a/web端/运维管理/车辆业务/备车管理.jsx +++ b/web端/运维管理/车辆业务/备车管理.jsx @@ -17,6 +17,7 @@ const Component = function () { var Modal = antd.Modal; var Steps = antd.Steps; var Input = antd.Input; + var Cascader = antd.Cascader; var Radio = antd.Radio; var Space = antd.Space; var Row = antd.Row; @@ -36,7 +37,8 @@ const Component = function () { operator: undefined, plateNo: undefined, vehicleType: undefined, - parkingLot: undefined + vehicleModelCascader: undefined, + parkingLot: [] }); var filters = filterState[0]; var setFilters = filterState[1]; @@ -47,7 +49,8 @@ const Component = function () { operator: undefined, plateNo: undefined, vehicleType: undefined, - parkingLot: undefined + vehicleModelCascader: undefined, + parkingLot: [] }); var appliedFilters = appliedFilterState[0]; var setAppliedFilters = appliedFilterState[1]; @@ -58,31 +61,31 @@ const Component = function () { var defaultInspection = [{ checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '灯光', checkItem: '尾灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '轮胎', checkItem: '前左胎', result: '正常', treadDepth: '5.2mm', remark: '' }, { checkType: '车身', checkItem: '车身外观', result: '正常', treadDepth: '', remark: '' }]; var completedListState = useState([ - { id: '1', completeDate: '2025-02-10 09:30', operator: '张三', vehicleType: '厢式货车', plateNo: '京A12345', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 3, adPhotos: ['https://picsum.photos/200/150?random=1', 'https://picsum.photos/200/150?random=2', 'https://picsum.photos/200/150?random=3'], tailboard: '有', hasTrailer: '有', trailerPlate: '京B67890', flawPhotoCount: 2, flawPhotos: ['https://picsum.photos/200/150?random=4', 'https://picsum.photos/200/150?random=5'], inspectionList: [{ checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '灯光', checkItem: '尾灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '轮胎', checkItem: '前左胎', result: '正常', treadDepth: '5.2mm', remark: '' }, { checkType: '车身', checkItem: '车身外观', result: '异常', treadDepth: '', remark: '有划痕' }] }, - { id: '2', completeDate: '2025-02-09 10:15', operator: '李四', vehicleType: '平板货车', plateNo: '京C11111', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=6'], inspectionList: [{ checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, { checkType: '轮胎', checkItem: '前左胎', result: '异常', treadDepth: '3.1mm', remark: '需更换' }] }, - { id: '3', completeDate: '2025-02-08 11:00', operator: '王五', vehicleType: '厢式货车', plateNo: '京D22222', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M8888888', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=7', 'https://picsum.photos/200/150?random=8'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection }, - { id: '4', completeDate: '2025-02-07 13:45', operator: '赵六', vehicleType: '栏板货车', plateNo: '京E33333', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M7777777', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '有', trailerPlate: '京E33334', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=9'], inspectionList: defaultInspection }, - { id: '5', completeDate: '2025-02-06 08:20', operator: '张三', vehicleType: '平板货车', plateNo: '京A23456', brand: '东风', model: 'DFH1190', vin: 'LGHXCAE28M6666666', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 1, adPhotos: ['https://picsum.photos/200/150?random=10'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection }, - { id: '6', completeDate: '2025-02-05 14:30', operator: '李四', vehicleType: '厢式货车', plateNo: '京B34567', brand: '福田', model: 'BJ1190', vin: 'LGHXCAE28M5555555', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 2, flawPhotos: ['https://picsum.photos/200/150?random=11', 'https://picsum.photos/200/150?random=12'], inspectionList: defaultInspection }, - { id: '7', completeDate: '2025-02-04 16:00', operator: '王五', vehicleType: '栏板货车', plateNo: '京C45678', brand: '江淮', model: 'HFC1190', vin: 'LGHXCAE28M4444444', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=13', 'https://picsum.photos/200/150?random=14'], tailboard: '有', hasTrailer: '有', trailerPlate: '京C45679', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection }, - { id: '8', completeDate: '2025-02-03 09:00', operator: '赵六', vehicleType: '厢式货车', plateNo: '京D56789', brand: '重汽', model: 'ZZ1190', vin: 'LGHXCAE28M3333333', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=15'], inspectionList: defaultInspection }, - { id: '9', completeDate: '2025-02-02 10:30', operator: '张三', vehicleType: '平板货车', plateNo: '京E67890', brand: '东风', model: 'DFH1200', vin: 'LGHXCAE28M2222222', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 3, adPhotos: ['https://picsum.photos/200/150?random=16', 'https://picsum.photos/200/150?random=17', 'https://picsum.photos/200/150?random=18'], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection }, - { id: '10', completeDate: '2025-02-01 15:45', operator: '李四', vehicleType: '厢式货车', plateNo: '京A78901', brand: '福田', model: 'BJ1200', vin: 'LGHXCAE28M1111111', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '有', hasTrailer: '有', trailerPlate: '京A78902', flawPhotoCount: 0, flawPhotos: [], inspectionList: defaultInspection } + { id: '1', completeDate: '2025-02-10 09:30', operator: '张三', vehicleType: '重型厢式货车', plateNo: '京A12345', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M1234567', parkingLot: '朝阳停车场', bodyAd: '有', adPhotos: ['https://picsum.photos/200/150?random=1', 'https://picsum.photos/200/150?random=2', 'https://picsum.photos/200/150?random=3'], tailboard: '有', spareTireTreadDepth: '5.2mm', spareTirePhotos: ['https://picsum.photos/200/150?random=sp1'], flawPhotos: ['https://picsum.photos/200/150?random=4', 'https://picsum.photos/200/150?random=5'], inspectionList: defaultInspection }, + { id: '2', completeDate: '2025-02-09 10:15', operator: '李四', vehicleType: '重型平板半挂车', plateNo: '京C11111', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M7654321', parkingLot: '海淀停车场', bodyAd: '无', adPhotos: [], tailboard: '无', spareTireTreadDepth: '3.1mm', spareTirePhotos: [], flawPhotos: ['https://picsum.photos/200/150?random=6'], inspectionList: defaultInspection }, + { id: '3', completeDate: '2025-02-08 11:00', operator: '王五', vehicleType: '轻型厢式货车', plateNo: '京D22222', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M8888888', parkingLot: '丰台停车场', bodyAd: '有', adPhotos: ['https://picsum.photos/200/150?random=7', 'https://picsum.photos/200/150?random=8'], tailboard: '有', spareTireTreadDepth: '4.0mm', spareTirePhotos: ['https://picsum.photos/200/150?random=sp2'], flawPhotos: [], inspectionList: defaultInspection }, + { id: '4', completeDate: '2025-02-07 13:45', operator: '赵六', vehicleType: '重型半挂牵引车', plateNo: '京E33333', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M7777777', parkingLot: '西城停车场', bodyAd: '无', adPhotos: [], tailboard: '无', spareTireTreadDepth: '5.0mm', spareTirePhotos: ['https://picsum.photos/200/150?random=sp3'], flawPhotos: ['https://picsum.photos/200/150?random=9'], inspectionList: defaultInspection }, + { id: '5', completeDate: '2025-02-06 08:20', operator: '张三', vehicleType: '重型厢式货车', plateNo: '京A23456', brand: '东风', model: 'DFH1190', vin: 'LGHXCAE28M6666666', parkingLot: '朝阳停车场', bodyAd: '有', adPhotos: ['https://picsum.photos/200/150?random=10'], tailboard: '有', spareTireTreadDepth: '4.5mm', spareTirePhotos: [], flawPhotos: [], inspectionList: defaultInspection }, + { id: '6', completeDate: '2025-02-05 14:30', operator: '李四', vehicleType: '轻型厢式货车', plateNo: '京B34567', brand: '福田', model: 'BJ1190', vin: 'LGHXCAE28M5555555', parkingLot: '海淀停车场', bodyAd: '无', adPhotos: [], tailboard: '无', spareTireTreadDepth: '3.8mm', spareTirePhotos: ['https://picsum.photos/200/150?random=sp4'], flawPhotos: ['https://picsum.photos/200/150?random=11', 'https://picsum.photos/200/150?random=12'], inspectionList: defaultInspection }, + { id: '7', completeDate: '2025-02-04 16:00', operator: '王五', vehicleType: '重型集装箱半挂车', plateNo: '京C45678', brand: '江淮', model: 'HFC1190', vin: 'LGHXCAE28M4444444', parkingLot: '丰台停车场', bodyAd: '有', adPhotos: ['https://picsum.photos/200/150?random=13', 'https://picsum.photos/200/150?random=14'], tailboard: '有', spareTireTreadDepth: '5.2mm', spareTirePhotos: ['https://picsum.photos/200/150?random=sp5'], flawPhotos: [], inspectionList: defaultInspection }, + { id: '8', completeDate: '2025-02-03 09:00', operator: '赵六', vehicleType: '重型厢式货车', plateNo: '京D56789', brand: '重汽', model: 'ZZ1190', vin: 'LGHXCAE28M3333333', parkingLot: '西城停车场', bodyAd: '无', adPhotos: [], tailboard: '有', spareTireTreadDepth: '4.2mm', spareTirePhotos: [], flawPhotos: ['https://picsum.photos/200/150?random=15'], inspectionList: defaultInspection }, + { id: '9', completeDate: '2025-02-02 10:30', operator: '张三', vehicleType: '小型普通客车', plateNo: '京E67890', brand: '东风', model: 'DFH1200', vin: 'LGHXCAE28M2222222', parkingLot: '朝阳停车场', bodyAd: '有', adPhotos: ['https://picsum.photos/200/150?random=16', 'https://picsum.photos/200/150?random=17', 'https://picsum.photos/200/150?random=18'], tailboard: '无', spareTireTreadDepth: '5.0mm', spareTirePhotos: ['https://picsum.photos/200/150?random=sp6'], flawPhotos: [], inspectionList: defaultInspection }, + { id: '10', completeDate: '2025-02-01 15:45', operator: '李四', vehicleType: '轻型厢式货车', plateNo: '京A78901', brand: '福田', model: 'BJ1200', vin: 'LGHXCAE28M1111111', parkingLot: '海淀停车场', bodyAd: '无', adPhotos: [], tailboard: '有', spareTireTreadDepth: '3.5mm', spareTirePhotos: [], flawPhotos: [], inspectionList: defaultInspection } ]); var completedList = completedListState[0]; var setCompletedList = completedListState[1]; var pendingListState = useState([ - { id: 'p1', operateDate: '2025-02-10 14:30', operator: '王五', vehicleType: '厢式货车', plateNo: '京F10001', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M9990001', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=19', 'https://picsum.photos/200/150?random=20'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] }, - { id: 'p2', operateDate: '2025-02-09 09:15', operator: '赵六', vehicleType: '平板货车', plateNo: '京F10002', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M9990002', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=21'] }, - { id: 'p3', operateDate: '2025-02-08 11:45', operator: '张三', vehicleType: '栏板货车', plateNo: '京F10003', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M9990003', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 1, adPhotos: ['https://picsum.photos/200/150?random=22'], tailboard: '有', hasTrailer: '有', trailerPlate: '京F10004', flawPhotoCount: 0, flawPhotos: [] }, - { id: 'p4', operateDate: '2025-02-07 08:00', operator: '李四', vehicleType: '厢式货车', plateNo: '京F10005', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M9990004', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] }, - { id: 'p5', operateDate: '2025-02-06 13:20', operator: '王五', vehicleType: '厢式货车', plateNo: '京F10006', brand: '江淮', model: 'HFC1190', vin: 'LGHXCAE28M9990005', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 3, adPhotos: ['https://picsum.photos/200/150?random=23', 'https://picsum.photos/200/150?random=24', 'https://picsum.photos/200/150?random=25'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 2, flawPhotos: ['https://picsum.photos/200/150?random=26', 'https://picsum.photos/200/150?random=27'] }, - { id: 'p6', operateDate: '2025-02-05 15:30', operator: '赵六', vehicleType: '平板货车', plateNo: '京F10007', brand: '重汽', model: 'ZZ1190', vin: 'LGHXCAE28M9990006', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '有', trailerPlate: '京F10008', flawPhotoCount: 0, flawPhotos: [] }, - { id: 'p7', operateDate: '2025-02-04 10:00', operator: '张三', vehicleType: '厢式货车', plateNo: '京F10009', brand: '东风', model: 'DFH1190', vin: 'LGHXCAE28M9990007', parkingLot: '朝阳停车场', bodyAd: '有', adPhotoCount: 2, adPhotos: ['https://picsum.photos/200/150?random=28', 'https://picsum.photos/200/150?random=29'], tailboard: '有', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] }, - { id: 'p8', operateDate: '2025-02-03 16:45', operator: '李四', vehicleType: '栏板货车', plateNo: '京F10010', brand: '福田', model: 'BJ1190', vin: 'LGHXCAE28M9990008', parkingLot: '海淀停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 1, flawPhotos: ['https://picsum.photos/200/150?random=30'] }, - { id: 'p9', operateDate: '2025-02-02 09:30', operator: '王五', vehicleType: '厢式货车', plateNo: '京F10011', brand: '江淮', model: 'HFC1200', vin: 'LGHXCAE28M9990009', parkingLot: '丰台停车场', bodyAd: '有', adPhotoCount: 1, adPhotos: ['https://picsum.photos/200/150?random=31'], tailboard: '有', hasTrailer: '有', trailerPlate: '京F10012', flawPhotoCount: 0, flawPhotos: [] }, - { id: 'p10', operateDate: '2025-02-01 14:00', operator: '赵六', vehicleType: '平板货车', plateNo: '京F10013', brand: '重汽', model: 'ZZ1200', vin: 'LGHXCAE28M9990010', parkingLot: '西城停车场', bodyAd: '无', adPhotoCount: 0, adPhotos: [], tailboard: '无', hasTrailer: '无', trailerPlate: '', flawPhotoCount: 0, flawPhotos: [] } + { id: 'p1', operateDate: '2025-02-10 14:30', operator: '王五', vehicleType: '轻型厢式货车', plateNo: '京F10001', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M9990001', parkingLot: '丰台停车场', bodyAd: '有', adPhotos: ['https://picsum.photos/200/150?random=19', 'https://picsum.photos/200/150?random=20'], tailboard: '有', spareTireTreadDepth: '4.0mm', spareTirePhotos: ['https://picsum.photos/200/150?random=sp7'], flawPhotos: [] }, + { id: 'p2', operateDate: '2025-02-09 09:15', operator: '赵六', vehicleType: '重型平板半挂车', plateNo: '京F10002', brand: '重汽', model: 'ZZ1180', vin: 'LGHXCAE28M9990002', parkingLot: '西城停车场', bodyAd: '无', adPhotos: [], tailboard: '无', spareTireTreadDepth: '3.1mm', spareTirePhotos: [], flawPhotos: ['https://picsum.photos/200/150?random=21'] }, + { id: 'p3', operateDate: '2025-02-08 11:45', operator: '张三', vehicleType: '重型半挂牵引车', plateNo: '京F10003', brand: '东风', model: 'DFH1180', vin: 'LGHXCAE28M9990003', parkingLot: '朝阳停车场', bodyAd: '有', adPhotos: ['https://picsum.photos/200/150?random=22'], tailboard: '有', spareTireTreadDepth: '5.0mm', spareTirePhotos: [], flawPhotos: [] }, + { id: 'p4', operateDate: '2025-02-07 08:00', operator: '李四', vehicleType: '重型厢式货车', plateNo: '京F10005', brand: '福田', model: 'BJ1180', vin: 'LGHXCAE28M9990004', parkingLot: '海淀停车场', bodyAd: '无', adPhotos: [], tailboard: '无', spareTireTreadDepth: '4.2mm', spareTirePhotos: ['https://picsum.photos/200/150?random=sp8'], flawPhotos: [] }, + { id: 'p5', operateDate: '2025-02-06 13:20', operator: '王五', vehicleType: '轻型厢式货车', plateNo: '京F10006', brand: '江淮', model: 'HFC1190', vin: 'LGHXCAE28M9990005', parkingLot: '丰台停车场', bodyAd: '有', adPhotos: ['https://picsum.photos/200/150?random=23', 'https://picsum.photos/200/150?random=24', 'https://picsum.photos/200/150?random=25'], tailboard: '有', spareTireTreadDepth: '3.8mm', spareTirePhotos: [], flawPhotos: ['https://picsum.photos/200/150?random=26', 'https://picsum.photos/200/150?random=27'] }, + { id: 'p6', operateDate: '2025-02-05 15:30', operator: '赵六', vehicleType: '重型集装箱半挂车', plateNo: '京F10007', brand: '重汽', model: 'ZZ1190', vin: 'LGHXCAE28M9990006', parkingLot: '西城停车场', bodyAd: '无', adPhotos: [], tailboard: '无', spareTireTreadDepth: '5.2mm', spareTirePhotos: ['https://picsum.photos/200/150?random=sp9'], flawPhotos: [] }, + { id: 'p7', operateDate: '2025-02-04 10:00', operator: '张三', vehicleType: '重型厢式货车', plateNo: '京F10009', brand: '东风', model: 'DFH1190', vin: 'LGHXCAE28M9990007', parkingLot: '朝阳停车场', bodyAd: '有', adPhotos: ['https://picsum.photos/200/150?random=28', 'https://picsum.photos/200/150?random=29'], tailboard: '有', spareTireTreadDepth: '4.5mm', spareTirePhotos: [], flawPhotos: [] }, + { id: 'p8', operateDate: '2025-02-03 16:45', operator: '李四', vehicleType: '小型普通客车', plateNo: '京F10010', brand: '福田', model: 'BJ1190', vin: 'LGHXCAE28M9990008', parkingLot: '海淀停车场', bodyAd: '无', adPhotos: [], tailboard: '无', spareTireTreadDepth: '3.5mm', spareTirePhotos: [], flawPhotos: ['https://picsum.photos/200/150?random=30'] }, + { id: 'p9', operateDate: '2025-02-02 09:30', operator: '王五', vehicleType: '轻型厢式货车', plateNo: '京F10011', brand: '江淮', model: 'HFC1200', vin: 'LGHXCAE28M9990009', parkingLot: '丰台停车场', bodyAd: '有', adPhotos: ['https://picsum.photos/200/150?random=31'], tailboard: '有', spareTireTreadDepth: '5.0mm', spareTirePhotos: ['https://picsum.photos/200/150?random=sp10'], flawPhotos: [] }, + { id: 'p10', operateDate: '2025-02-01 14:00', operator: '赵六', vehicleType: '重型平板半挂车', plateNo: '京F10013', brand: '重汽', model: 'ZZ1200', vin: 'LGHXCAE28M9990010', parkingLot: '西城停车场', bodyAd: '无', adPhotos: [], tailboard: '无', spareTireTreadDepth: '4.0mm', spareTirePhotos: [], flawPhotos: [] } ]); var pendingList = pendingListState[0]; var setPendingList = pendingListState[1]; @@ -94,10 +97,50 @@ const Component = function () { var pageSize = pageSizeState[0]; var setPageSize = pageSizeState[1]; - var operatorOptions = useMemo(function () { return [{ value: '张三', label: '张三' }, { value: '李四', label: '李四' }, { value: '王五', label: '王五' }, { value: '赵六', label: '赵六' }]; }, []); - var plateOptions = useMemo(function () { return [{ value: '京A12345', label: '京A12345' }, { value: '京C11111', label: '京C11111' }, { value: '京D22222', label: '京D22222' }, { value: '京E33333', label: '京E33333' }]; }, []); - var vehicleTypeOptions = useMemo(function () { return [{ value: '厢式货车', label: '厢式货车' }, { value: '平板货车', label: '平板货车' }, { value: '栏板货车', label: '栏板货车' }]; }, []); + var operatorOptions = useMemo(function () { + var set = new Set(); + completedList.forEach(function (r) { if (r.operator) set.add(r.operator); }); + pendingList.forEach(function (r) { if (r.operator) set.add(r.operator); }); + return Array.from(set).map(function (v) { return { value: v, label: v }; }); + }, [completedList, pendingList]); + var plateOptions = useMemo(function () { + var set = new Set(); + completedList.forEach(function (r) { if (r.plateNo) set.add(r.plateNo); }); + pendingList.forEach(function (r) { if (r.plateNo) set.add(r.plateNo); }); + return Array.from(set).map(function (v) { return { value: v, label: v }; }); + }, [completedList, pendingList]); + var vehicleTypeOptions = useMemo(function () { + return [ + { value: '轻型厢式货车', label: '轻型厢式货车' }, + { value: '重型厢式货车', label: '重型厢式货车' }, + { value: '重型半挂牵引车', label: '重型半挂牵引车' }, + { value: '重型集装箱半挂车', label: '重型集装箱半挂车' }, + { value: '小型普通客车', label: '小型普通客车' }, + { value: '重型平板半挂车', label: '重型平板半挂车' }, + { value: '叉车', label: '叉车' }, + { value: '油车', label: '油车' }, + { value: '观光车', label: '观光车' } + ]; + }, []); var parkingOptions = useMemo(function () { return [{ value: '朝阳停车场', label: '朝阳停车场' }, { value: '海淀停车场', label: '海淀停车场' }, { value: '丰台停车场', label: '丰台停车场' }, { value: '西城停车场', label: '西城停车场' }]; }, []); + var vehicleModelCascaderOptions = useMemo(function () { + var map = {}; + completedList.forEach(function (r) { + if (!r.brand) return; + if (!map[r.brand]) map[r.brand] = { value: r.brand, label: r.brand, children: [] }; + var children = map[r.brand].children; + var model = r.model || ''; + if (model && children.every(function (c) { return c.value !== model; })) children.push({ value: model, label: model }); + }); + pendingList.forEach(function (r) { + if (!r.brand) return; + if (!map[r.brand]) map[r.brand] = { value: r.brand, label: r.brand, children: [] }; + var children = map[r.brand].children; + var model = r.model || ''; + if (model && children.every(function (c) { return c.value !== model; })) children.push({ value: model, label: model }); + }); + return Object.keys(map).map(function (k) { return map[k]; }); + }, [completedList, pendingList]); var reqDocOpenState = useState(false); var photoModalState = useState({ open: false, photos: [], currentIndex: 0 }); @@ -107,12 +150,14 @@ const Component = function () { plateNo: '', vehicleType: '', brand: '', + model: '', vin: '', bodyAd: '无', adPhotos: [], tailboard: '无', - hasTrailer: '', trailerPlate: '', + spareTireTreadDepth: '', + spareTirePhotos: [], flawPhotos: [], inspectionList: [ { checkType: '灯光', checkItem: '前大灯', result: '正常', treadDepth: '', remark: '' }, @@ -124,10 +169,19 @@ const Component = function () { var filteredCompleted = useMemo(function () { var list = completedList.slice(); - if (appliedFilters.operator) list = list.filter(function (r) { return r.operator.indexOf(appliedFilters.operator) !== -1; }); - if (appliedFilters.plateNo) list = list.filter(function (r) { return r.plateNo.indexOf(appliedFilters.plateNo) !== -1; }); - if (appliedFilters.vehicleType) list = list.filter(function (r) { return r.vehicleType.indexOf(appliedFilters.vehicleType) !== -1; }); - if (appliedFilters.parkingLot) list = list.filter(function (r) { return r.parkingLot.indexOf(appliedFilters.parkingLot) !== -1; }); + if (appliedFilters.operator) list = list.filter(function (r) { return (r.operator || '').indexOf(appliedFilters.operator) !== -1; }); + if (appliedFilters.plateNo) list = list.filter(function (r) { return (r.plateNo || '').indexOf(appliedFilters.plateNo) !== -1; }); + if (appliedFilters.vehicleType) list = list.filter(function (r) { return r.vehicleType === appliedFilters.vehicleType; }); + if (appliedFilters.vehicleModelCascader && appliedFilters.vehicleModelCascader.length >= 1) { + var brand = appliedFilters.vehicleModelCascader[0]; + var model = appliedFilters.vehicleModelCascader[1]; + list = list.filter(function (r) { + if (r.brand !== brand) return false; + if (model && r.model !== model) return false; + return true; + }); + } + if (appliedFilters.parkingLot && appliedFilters.parkingLot.length > 0) list = list.filter(function (r) { return appliedFilters.parkingLot.indexOf(r.parkingLot) !== -1; }); if (appliedFilters.dateStart) list = list.filter(function (r) { var d = (r.completeDate || '').slice(0, 10); return d >= appliedFilters.dateStart; }); if (appliedFilters.dateEnd) list = list.filter(function (r) { var d = (r.completeDate || '').slice(0, 10); return d <= appliedFilters.dateEnd; }); return list.sort(function (a, b) { return (b.completeDate || '').localeCompare(a.completeDate || ''); }); @@ -135,10 +189,19 @@ const Component = function () { var filteredPending = useMemo(function () { var list = pendingList.slice(); - if (appliedFilters.operator) list = list.filter(function (r) { return r.operator.indexOf(appliedFilters.operator) !== -1; }); - if (appliedFilters.plateNo) list = list.filter(function (r) { return r.plateNo.indexOf(appliedFilters.plateNo) !== -1; }); - if (appliedFilters.vehicleType) list = list.filter(function (r) { return r.vehicleType.indexOf(appliedFilters.vehicleType) !== -1; }); - if (appliedFilters.parkingLot) list = list.filter(function (r) { return r.parkingLot.indexOf(appliedFilters.parkingLot) !== -1; }); + if (appliedFilters.operator) list = list.filter(function (r) { return (r.operator || '').indexOf(appliedFilters.operator) !== -1; }); + if (appliedFilters.plateNo) list = list.filter(function (r) { return (r.plateNo || '').indexOf(appliedFilters.plateNo) !== -1; }); + if (appliedFilters.vehicleType) list = list.filter(function (r) { return r.vehicleType === appliedFilters.vehicleType; }); + if (appliedFilters.vehicleModelCascader && appliedFilters.vehicleModelCascader.length >= 1) { + var brand = appliedFilters.vehicleModelCascader[0]; + var model = appliedFilters.vehicleModelCascader[1]; + list = list.filter(function (r) { + if (r.brand !== brand) return false; + if (model && r.model !== model) return false; + return true; + }); + } + if (appliedFilters.parkingLot && appliedFilters.parkingLot.length > 0) list = list.filter(function (r) { return appliedFilters.parkingLot.indexOf(r.parkingLot) !== -1; }); if (appliedFilters.dateStart) list = list.filter(function (r) { var d = (r.operateDate || '').slice(0, 10); return d >= appliedFilters.dateStart; }); if (appliedFilters.dateEnd) list = list.filter(function (r) { var d = (r.operateDate || '').slice(0, 10); return d <= appliedFilters.dateEnd; }); return list.sort(function (a, b) { return (b.operateDate || '').localeCompare(a.operateDate || ''); }); @@ -159,11 +222,11 @@ const Component = function () { var totalCount = activeTab === 'completed' ? totalCompleted : totalPending; var handleQuery = useCallback(function () { - setAppliedFilters({ dateStart: filters.dateStart, dateEnd: filters.dateEnd, operator: filters.operator, plateNo: filters.plateNo, vehicleType: filters.vehicleType, parkingLot: filters.parkingLot }); + setAppliedFilters({ dateStart: filters.dateStart, dateEnd: filters.dateEnd, operator: filters.operator, plateNo: filters.plateNo, vehicleType: filters.vehicleType, vehicleModelCascader: filters.vehicleModelCascader, parkingLot: filters.parkingLot || [] }); setPage(1); - }, [filters.dateStart, filters.dateEnd, filters.operator, filters.plateNo, filters.vehicleType, filters.parkingLot]); + }, [filters.dateStart, filters.dateEnd, filters.operator, filters.plateNo, filters.vehicleType, filters.vehicleModelCascader, filters.parkingLot]); var handleReset = useCallback(function () { - var empty = { dateStart: '', dateEnd: '', operator: undefined, plateNo: undefined, vehicleType: undefined, parkingLot: undefined }; + var empty = { dateStart: '', dateEnd: '', operator: undefined, plateNo: undefined, vehicleType: undefined, vehicleModelCascader: undefined, parkingLot: [] }; setFilters(empty); setAppliedFilters(empty); setPage(1); @@ -175,10 +238,10 @@ const Component = function () { return; } var rows = filteredCompleted; - var headers = ['备车时间', '备车人', '车辆类型', '车牌号', '品牌', '型号', '车辆识别代码', '停车场', '车身广告', '广告照片', '尾板', '是否有挂', '挂车牌号', '瑕疵照片']; + var headers = ['备车时间', '备车人', '车辆类型', '车牌号', '品牌', '型号', '车辆识别代码', '停车场', '车身广告及放大字', '尾板', '备胎胎纹深度']; var csv = headers.join(',') + '\n'; rows.forEach(function (r) { - csv += [r.completeDate, r.operator, r.vehicleType, r.plateNo, r.brand, r.model, r.vin, r.parkingLot, r.bodyAd, (r.adPhotoCount || 0) + '张', r.tailboard, r.hasTrailer, r.trailerPlate || '', (r.flawPhotoCount || 0) + '张'].join(',') + '\n'; + csv += [(r.completeDate || '').slice(0, 10), r.operator, r.vehicleType, r.plateNo, r.brand, r.model, r.vin, r.parkingLot, r.bodyAd, r.tailboard, r.spareTireTreadDepth || ''].join(',') + '\n'; }); var blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' }); var url = URL.createObjectURL(blob); @@ -190,6 +253,14 @@ const Component = function () { message.success('导出成功'); }, [activeTab, filteredCompleted]); + var handlePendingDelete = useCallback(function () { + if (pendingDeleteId) { + setPendingList(function (prev) { return prev.filter(function (r) { return r.id !== pendingDeleteId; }); }); + message.success('已删除'); + } + setPendingDeleteId(null); + }, [pendingDeleteId]); + var openPhotoModal = useCallback(function (photos, index) { if (!photos || photos.length === 0) return; photoModalState[1]({ open: true, photos: photos, currentIndex: index || 0 }); @@ -208,25 +279,24 @@ const Component = function () { id: 'p' + Date.now(), operateDate: dateTimeStr, operator: '当前用户', - vehicleType: addForm.vehicleType || '厢式货车', + vehicleType: addForm.vehicleType || '轻型厢式货车', plateNo: addForm.plateNo, brand: addForm.brand || '-', model: addForm.model || '-', vin: addForm.vin, parkingLot: addForm.parkingLot || '朝阳停车场', bodyAd: addForm.bodyAd, - adPhotoCount: (addForm.adPhotos && addForm.adPhotos.length) || 0, adPhotos: addForm.adPhotos || [], tailboard: addForm.tailboard, - hasTrailer: addForm.trailerPlate ? '有' : '无', trailerPlate: addForm.trailerPlate || '', - flawPhotoCount: (addForm.flawPhotos && addForm.flawPhotos.length) || 0, + spareTireTreadDepth: addForm.spareTireTreadDepth || '', + spareTirePhotos: addForm.spareTirePhotos || [], flawPhotos: addForm.flawPhotos || [] }; setPendingList(pendingList.concat([item])); setCurrentView('list'); setActiveTab('pending'); - setAddForm({ plateNo: '', vehicleType: '', brand: '', vin: '', bodyAd: '无', adPhotos: [], tailboard: '无', hasTrailer: '', trailerPlate: '', flawPhotos: [], inspectionList: addForm.inspectionList }); + setAddForm({ plateNo: '', vehicleType: '', brand: '', model: '', vin: '', bodyAd: '无', adPhotos: [], tailboard: '无', trailerPlate: '', spareTireTreadDepth: '', spareTirePhotos: [], flawPhotos: [], inspectionList: addForm.inspectionList }); setAddStep(1); message.success('已保存至待提交列表'); }, [addForm, pendingList]); @@ -247,26 +317,24 @@ const Component = function () { id: String(Date.now()), completeDate: dateTimeStr, operator: '当前用户', - vehicleType: addForm.vehicleType || '厢式货车', + vehicleType: addForm.vehicleType || '轻型厢式货车', plateNo: addForm.plateNo, brand: addForm.brand || '-', model: addForm.model || '-', vin: addForm.vin, parkingLot: addForm.parkingLot || '朝阳停车场', bodyAd: addForm.bodyAd, - adPhotoCount: (addForm.adPhotos && addForm.adPhotos.length) || 0, adPhotos: addForm.adPhotos || [], tailboard: addForm.tailboard, - hasTrailer: addForm.trailerPlate ? '有' : '无', - trailerPlate: addForm.trailerPlate || '', - flawPhotoCount: (addForm.flawPhotos && addForm.flawPhotos.length) || 0, + spareTireTreadDepth: addForm.spareTireTreadDepth || '', + spareTirePhotos: addForm.spareTirePhotos || [], flawPhotos: addForm.flawPhotos || [], inspectionList: addForm.inspectionList || [] }; setCompletedList(completedList.concat([item])); setCurrentView('list'); setActiveTab('completed'); - setAddForm({ plateNo: '', vehicleType: '', brand: '', vin: '', bodyAd: '无', adPhotos: [], tailboard: '无', hasTrailer: '', trailerPlate: '', flawPhotos: [], inspectionList: addForm.inspectionList }); + setAddForm({ plateNo: '', vehicleType: '', brand: '', model: '', vin: '', bodyAd: '无', adPhotos: [], tailboard: '无', trailerPlate: '', spareTireTreadDepth: '', spareTirePhotos: [], flawPhotos: [], inspectionList: addForm.inspectionList }); setAddStep(1); message.success('提交成功'); }, [addForm, completedList]); @@ -283,7 +351,7 @@ const Component = function () { }); }, []); - var requirementDocContent = '#.面包屑:运维管理-车辆业务-备车管理,支持点击对应模块跳转\n\n#筛选:支持通过备车日期、备车人、车牌号、车辆类型、停车场等方式进行筛选;\n1.备车日期:日期选择器,点击显示两个日历控件,支持选择开始和结束时间,时间格式为YYYY-MM-DD;\n2.备车人:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n3.车牌号:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n4.车辆类型:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n5.停车场:选择器,支持从输入框输入内容模糊搜索展示对应选项;\n6.右侧显示查询、重置按钮,点击查询按钮按筛选条件显示列表内容,点击重置清除筛选条件并按默认条件显示列表内容;\n\n#列表:\n列表分为两个tab,分别为:已完成、待检查;已完成列表右侧包含新增、导出按钮,下方增加分页功能,支持选择单页显示数据记录条数;\n1.已完成tab显示完成车辆记录及备车检查两个步骤表单并点击提交的记录;\n2.待检查tab显示新增备车时只点击保存,但未提交的记录;\n3.点击新增按钮,打开新页面进行新增表单页;\n4.导出需全选/多选对应数据记录,再点击导出下载csv;\n5.列表以区域进行数据权限划分,用户表中对应区域的用户只能查询自己区域的数据;'; + var requirementDocContent = '备车管理:\n一个「数字化资产ONEOS运管平台」中的「备车管理」模块\n1.面包屑:\n1.1.运维管理-车辆业务-备车管理\n\n2.筛选:\n2.1.备车日期:日期选择器,精确至天,支持单输入框双日历选择开始日期-结束日期;\n2.2.备车人:选择器,支持输入框内输入进行模糊搜索下拉选项匹配,提示信息为:请选择或输入备车人姓名;\n2.3.车牌号:选择器,支持输入框内输入进行模糊搜索下拉选项匹配,提示信息为:请选择或输入车牌号;\n2.4.车辆类型:选择器,选项包含:轻型厢式货车、重型厢式货车、重型半挂牵引车、轻型厢式货车、重型厢式货车、重型集装箱半挂车、小型普通客车、重型半挂牵引车、重型平板半挂车、叉车、油车、观光车;\n2.5.车辆型号:级联选择器,选择品牌及型号;\n2.5.停车场:选择器,支持多选,选择所有停车场;\n\n3.列表:分为已完成、待提交2个tab,已完成tab下显示所有已完成备车的车辆,待提交tab下显示所有仅保存待提交的车辆;\n3.1.已完成,列表右侧为新增、导出:\n 3.1.1.备车时间:显示备车完成时间,格式为:YYYY-MM-DD;\n 3.1.2.备车人:显示备车用户姓名;\n 3.1.3.车辆类型:显示备车车辆类型;\n 3.1.4.品牌:显示备车车辆品牌;\n 3.1.5.型号:显示备车车辆型号;\n 3.1.6.车牌号:显示备车车辆车牌号;\n 3.1.7.车辆识别代码:显示备车车辆识别代码;\n 3.1.8.停车场:显示备车车辆所在停车场;\n 3.1.9.车身广告及放大字:显示备车车辆是否有车身广告及放大字,显示为有或无;\n 3.1.10.尾板:如尾板为有,显示备车车辆是否有尾板,显示为有或无;\n 3.1.11.备胎胎纹深度:显示备车车辆备胎胎纹深度,格式为:xxmm;\n 3.1.12.操作:查看;\n 3.1.12.1.查看:点击查看,进入备车管理-待提交-查看页;\n\n3.2.待提交,列表右侧为新增:\n 3.2.1.创建时间:显示记录创建时间,格式为:YYYY-MM-DD;\n 3.2.2.创建人:显示创建用户姓名;\n 3.2.3.车辆类型:显示备车车辆类型;\n 3.2.4.品牌:显示备车车辆品牌;\n 3.2.5.型号:显示备车车辆型号;\n 3.2.6.车牌号:显示备车车辆车牌号;\n 3.2.7.车辆识别代码:显示备车车辆识别代码;\n 3.2.8.停车场:显示备车车辆所在停车场;\n 3.2.9.车身广告及放大字:显示备车车辆是否有车身广告及放大字,显示为有或无;\n 3.2.10.广告照片:如车身广告及放大字为有,则显示查看照片,如车身广告及放大字为无,则显示-;\n 3.2.11.放大字照片:如车身广告及放大字为有,则显示查看照片,如车身广告及放大字为无,则显示-;\n 3.2.12.尾板:如尾板为有,显示备车车辆是否有尾板,显示为有或无;\n 3.2.13.备胎胎纹深度:显示备车车辆备胎胎纹深度,格式为:xxmm;\n 3.2.14.备胎照片:如备胎照片为有,则显示查看照片,如备胎为无,则显示-;\n 3.2.15.瑕疵照片:如瑕疵照片为有,则显示查看照片,如瑕疵照片为无,则显示-;\n 3.2.16.操作:查看、编辑、删除;\n 3.2.16.1.查看:点击查看,进入备车管理-待提交-查看页;\n 3.2.16.2.编辑:点击编辑,进入备车管理-待提交-编辑页;\n 3.2.16.3.删除:点击进行二次确认,确认后删除;\n\n已完成、待提交均支持分页功能,可设置单页查询数据条数;\n\n备车权限说明:\n1.运维人员分区域,对应区域运维人员只能新增自己区域停车场的车辆进行备车;\n2.运维人员只能查看自己区域停车场的备车记录;'; // 日期范围变化(antd RangePicker 返回 dayjs 或 [string, string] 依项目而定,此处用字符串存) function onDateRangeChange(dates, dateStrings) { @@ -306,70 +374,60 @@ const Component = function () { return null; }, [filters.dateStart, filters.dateEnd]); - // 已完成表格列(含多选、照片链接、操作列查看按钮,点击不打开新页面) - function getCompletedColumns(openPhoto) { + // 已完成表格列 + function getCompletedColumns() { return [ - { title: '备车时间', dataIndex: 'completeDate', key: 'completeDate', width: 160 }, - { title: '备车人', dataIndex: 'operator', key: 'operator', width: 90 }, - { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 }, - { title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 }, - { title: '型号', dataIndex: 'model', key: 'model', width: 100 }, - { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 }, - { title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 180 }, - { title: '停车场', dataIndex: 'parkingLot', key: 'parkingLot', width: 110 }, - { title: '车身广告', dataIndex: 'bodyAd', key: 'bodyAd', width: 90 }, - { title: '广告照片', key: 'adPhotos', width: 90, render: function (_, r) { - var photos = r.adPhotos && r.adPhotos.length > 0 ? r.adPhotos : []; - return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-'; - }}, - { title: '尾板', dataIndex: 'tailboard', key: 'tailboard', width: 70 }, - { title: '是否有挂', dataIndex: 'hasTrailer', key: 'hasTrailer', width: 90 }, - { title: '挂车牌号', dataIndex: 'trailerPlate', key: 'trailerPlate', width: 100, render: function (v) { return v || '-'; } }, - { title: '瑕疵照片', key: 'flawPhotos', width: 90, render: function (_, r) { - var photos = r.flawPhotos && r.flawPhotos.length > 0 ? r.flawPhotos : []; - return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-'; - }}, + { title: '备车时间', dataIndex: 'completeDate', key: 'completeDate', width: 120, render: function (v) { return v ? String(v).slice(0, 10) : '—'; } }, + { title: '备车人', dataIndex: 'operator', key: 'operator', width: 90, render: function (v) { return v || '—'; } }, + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 120, render: function (v) { return v || '—'; } }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 80, render: function (v) { return v || '—'; } }, + { title: '型号', dataIndex: 'model', key: 'model', width: 100, render: function (v) { return v || '—'; } }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100, render: function (v) { return v || '—'; } }, + { title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 180, ellipsis: true, render: function (v) { return v || '—'; } }, + { title: '停车场', dataIndex: 'parkingLot', key: 'parkingLot', width: 110, render: function (v) { return v || '—'; } }, + { title: '车身广告及放大字', dataIndex: 'bodyAd', key: 'bodyAd', width: 120, render: function (v) { return v === '有' || v === '无' ? v : '—'; } }, + { title: '尾板', dataIndex: 'tailboard', key: 'tailboard', width: 70, render: function (v) { return v === '有' || v === '无' ? v : '—'; } }, + { title: '备胎胎纹深度', key: 'spareTireTreadDepth', width: 110, render: function (_, r) { var d = r.spareTireTreadDepth; return d ? (String(d).replace(/mm$/, '') + 'mm') : '—'; } }, { title: '操作', key: 'action', width: 80, fixed: 'right', render: function (_, r) { return React.createElement(Button, { type: 'link', size: 'small', onClick: function () {} }, '查看'); }} ]; } - function getPendingColumns(openPhoto, onEdit) { + var pendingDeleteIdState = useState(null); + var pendingDeleteId = pendingDeleteIdState[0]; + var setPendingDeleteId = pendingDeleteIdState[1]; + + function getPendingColumns() { return [ - { title: '创建时间', dataIndex: 'operateDate', key: 'operateDate', width: 160 }, - { title: '备车人', dataIndex: 'operator', key: 'operator', width: 90 }, - { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 100 }, - { title: '品牌', dataIndex: 'brand', key: 'brand', width: 80 }, - { title: '型号', dataIndex: 'model', key: 'model', width: 100 }, - { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100 }, - { title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 180 }, - { title: '停车场', dataIndex: 'parkingLot', key: 'parkingLot', width: 110 }, - { title: '车身广告', dataIndex: 'bodyAd', key: 'bodyAd', width: 90 }, - { title: '广告照片', key: 'adPhotos', width: 90, render: function (_, r) { - var photos = (r.adPhotos && r.adPhotos.length > 0) ? r.adPhotos : []; - return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-'; - }}, - { title: '尾板', dataIndex: 'tailboard', key: 'tailboard', width: 70 }, - { title: '是否有挂', dataIndex: 'hasTrailer', key: 'hasTrailer', width: 90 }, - { title: '挂车牌号', dataIndex: 'trailerPlate', key: 'trailerPlate', width: 100, render: function (v) { return v || '-'; } }, - { title: '瑕疵照片', key: 'flawPhotos', width: 90, render: function (_, r) { - var photos = (r.flawPhotos && r.flawPhotos.length > 0) ? r.flawPhotos : []; - return photos.length > 0 ? React.createElement(Button, { type: 'link', size: 'small', onClick: function (e) { if (e) { e.preventDefault(); e.stopPropagation(); } openPhoto(photos, 0); } }, photos.length + '张') : '-'; - }}, - { title: '操作', key: 'action', width: 80, fixed: 'right', render: function (_, r) { - return React.createElement(Button, { type: 'link', size: 'small', onClick: function () { - setAddForm(function (prev) { - var n = {}; - for (var k in prev) n[k] = prev[k]; - n.plateNo = r.plateNo; n.vehicleType = r.vehicleType; n.brand = r.brand; n.vin = r.vin; - n.bodyAd = r.bodyAd; n.adPhotos = r.adPhotos || []; n.tailboard = r.tailboard; - n.trailerPlate = r.trailerPlate || ''; n.flawPhotos = r.flawPhotos || []; - return n; - }); - setAddStep(1); - setCurrentView('add'); - } }, '编辑'); + { title: '创建时间', dataIndex: 'operateDate', key: 'operateDate', width: 120, render: function (v) { return v ? String(v).slice(0, 10) : '—'; } }, + { title: '创建人', dataIndex: 'operator', key: 'operator', width: 90, render: function (v) { return v || '—'; } }, + { title: '车辆类型', dataIndex: 'vehicleType', key: 'vehicleType', width: 120, render: function (v) { return v || '—'; } }, + { title: '品牌', dataIndex: 'brand', key: 'brand', width: 80, render: function (v) { return v || '—'; } }, + { title: '型号', dataIndex: 'model', key: 'model', width: 100, render: function (v) { return v || '—'; } }, + { title: '车牌号', dataIndex: 'plateNo', key: 'plateNo', width: 100, render: function (v) { return v || '—'; } }, + { title: '车辆识别代码', dataIndex: 'vin', key: 'vin', width: 180, ellipsis: true, render: function (v) { return v || '—'; } }, + { title: '停车场', dataIndex: 'parkingLot', key: 'parkingLot', width: 110, render: function (v) { return v || '—'; } }, + { title: '车身广告及放大字', dataIndex: 'bodyAd', key: 'bodyAd', width: 120, render: function (v) { return v === '有' || v === '无' ? v : '—'; } }, + { title: '尾板', dataIndex: 'tailboard', key: 'tailboard', width: 70, render: function (v) { return v === '有' || v === '无' ? v : '—'; } }, + { title: '备胎胎纹深度', key: 'spareTireTreadDepth', width: 110, render: function (_, r) { var d = r.spareTireTreadDepth; return d ? (String(d).replace(/mm$/, '') + 'mm') : '—'; } }, + { title: '操作', key: 'action', width: 160, fixed: 'right', render: function (_, r) { + return React.createElement(Space, { size: 'small' }, + React.createElement(Button, { type: 'link', size: 'small', onClick: function () {} }, '查看'), + React.createElement(Button, { type: 'link', size: 'small', onClick: function () { + setAddForm(function (prev) { + var n = {}; + for (var k in prev) n[k] = prev[k]; + n.plateNo = r.plateNo; n.vehicleType = r.vehicleType; n.brand = r.brand; n.model = r.model; n.vin = r.vin; + n.bodyAd = r.bodyAd; n.adPhotos = r.adPhotos || []; n.tailboard = r.tailboard; + n.trailerPlate = r.trailerPlate || ''; n.flawPhotos = r.flawPhotos || []; + return n; + }); + setAddStep(1); + setCurrentView('add'); + } }, '编辑'), + React.createElement(Button, { type: 'link', size: 'small', danger: true, onClick: function () { setPendingDeleteId(r.id); } }, '删除') + ); }} ]; } @@ -440,10 +498,10 @@ const Component = function () { React.createElement('div', null, React.createElement('div', { style: { marginBottom: 4 } }, '备车人'), React.createElement(Select, { - placeholder: '请选择备车人', + placeholder: '请选择或输入备车人姓名', allowClear: true, showSearch: true, - optionFilterProp: 'label', + filterOption: function (input, opt) { return (opt && opt.label && String(opt.label).toLowerCase().indexOf((input || '').toLowerCase()) !== -1); }, value: filters.operator, onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.operator = v; return g; }); }, style: { width: '100%' }, @@ -453,10 +511,10 @@ const Component = function () { React.createElement('div', null, React.createElement('div', { style: { marginBottom: 4 } }, '车牌号'), React.createElement(Select, { - placeholder: '请选择车牌号', + placeholder: '请选择或输入车牌号', allowClear: true, showSearch: true, - optionFilterProp: 'label', + filterOption: function (input, opt) { return (opt && opt.label && String(opt.label).toLowerCase().indexOf((input || '').toLowerCase()) !== -1); }, value: filters.plateNo, onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.plateNo = v; return g; }); }, style: { width: '100%' }, @@ -468,23 +526,32 @@ const Component = function () { React.createElement(Select, { placeholder: '请选择车辆类型', allowClear: true, - showSearch: true, - optionFilterProp: 'label', value: filters.vehicleType, onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.vehicleType = v; return g; }); }, style: { width: '100%' }, options: vehicleTypeOptions }) ), + React.createElement('div', null, + React.createElement('div', { style: { marginBottom: 4 } }, '车辆型号'), + React.createElement(Cascader, { + options: vehicleModelCascaderOptions, + value: filters.vehicleModelCascader, + onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.vehicleModelCascader = v; return g; }); }, + placeholder: '请选择品牌及型号', + allowClear: true, + style: { width: '100%' }, + changeOnSelect: true + }) + ), React.createElement('div', null, React.createElement('div', { style: { marginBottom: 4 } }, '停车场'), React.createElement(Select, { placeholder: '请选择停车场', allowClear: true, - showSearch: true, - optionFilterProp: 'label', - value: filters.parkingLot, - onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.parkingLot = v; return g; }); }, + mode: 'multiple', + value: filters.parkingLot || [], + onChange: function (v) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.parkingLot = v || []; return g; }); }, style: { width: '100%' }, options: parkingOptions }) @@ -508,11 +575,11 @@ const Component = function () { key: 'completed', label: '已完成', children: React.createElement(Table, { - columns: getCompletedColumns(openPhotoModal), + columns: getCompletedColumns(), dataSource: displayCompleted, rowKey: 'id', pagination: tablePagination, - scroll: { x: 1400 }, + scroll: { x: 1600 }, size: 'middle' }) }, @@ -520,17 +587,25 @@ const Component = function () { key: 'pending', label: '待提交', children: React.createElement(Table, { - columns: getPendingColumns(openPhotoModal), + columns: getPendingColumns(), dataSource: displayPending, rowKey: 'id', pagination: tablePagination, - scroll: { x: 1400 }, + scroll: { x: 1600 }, size: 'middle' }) } ] }) ), + React.createElement(Modal, { + title: '确认删除', + open: !!pendingDeleteId, + onCancel: function () { setPendingDeleteId(null); }, + onOk: handlePendingDelete, + okText: '确认', + cancelText: '取消' + }, '确定要删除该条待提交记录吗?'), // 照片预览:全屏遮罩 + 放大图片,点击遮罩关闭 photoModalState[0].open && photoModalState[0].photos && photoModalState[0].photos.length > 0 ? React.createElement('div', { style: { diff --git a/web端/运维管理/车辆业务/新增备车.jsx b/web端/运维管理/车辆业务/新增备车.jsx index 5f12c7e..f67e1aa 100644 --- a/web端/运维管理/车辆业务/新增备车.jsx +++ b/web端/运维管理/车辆业务/新增备车.jsx @@ -6,6 +6,7 @@ const Component = function () { var useCallback = React.useCallback; var useMemo = React.useMemo; var useRef = React.useRef; + var useEffect = React.useEffect; var antd = window.antd; var Breadcrumb = antd.Breadcrumb; @@ -29,7 +30,7 @@ const Component = function () { { plateNo: '京F10001', vehicleType: '厢式货车', brand: '江淮', model: 'HFC1180', vin: 'LGHXCAE28M9990001', bodyAd: true, tailboard: true } ]; - // 备车检查单:类别 -> 检查项目列表 + // 备车检查单:类别 -> 检查项目列表(与需求 2.11.1、2.11.2 一致) var inspectionCategoryItems = { '车灯': ['大灯', '转向灯', '小灯', '示廓灯', '刹车灯', '倒车灯', '牌照灯', '防雾灯', '室内灯'], '仪表盘': ['氢系统指示', '电控系统指示', '数值清晰准确', '故障报警灯'], @@ -54,7 +55,7 @@ const Component = function () { var cat = categories[i]; var items = inspectionCategoryItems[cat]; for (var j = 0; j < items.length; j++) { - list.push({ category: cat, checkItem: items[j], checked: true, remark: '' }); + list.push({ category: cat, checkItem: items[j], checked: true, remark: '', treadDepth: '' }); } } return list; @@ -67,10 +68,12 @@ const Component = function () { model: '', vin: '', bodyAd: true, - adPhotos: [], + adPhotos: [], // 广告照片,最多1张 + enlargePhotos: [], // 放大字照片,最多1张 tailboard: false, - trailerPlate: '', - flawPhotos: [], + spareTirePhotos: [], // 备胎照片,最多1张 + spareTireTreadDepth: '', // 备胎胎纹深度,OCR反写可改 + flawPhotos: [], // 瑕疵,最多4张 inspectionList: buildInspectionList() }); var form = formState[0]; @@ -80,7 +83,12 @@ const Component = function () { var photoPreviewState = useState({ open: false, url: null }); var reqDocOpenState = useState(false); var adPhotoInputRef = useRef(null); + var enlargePhotoInputRef = useRef(null); + var spareTirePhotoInputRef = useRef(null); var flawPhotoInputRef = useRef(null); + // 备胎照片 OCR 弹窗:识别中 -> 左侧照片、右侧胎纹深度输入、确认反写 + var spareTireOcrModalState = useState({ open: false, tempUrl: null, tempDepth: '', phase: 'recognizing' }); + var spareTireOcrPhaseRef = useRef(null); var plateOptions = useMemo(function () { return plateManageList.map(function (p) { return { value: p.plateNo, label: p.plateNo }; }); }, []); @@ -114,38 +122,106 @@ const Component = function () { }); }, []); + // 广告照片:最多1张 var addAdPhoto = useCallback(function () { - if ((form.adPhotos || []).length >= 4) { message.warning('最多上传4张'); return; } + if ((form.adPhotos || []).length >= 1) { message.warning('最多上传1张'); return; } if (adPhotoInputRef.current) adPhotoInputRef.current.click(); }, [form.adPhotos]); var onAdPhotoFileChange = useCallback(function (e) { var files = e.target.files; if (!files || files.length === 0) return; - setForm(function (p) { - var current = p.adPhotos || []; - var remain = 4 - current.length; - if (remain <= 0) return p; - var urls = []; - for (var i = 0; i < files.length && urls.length < remain; i++) { - if (files[i].type.indexOf('image') !== -1) urls.push(URL.createObjectURL(files[i])); - } - if (urls.length === 0) return p; - var n = {}; for (var k in p) n[k] = p[k]; n.adPhotos = current.concat(urls); return n; - }); + var file = files[0]; + if (!file || file.type.indexOf('image') === -1) { e.target.value = ''; return; } + var url = URL.createObjectURL(file); + setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.adPhotos = [url]; return n; }); e.target.value = ''; }, []); - var removeAdPhoto = useCallback(function (idx) { + var removeAdPhoto = useCallback(function () { setForm(function (p) { - var n = {}; - for (var k in p) n[k] = p[k]; - var arr = (p.adPhotos || []).slice(); - var url = arr[idx]; - if (url && url.indexOf('blob:') === 0) URL.revokeObjectURL(url); - arr.splice(idx, 1); - n.adPhotos = arr; + var n = {}; for (var k in p) n[k] = p[k]; + var arr = (p.adPhotos || [])[0]; + if (arr && typeof arr === 'string' && arr.indexOf('blob:') === 0) URL.revokeObjectURL(arr); + n.adPhotos = []; return n; }); }, []); + // 放大字照片:最多1张 + var addEnlargePhoto = useCallback(function () { + if ((form.enlargePhotos || []).length >= 1) { message.warning('最多上传1张'); return; } + if (enlargePhotoInputRef.current) enlargePhotoInputRef.current.click(); + }, [form.enlargePhotos]); + var onEnlargePhotoFileChange = useCallback(function (e) { + var files = e.target.files; + if (!files || files.length === 0) return; + var file = files[0]; + if (!file || file.type.indexOf('image') === -1) { e.target.value = ''; return; } + var url = URL.createObjectURL(file); + setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.enlargePhotos = [url]; return n; }); + e.target.value = ''; + }, []); + var removeEnlargePhoto = useCallback(function () { + setForm(function (p) { + var n = {}; for (var k in p) n[k] = p[k]; + var u = (p.enlargePhotos || [])[0]; + if (u && typeof u === 'string' && u.indexOf('blob:') === 0) URL.revokeObjectURL(u); + n.enlargePhotos = []; + return n; + }); + }, []); + // 备胎照片:上传后弹窗识别,确认反写至备胎胎纹深度 + var addSpareTirePhoto = useCallback(function () { + if ((form.spareTirePhotos || []).length >= 1) { message.warning('最多上传1张'); return; } + if (spareTirePhotoInputRef.current) spareTirePhotoInputRef.current.click(); + }, [form.spareTirePhotos]); + var onSpareTirePhotoFileChange = useCallback(function (e) { + var files = e.target.files; + if (!files || files.length === 0) return; + var file = files[0]; + if (!file || file.type.indexOf('image') === -1) { e.target.value = ''; return; } + var url = URL.createObjectURL(file); + spareTireOcrModalState[1]({ open: true, tempUrl: url, tempDepth: '', phase: 'recognizing' }); + e.target.value = ''; + }, []); + useEffect(function () { + var s = spareTireOcrModalState[0]; + if (!s.open || s.phase !== 'recognizing') return; + if (spareTireOcrPhaseRef.current) clearTimeout(spareTireOcrPhaseRef.current); + spareTireOcrPhaseRef.current = setTimeout(function () { + spareTireOcrModalState[1](function (prev) { + return prev.open ? { open: true, tempUrl: prev.tempUrl, tempDepth: '5.2', phase: 'result' } : prev; + }); + }, 1500); + return function () { + if (spareTireOcrPhaseRef.current) clearTimeout(spareTireOcrPhaseRef.current); + }; + }, [spareTireOcrModalState[0].open, spareTireOcrModalState[0].phase]); + var confirmSpareTireOcr = useCallback(function () { + var s = spareTireOcrModalState[0]; + var depth = (s.tempDepth || '').trim(); + setForm(function (p) { + var n = {}; for (var k in p) n[k] = p[k]; + n.spareTireTreadDepth = depth; + n.spareTirePhotos = s.tempUrl ? [s.tempUrl] : []; + return n; + }); + spareTireOcrModalState[1]({ open: false, tempUrl: null, tempDepth: '', phase: 'recognizing' }); + }, []); + var closeSpareTireOcrWithoutSave = useCallback(function () { + var s = spareTireOcrModalState[0]; + if (s.tempUrl && s.tempUrl.indexOf('blob:') === 0) URL.revokeObjectURL(s.tempUrl); + spareTireOcrModalState[1]({ open: false, tempUrl: null, tempDepth: '', phase: 'recognizing' }); + }, []); + var setSpareTireOcrDepth = useCallback(function (v) { + spareTireOcrModalState[1](function (prev) { return { open: prev.open, tempUrl: prev.tempUrl, tempDepth: v, phase: prev.phase }; }); + }, []); + var removeSpareTirePhoto = useCallback(function () { + setForm(function (p) { + var n = {}; for (var k in p) n[k] = p[k]; + var u = (p.spareTirePhotos || [])[0]; + if (u && typeof u === 'string' && u.indexOf('blob:') === 0) URL.revokeObjectURL(u); + n.spareTirePhotos = []; return n; + }); + }, []); var addFlawPhoto = useCallback(function () { if ((form.flawPhotos || []).length >= 4) { message.warning('最多上传4张'); return; } if (flawPhotoInputRef.current) flawPhotoInputRef.current.click(); @@ -191,6 +267,10 @@ const Component = function () { // 保存数据并跳转至备车管理-待提交 if (typeof window !== 'undefined' && window.history) window.history.back(); }, [form.plateNo]); + var handleCancel = useCallback(function () { + if (typeof window !== 'undefined' && window.history) window.history.back(); + else message.info('取消'); + }, []); // 布局样式(参照新增租赁合同) var styles = { @@ -245,8 +325,16 @@ const Component = function () { { title: '检查情况', key: 'checked', - width: 100, + width: 140, render: function (_, record, index) { + if (record.category === '轮胎') { + return React.createElement(Input, { + value: record.treadDepth, + onChange: function (e) { updateInspection(index, 'treadDepth', e.target.value); }, + placeholder: '请输入胎纹深度', + style: { width: '100%' } + }); + } return React.createElement(Switch, { checked: record.checked !== false, onChange: function (checked) { updateInspection(index, 'checked', checked); } @@ -280,7 +368,7 @@ const Component = function () { )); }; - var requirementDocContent = '新增备车\n\n1.面包屑:\n1.1.运维管理-车辆业务-备车管理-新增备车\n\n2.表单:\n2.1.车牌号:必选项,选择器,支持从输入框内输入内容进行模糊搜索,默认拉取「车牌管理」中所有「车牌号」;\n2.2.车辆类型:输入框禁用,选择车牌号后自动拉取该车牌号对应「车辆类型」;\n2.3.品牌:输入框禁用,选择车牌号后自动拉取该车牌号对应「品牌」;\n2.4.型号:输入框禁用,选择车牌号后自动拉取该车牌号对应「型号」\n2.5.车辆识别代码:输入框禁用,选择车牌号后自动拉取该车牌号「车辆识别代码」;\n2.6.车身广告:开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中,安装时间以该条备车记录提交成功为准;\n2.7.广告照片:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.8.尾板:开关,选择车辆后拉取该车辆「后装设备」「尾板」,如果该车辆有「尾板」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「尾板」中,安装时间以该条备车记录提交成功为准;\n2.9.是否有挂:输入框,提示信息为:请输入挂车牌号,不输入为无挂;\n2.10.瑕疵:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.11.车辆检查:按钮,文字为备车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、选择、备注;\n 2.11.1.类别:分为车灯、仪表盘、驾驶室、轮胎、液位检查、外观检查、车辆外观、其他、随车工具、随车证件、整车、燃料电池系统、冷机、制动系统;\n 2.11.2.检查项目:车灯类别对应(大灯、转向灯、小灯、示廓灯、刹车灯、倒车灯、牌照灯、防雾灯、室内灯)、仪表盘对应(氢系统指示、电控系统指示、数值清晰准确、故障报警灯)、驾驶室对应(点烟器、车窗升降、按键开关、雨刮器、内后视镜是否正常、内/外门把手、安全带、空调冷暖风、仪表盘、门锁功能、手刹、车钥匙功能是否正常、喇叭、音响功能、遮阳板、主副驾座椅、方向盘、内饰干净整洁)\n 2.11.3.检查情况:开关,在检查项目每项后方显示,默认为开,可手动进行关闭;\n 2.11.4.备注:输入框;\n2.12.下方为提交和保存;\n 2.12.1.提交:点击提交,toast提示:备车成功,同时保存该条数据并跳转至「备车管理」「已完成」;\n 2.12.2.保存:点击保存,toast提示:暂存成功,同时保存该条数据并跳转至「备车管理」「待提交」;'; + var requirementDocContent = '新增备车\n一个「数字化资产ONEOS运管平台」中的「新增」模块\n1.面包屑:\n1.1.运维管理-车辆业务-备车管理-新增\n\n2.表单:\n2.1.车牌号:必选项,选择器,支持从输入框内输入内容进行模糊搜索,默认拉取「车牌管理」中所有「车牌号」;\n2.2.车辆类型:输入框禁用,选择车牌号后自动拉取该车牌号对应「车辆类型」;\n2.3.品牌:输入框禁用,选择车牌号后自动拉取该车牌号对应「品牌」;\n2.4.型号:输入框禁用,选择车牌号后自动拉取该车牌号对应「型号」\n2.5.车辆识别代码:输入框禁用,选择车牌号后自动拉取该车牌号「车辆识别代码」;\n2.6.车身广告及放大字:开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中,安装时间以该条备车记录提交成功为准;\n2.7.广告照片:图片上传,最多支持上传1张图片,支持主流照片格式。上传后,上传按钮切换为显示已上传图片缩略图,支持点击预览和删除,删除后,切换为上传按钮;\n2.8.放大字照片:图片上传,最多支持上传1张图片,支持主流照片格式。上传后,上传按钮切换为显示已上传图片缩略图,支持点击预览和删除,删除后,切换为上传按钮;\n2.8.尾板:开关,选择车辆后拉取该车辆「后装设备」「尾板」,如果该车辆有「尾板」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「尾板」中,安装时间以该条备车记录提交成功为准;\n2.9.备胎照片:图片上传,最多支持上传1张图片,支持主流照片格式。上传时弹出卡片,提示正在识别中,请勿关闭页面,之后卡片左侧显示备胎照片,右侧输入框显示识别出的胎纹深度,后缀单位为mm,点击卡片中确认按钮,反写至备胎胎纹深度字段下;\n2.10.备胎胎纹深度:输入框,反写备胎照片OCR识别胎纹深度结果,支持修改;\n2.11.瑕疵:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除;\n2.12.车辆检查:按钮,文字为备车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、选择、备注;\n 2.11.1.类别:分为车灯、仪表盘、驾驶室、轮胎、液位检查、外观检查、车辆外观、其他、随车工具、随车证件、整车、燃料电池系统、冷机、制动系统;\n 2.11.2.检查项目:车灯类别对应(大灯、转向灯、小灯、示廓灯、刹车灯、倒车灯、牌照灯、防雾灯、室内灯)、仪表盘对应(氢系统指示、电控系统指示、数值清晰准确、故障报警灯)、驾驶室对应(点烟器、车窗升降、按键开关、雨刮器、内后视镜是否正常、内/外门把手、安全带、空调冷暖风、仪表盘、门锁功能、手刹、车钥匙功能是否正常、喇叭、音响功能、遮阳板、主副驾座椅、方向盘、内饰干净整洁)\n 2.11.3.检查情况:其他项为开关,在检查项目每项后方显示,默认为开,可手动进行关闭,轮胎为输入框,提示请输入胎纹深度;\n 2.11.4.备注:输入框;\n2.12.下方为提交和保存;\n 2.12.1.提交:点击提交,toast提示:备车成功,同时保存该条数据并跳转至「备车管理」「已完成」;\n 2.12.2.保存:点击保存,toast提示:暂存成功,同时保存该条数据并跳转至「备车管理」「待提交」;'; var breadcrumbNodes = [ React.createElement('span', { key: '1' }, '运维管理'), @@ -289,7 +377,7 @@ const Component = function () { React.createElement('span', { key: '4', style: styles.breadcrumbSep }, ' / '), React.createElement('span', { key: '5' }, '备车管理'), React.createElement('span', { key: '6', style: styles.breadcrumbSep }, ' / '), - React.createElement('span', { key: '7', style: { color: '#1890ff' } }, '新增备车') + React.createElement('span', { key: '7', style: { color: '#1890ff' } }, '新增') ]; return React.createElement('div', { style: styles.page }, @@ -302,7 +390,7 @@ const Component = function () { React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '车牌号', required: true }, React.createElement(Select, { - placeholder: '请选择车牌号', + placeholder: '请选择或输入车牌号', style: { width: '100%' }, value: form.plateNo || undefined, onChange: onPlateSelect, @@ -326,9 +414,10 @@ const Component = function () { ), React.createElement(FormItem, { label: '车辆识别代码' }, React.createElement(Input, { value: form.vin, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) - ), - React.createElement('div', { style: styles.formCol, key: 'ph0' }), - React.createElement(FormItem, { label: '车身广告' }, + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '车身广告及放大字' }, React.createElement(Switch, { checked: form.bodyAd, checkedChildren: '开', @@ -338,22 +427,10 @@ const Component = function () { } }) ), - React.createElement('div', { style: styles.formCol, key: 'ph1' }), - React.createElement('div', { style: styles.formCol, key: 'ph2' }), - React.createElement(FormItem, { label: '尾板' }, - React.createElement(Switch, { - checked: form.tailboard, - checkedChildren: '开', - unCheckedChildren: '关', - onChange: function (checked) { - setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.tailboard = checked; return n; }); - } - }) - ), - React.createElement(FormItem, { label: '广告照片', fullWidth: true }, + React.createElement(FormItem, { label: '广告照片' }, React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, - React.createElement('input', { type: 'file', ref: adPhotoInputRef, accept: 'image/*', multiple: true, style: { display: 'none' }, onChange: onAdPhotoFileChange }), - (form.adPhotos || []).length < 4 ? cameraIconBox(addAdPhoto) : null, + React.createElement('input', { type: 'file', ref: adPhotoInputRef, accept: 'image/*', style: { display: 'none' }, onChange: onAdPhotoFileChange }), + (form.adPhotos || []).length < 1 ? cameraIconBox(addAdPhoto) : null, (form.adPhotos || []).map(function (url, i) { return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } }, React.createElement('img', { @@ -364,22 +441,75 @@ const Component = function () { }), React.createElement('span', { style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }, - onClick: function () { removeAdPhoto(i); } + onClick: function () { removeAdPhoto(); } }, '×') ); }) ) ), - React.createElement(FormItem, { label: '是否有挂' }, - React.createElement(Input, { - placeholder: '请输入挂车牌号,不输入为无挂', - value: form.trailerPlate, - onChange: function (e) { - setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.trailerPlate = e.target.value; return n; }); - }, - style: { width: '100%', maxWidth: 320 } + React.createElement(FormItem, { label: '放大字照片' }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + React.createElement('input', { type: 'file', ref: enlargePhotoInputRef, accept: 'image/*', style: { display: 'none' }, onChange: onEnlargePhotoFileChange }), + (form.enlargePhotos || []).length < 1 ? cameraIconBox(addEnlargePhoto) : null, + (form.enlargePhotos || []).map(function (url, i) { + return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } }, + React.createElement('img', { + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }), + React.createElement('span', { + style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }, + onClick: function () { removeEnlargePhoto(); } + }, '×') + ); + }) + ) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '尾板' }, + React.createElement(Switch, { + checked: form.tailboard, + checkedChildren: '开', + unCheckedChildren: '关', + onChange: function (checked) { + setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.tailboard = checked; return n; }); + } }) ), + React.createElement(FormItem, { label: '备胎照片' }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + React.createElement('input', { type: 'file', ref: spareTirePhotoInputRef, accept: 'image/*', style: { display: 'none' }, onChange: onSpareTirePhotoFileChange }), + (form.spareTirePhotos || []).length < 1 ? cameraIconBox(addSpareTirePhoto) : null, + (form.spareTirePhotos || []).map(function (url, i) { + return React.createElement('span', { key: i, style: { position: 'relative', display: 'inline-block' } }, + React.createElement('img', { + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }), + React.createElement('span', { + style: { position: 'absolute', top: -6, right: -6, width: 20, height: 20, borderRadius: '50%', background: '#ff4d4f', color: '#fff', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }, + onClick: function () { removeSpareTirePhoto(); } + }, '×') + ); + }) + ) + ), + React.createElement(FormItem, { label: '备胎胎纹深度' }, + React.createElement(Input, { + value: form.spareTireTreadDepth, + onChange: function (e) { setForm(function (p) { var n = {}; for (var k in p) n[k] = p[k]; n.spareTireTreadDepth = e.target.value; return n; }); }, + placeholder: 'OCR识别结果可修改', + style: { width: '100%' }, + addonAfter: 'mm' + }) + ) + ), + React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '瑕疵', fullWidth: true }, React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, React.createElement('input', { type: 'file', ref: flawPhotoInputRef, accept: 'image/*', multiple: true, style: { display: 'none' }, onChange: onFlawPhotoFileChange }), @@ -399,7 +529,9 @@ const Component = function () { ); }) ) - ), + ) + ), + React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '车辆检查', fullWidth: true }, React.createElement(Button, { type: 'default', onClick: function () { drawerOpenState[1](true); } }, '备车检查单') ) @@ -409,7 +541,7 @@ const Component = function () { React.createElement('div', { style: styles.footer }, React.createElement(Button, { type: 'primary', onClick: handleSubmit }, '提交'), React.createElement(Button, { onClick: handleSave }, '保存'), - React.createElement(Button, { onClick: function () { if (typeof window !== 'undefined' && window.history) window.history.back(); } }, '取消') + React.createElement(Button, { onClick: handleCancel }, '取消') ), React.createElement(Drawer, { title: '备车检查单', @@ -441,6 +573,39 @@ const Component = function () { width: 720, styles: { body: { maxHeight: '70vh', overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 14, lineHeight: 1.8 } } }, requirementDocContent), + spareTireOcrModalState[0].open + ? React.createElement(Modal, { + title: '备胎照片识别', + open: true, + onCancel: spareTireOcrModalState[0].phase === 'result' ? closeSpareTireOcrWithoutSave : undefined, + width: 560, + maskClosable: spareTireOcrModalState[0].phase === 'result', + footer: spareTireOcrModalState[0].phase === 'result' + ? React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } }, + React.createElement(Button, { onClick: closeSpareTireOcrWithoutSave }, '取消'), + React.createElement(Button, { type: 'primary', onClick: confirmSpareTireOcr }, '确认') + ) + : null, + closable: spareTireOcrModalState[0].phase === 'result' + }, spareTireOcrModalState[0].phase === 'recognizing' + ? React.createElement('div', { style: { padding: '24px 0', textAlign: 'center', fontSize: 15, color: '#666' } }, '正在识别中,请勿关闭页面') + : React.createElement('div', { style: { display: 'flex', gap: 24, alignItems: 'flex-start' } }, + React.createElement('div', { style: { flex: '0 0 200px' } }, + React.createElement('img', { src: spareTireOcrModalState[0].tempUrl, alt: '', style: { width: '100%', display: 'block', borderRadius: 4 } }) + ), + React.createElement('div', { style: { flex: 1 } }, + React.createElement('div', { style: { marginBottom: 8, color: '#333' } }, '识别胎纹深度(mm)'), + React.createElement(Input, { + value: spareTireOcrModalState[0].tempDepth, + onChange: function (e) { setSpareTireOcrDepth(e.target.value); }, + placeholder: '请输入', + addonAfter: 'mm', + style: { width: '100%', maxWidth: 160 } + }) + ) + ) + ) + : null, photoPreviewState[0].open && photoPreviewState[0].url ? React.createElement(Modal, { title: '预览', diff --git a/web端/运维管理/车辆业务/查看备车.jsx b/web端/运维管理/车辆业务/查看备车.jsx index 815b0e1..300c31c 100644 --- a/web端/运维管理/车辆业务/查看备车.jsx +++ b/web端/运维管理/车辆业务/查看备车.jsx @@ -46,13 +46,13 @@ const Component = function () { var cat = categories[i]; var items = inspectionCategoryItems[cat]; for (var j = 0; j < items.length; j++) { - list.push({ category: cat, checkItem: items[j], checked: true, remark: '' }); + list.push({ category: cat, checkItem: items[j], checked: true, remark: '', treadDepth: '' }); } } return list; } - // 只读:一条已填写的备车记录(不可编辑) + // 只读:一条已填写的备车记录(与新增备车字段一致,全部不可编辑) var form = useMemo(function () { return { plateNo: '京A12345', @@ -61,9 +61,11 @@ const Component = function () { model: 'DFH1180', vin: 'LGHXCAE28M1234567', bodyAd: true, - adPhotos: ['https://picsum.photos/200/150?r=1', 'https://picsum.photos/200/150?r=2'], + adPhotos: ['https://picsum.photos/200/150?r=1'], + enlargePhotos: ['https://picsum.photos/200/150?r=2'], tailboard: true, - trailerPlate: '京B67890', + spareTirePhotos: ['https://picsum.photos/200/150?r=3'], + spareTireTreadDepth: '5.2', flawPhotos: ['https://picsum.photos/200/150?f=1'], inspectionList: buildInspectionList() }; @@ -125,8 +127,11 @@ const Component = function () { { title: '检查情况', key: 'checked', - width: 100, - render: function (_, record) { return record.checked !== false ? '开' : '关'; } + width: 140, + render: function (_, record) { + if (record.category === '轮胎') return record.treadDepth ? record.treadDepth + ' mm' : '-'; + return record.checked !== false ? '开' : '关'; + } }, { title: '备注', dataIndex: 'remark', key: 'remark', render: function (val) { return val || '-'; } } ]; @@ -165,17 +170,13 @@ const Component = function () { ), React.createElement(FormItem, { label: '车辆识别代码' }, React.createElement(Input, { value: form.vin, disabled: true, style: { width: '100%', background: '#f5f5f5' } }) - ), - React.createElement('div', { style: styles.formCol, key: 'ph0' }), - React.createElement(FormItem, { label: '车身广告' }, + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '车身广告及放大字' }, React.createElement(Input, { value: form.bodyAd ? '开' : '关', disabled: true, style: { width: '100%', background: '#f5f5f5' } }) ), - React.createElement('div', { style: styles.formCol, key: 'ph1' }), - React.createElement('div', { style: styles.formCol, key: 'ph2' }), - React.createElement(FormItem, { label: '尾板' }, - React.createElement(Input, { value: form.tailboard ? '开' : '关', disabled: true, style: { width: '100%', background: '#f5f5f5' } }) - ), - React.createElement(FormItem, { label: '广告照片', fullWidth: true }, + React.createElement(FormItem, { label: '广告照片' }, React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, (form.adPhotos || []).map(function (url, i) { return React.createElement('img', { @@ -189,9 +190,44 @@ const Component = function () { (form.adPhotos || []).length === 0 ? React.createElement('span', { style: { color: '#999' } }, '无') : null ) ), - React.createElement(FormItem, { label: '是否有挂' }, - React.createElement(Input, { value: form.trailerPlate || '无', disabled: true, style: { width: '100%', maxWidth: 320, background: '#f5f5f5' } }) + React.createElement(FormItem, { label: '放大字照片' }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + (form.enlargePhotos || []).map(function (url, i) { + return React.createElement('img', { + key: i, + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }); + }), + (form.enlargePhotos || []).length === 0 ? React.createElement('span', { style: { color: '#999' } }, '无') : null + ) + ) + ), + React.createElement('div', { style: styles.formRow }, + React.createElement(FormItem, { label: '尾板' }, + React.createElement(Input, { value: form.tailboard ? '开' : '关', disabled: true, style: { width: '100%', background: '#f5f5f5' } }) ), + React.createElement(FormItem, { label: '备胎照片' }, + React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, + (form.spareTirePhotos || []).map(function (url, i) { + return React.createElement('img', { + key: i, + src: url, + alt: '', + style: { width: 64, height: 64, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { photoPreviewState[1]({ open: true, url: url }); } + }); + }), + (form.spareTirePhotos || []).length === 0 ? React.createElement('span', { style: { color: '#999' } }, '无') : null + ) + ), + React.createElement(FormItem, { label: '备胎胎纹深度' }, + React.createElement(Input, { value: form.spareTireTreadDepth || '', disabled: true, style: { width: '100%', background: '#f5f5f5' }, addonAfter: 'mm' }) + ) + ), + React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '瑕疵', fullWidth: true }, React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8 } }, (form.flawPhotos || []).map(function (url, i) { @@ -205,7 +241,9 @@ const Component = function () { }), (form.flawPhotos || []).length === 0 ? React.createElement('span', { style: { color: '#999' } }, '无') : null ) - ), + ) + ), + React.createElement('div', { style: styles.formRow }, React.createElement(FormItem, { label: '车辆检查', fullWidth: true }, React.createElement(Button, { type: 'default', onClick: function () { drawerOpenState[1](true); } }, '备车检查单') ) diff --git a/web端/运维管理/车辆管理-查看.jsx b/web端/运维管理/车辆管理-查看.jsx new file mode 100644 index 0000000..a071e1b --- /dev/null +++ b/web端/运维管理/车辆管理-查看.jsx @@ -0,0 +1,289 @@ +// 【重要】必须使用 const Component 作为组件变量名 +// 运维管理 - 车辆数据 - 车辆详情(只读展示) + +const Component = function () { + var useState = React.useState; + + var antd = window.antd; + var Breadcrumb = antd.Breadcrumb; + var Card = antd.Card; + var Tabs = antd.Tabs; + var Table = antd.Table; + var Tag = antd.Tag; + var Button = antd.Button; + var Tooltip = antd.Tooltip; + + // 顶部概览 mock 数据(与图片一致) + var overview = { + plateNo: '浙F06900F', + plateTag: '租赁', + vin: 'LA9HE60A0NBAF4031', + vehicleNo: '22FHD0007', + owner: '浙江羚牛氢能科技有限公司', + contractNo: 'LNZLHTSH2023071301', + outStatus: '租赁交车', + licenseStatus: '正常', + location: '浙江省嘉兴市平湖市XXXXXXXXXXX街道XXXXXXXXXXX号XXXXXXXx', + bodyColor: '白色', + purchaseDate: '2026-09-09', + warehouseStatus: '已交付车-租赁服务', + customerName: '上海迅杰物流有限公司', + prepStatus: '正常', + scrapStatus: '无', + gpsLastTime: '2026-09-09 10:50', + resourceCategory: 'XXXXXXXXXX', + manufactureYear: '2025', + parkingPlace: '-', + bizDept: '业务三部', + lastPrepTime: '2026-09-09', + assetRating: 'XXXXXXXXXX', + ratingTime: '2027-09-09', + forceScrapDate: '2026-09-09', + nextInspectionTime: '2026-09-09', + bizManager: '金可鹏', + maintainStatus: '正常', + preemptStatus: '无', + transferStatus: '无' + }; + + // 型号参数 tab 数据 + var modelParams = { + brand: '苏龙', + model: '海格牌KLQ5180XYKFCEV', + vehicleType: '18吨双飞翼货车', + fuelType: '氢', + cabinColor: '绿牌', + wholeSize: '5995mm x 2145mm x 3130mm' + }; + var tireInfo = { tireCount: '8', tireSpec: '15/80R22.5' }; + var electricSystem = { + batteryType: '磷酸铁锂', + batteryVendor: 'xxxxxxxxxxxxx企业名称', + capacity: '100000 kWh', + range: '200 KM' + }; + var hydrogenSystem = { + vendor: '某某供氢系统科技有限公司', + cylinderCapacity: 'xxx L', + gaugeMode: 'MPa', + range: '1000 KM' + }; + var otherSystem = { + coldMachineVendor: 'xxxxxxxx企业', + stackVendor: 'XXXXXXXX企业' + }; + var maintenanceList = [ + { key: '1', item: '变速器油', kmCycle: '60000', monthCycle: '24', laborCost: '0', materialCost: '571', total: '571', lastKm: '' }, + { key: '2', item: '变速器油', kmCycle: '60000', monthCycle: '24', laborCost: '0', materialCost: '571', total: '571', lastKm: '' }, + { key: '3', item: '变速器油', kmCycle: '60000', monthCycle: '24', laborCost: '0', materialCost: '571', total: '571', lastKm: '5000' }, + { key: '4', item: '变速器油', kmCycle: '60000', monthCycle: '24', laborCost: '0', materialCost: '571', total: '571', lastKm: '5000' }, + { key: '5', item: '变速器油', kmCycle: '60000', monthCycle: '24', laborCost: '0', materialCost: '571', total: '571', lastKm: '5000' } + ]; + + var activeTab = useState('model'); + + var styles = { + page: { padding: '16px 24px 40px', 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' }, + overviewHeader: { display: 'flex', alignItems: 'center', gap: 12, marginBottom: 20, flexWrap: 'wrap' }, + plateNo: { fontSize: 18, fontWeight: 600, color: '#333' }, + overviewGrid: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '12px 24px' }, + overviewItem: { display: 'flex', gap: 8, minWidth: 0 }, + overviewLabel: { color: '#666', flex: '0 0 110px' }, + overviewValue: { color: '#333', flex: 1 }, + cardTitleBar: { display: 'flex', alignItems: 'center', marginBottom: 16, fontSize: 15, fontWeight: 500 }, + cardTitleBarLine: { width: 4, height: 16, backgroundColor: '#1890ff', marginRight: 8, borderRadius: 2 }, + infoGrid: { display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '12px 24px', marginBottom: 20 }, + infoItem: { display: 'flex', gap: 8 }, + infoLabel: { color: '#666', flex: '0 0 100px' }, + infoValue: { color: '#333' } + }; + + function InfoRow(props) { + var val = props.value || '-'; + var valueNode = props.ellipsisWithTooltip + ? React.createElement(Tooltip, { title: val }, + React.createElement('span', { + style: Object.assign({}, styles.overviewValue, { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block' }) + }, val) + ) + : React.createElement('span', { style: styles.overviewValue }, val); + return React.createElement('div', { style: styles.overviewItem }, + React.createElement('span', { style: styles.overviewLabel }, props.label + ':'), + valueNode + ); + } + + function CardTitleWithBar(props) { + return React.createElement('div', { style: styles.cardTitleBar }, + React.createElement('span', { style: styles.cardTitleBarLine }), + props.title + ); + } + + var tabItems = [ + { + key: 'model', + label: '型号参数', + children: React.createElement('div', { style: { padding: '0 4px' } }, + React.createElement(Card, { size: 'small', style: { marginBottom: 16 } }, + React.createElement(CardTitleWithBar, { title: '型号参数' }), + React.createElement('div', { style: styles.infoGrid }, + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '品牌:'), React.createElement('span', { style: styles.infoValue }, modelParams.brand)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '型号:'), React.createElement('span', { style: styles.infoValue }, modelParams.model)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '车辆类型:'), React.createElement('span', { style: styles.infoValue }, modelParams.vehicleType)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '燃料种类:'), React.createElement('span', { style: styles.infoValue }, modelParams.fuelType)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '车厢颜色:'), React.createElement('span', { style: styles.infoValue }, modelParams.cabinColor)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '整车尺寸:'), React.createElement('span', { style: styles.infoValue }, modelParams.wholeSize)) + ) + ), + React.createElement(Card, { size: 'small', style: { marginBottom: 16 } }, + React.createElement(CardTitleWithBar, { title: '轮胎情况' }), + React.createElement('div', { style: styles.infoGrid }, + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '轮胎数量:'), React.createElement('span', { style: styles.infoValue }, tireInfo.tireCount)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '轮胎规格:'), React.createElement('span', { style: styles.infoValue }, tireInfo.tireSpec)) + ) + ), + React.createElement(Card, { size: 'small', style: { marginBottom: 16 } }, + React.createElement(CardTitleWithBar, { title: '电气系统' }), + React.createElement('div', { style: styles.infoGrid }, + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '电池类型:'), React.createElement('span', { style: styles.infoValue }, electricSystem.batteryType)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '电池厂家:'), React.createElement('span', { style: styles.infoValue }, electricSystem.batteryVendor)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '储电量:'), React.createElement('span', { style: styles.infoValue }, electricSystem.capacity)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '续航里程:'), React.createElement('span', { style: styles.infoValue }, electricSystem.range)) + ) + ), + React.createElement(Card, { size: 'small', style: { marginBottom: 16 } }, + React.createElement(CardTitleWithBar, { title: '供氢系统' }), + React.createElement('div', { style: styles.infoGrid }, + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '供氢系统厂家:'), React.createElement('span', { style: styles.infoValue }, hydrogenSystem.vendor)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '氢瓶容量:'), React.createElement('span', { style: styles.infoValue }, hydrogenSystem.cylinderCapacity)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '仪表盘模式:'), React.createElement('span', { style: styles.infoValue }, hydrogenSystem.gaugeMode)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '续航里程:'), React.createElement('span', { style: styles.infoValue }, hydrogenSystem.range)) + ) + ), + React.createElement(Card, { size: 'small', style: { marginBottom: 16 } }, + React.createElement(CardTitleWithBar, { title: '其他系统' }), + React.createElement('div', { style: styles.infoGrid }, + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '冷机生产企业:'), React.createElement('span', { style: styles.infoValue }, otherSystem.coldMachineVendor)), + React.createElement('div', { style: styles.infoItem }, React.createElement('span', { style: styles.infoLabel }, '电堆生产企业:'), React.createElement('span', { style: styles.infoValue }, otherSystem.stackVendor)) + ) + ), + React.createElement(Card, { size: 'small' }, + React.createElement(CardTitleWithBar, { title: '保养参数' }), + React.createElement(Table, { + dataSource: maintenanceList, + columns: [ + { title: '序号', dataIndex: 'key', key: 'key', width: 60, render: function (_, __, i) { return i + 1; } }, + { title: '养护项目', dataIndex: 'item', key: 'item', width: 120 }, + { title: '保养公里周期(km)', dataIndex: 'kmCycle', key: 'kmCycle', width: 140 }, + { title: '保养时间周期(月)', dataIndex: 'monthCycle', key: 'monthCycle', width: 140 }, + { title: '工时费(元)', dataIndex: 'laborCost', key: 'laborCost', width: 100 }, + { title: '材料费(元)', dataIndex: 'materialCost', key: 'materialCost', width: 100 }, + { title: '合计', dataIndex: 'total', key: 'total', width: 80 }, + { title: '上次保养公里数(KM)', dataIndex: 'lastKm', key: 'lastKm', render: function (v) { return v || '—'; } } + ], + pagination: false, + size: 'small', + bordered: true + }) + ) + ) + }, + { key: 'equip', label: '后装设备', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'license', label: '证照信息', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'lease', label: '租赁记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'insurance', label: '保险记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'repair', label: '维修记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'maintain', label: '保养记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'accident', label: '事故记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'fault', label: '故障记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'violation', label: '违章记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'change', label: '异动记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'transfer', label: '调拨记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'prep', label: '整备记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') }, + { key: 'inspect', label: '抽检记录', children: React.createElement('div', { style: { padding: 24, color: '#999' } }, '暂无数据') } + ]; + + var breadcrumbNodes = [ + React.createElement('span', { key: '1' }, '车辆管理'), + React.createElement('span', { key: '2', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '3' }, '车辆数据'), + React.createElement('span', { key: '4', style: styles.breadcrumbSep }, ' / '), + React.createElement('span', { key: '5', style: { color: '#1890ff' } }, '详情') + ]; + + return React.createElement('div', { style: styles.page }, + React.createElement('div', { style: styles.breadcrumb }, breadcrumbNodes), + React.createElement('div', { style: styles.card }, + React.createElement('div', { style: styles.cardBody }, + React.createElement('div', { style: styles.overviewHeader }, + React.createElement('span', { style: styles.plateNo }, overview.plateNo), + React.createElement(Tag, { color: 'blue' }, overview.plateTag), + React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 } }, '1') + ), + React.createElement('div', { style: styles.overviewGrid }, + React.createElement(InfoRow, { label: '车架号', value: overview.vin }), + React.createElement(InfoRow, { label: '车辆编号', value: overview.vehicleNo }), + React.createElement(InfoRow, { label: '登记所有权', value: overview.owner }), + React.createElement(InfoRow, { label: '合同编号', value: overview.contractNo }), + React.createElement(InfoRow, { label: '出库状态', value: overview.outStatus }), + React.createElement(InfoRow, { label: '证照状态', value: overview.licenseStatus }), + React.createElement(InfoRow, { label: '车辆当前位置', value: overview.location, ellipsisWithTooltip: true }), + React.createElement(InfoRow, { label: '车身颜色', value: overview.bodyColor }), + React.createElement(InfoRow, { label: '采购入库日期', value: overview.purchaseDate }), + React.createElement(InfoRow, { label: '库位状态', value: overview.warehouseStatus }), + React.createElement(InfoRow, { label: '客户名称', value: overview.customerName }), + React.createElement(InfoRow, { label: '整备状态', value: overview.prepStatus }), + React.createElement(InfoRow, { label: '报废状态', value: overview.scrapStatus }), + React.createElement(InfoRow, { label: 'GPS最后上传', value: overview.gpsLastTime }), + React.createElement(InfoRow, { label: '资源分类', value: overview.resourceCategory }), + React.createElement(InfoRow, { label: '出厂年份', value: overview.manufactureYear }), + React.createElement(InfoRow, { label: '停车位置', value: overview.parkingPlace }), + React.createElement(InfoRow, { label: '业务部门', value: overview.bizDept }), + React.createElement(InfoRow, { label: '上次整备时间', value: overview.lastPrepTime }), + React.createElement(InfoRow, { label: '资产评级', value: overview.assetRating }), + React.createElement(InfoRow, { label: '等评时间', value: overview.ratingTime }), + React.createElement(InfoRow, { label: '强制报废期', value: overview.forceScrapDate }), + React.createElement(InfoRow, { label: '下次年检时间', value: overview.nextInspectionTime }), + React.createElement(InfoRow, { label: '业务负责人', value: overview.bizManager }), + React.createElement(InfoRow, { label: '维修状态', value: overview.maintainStatus }), + React.createElement(InfoRow, { label: '预占状态', value: overview.preemptStatus }), + React.createElement(InfoRow, { label: '过户状态', value: overview.transferStatus }) + ) + ) + ), + React.createElement('div', { style: Object.assign({}, styles.card, { marginBottom: 0 }) }, + React.createElement('div', { style: styles.cardBody }, + React.createElement(Tabs, { + activeKey: activeTab[0], + onChange: function (key) { activeTab[1](key); }, + items: tabItems, + type: 'line' + }) + ) + ) + ); +}; + +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)); + } + } +} diff --git a/web端/需求说明/业务管理/供应商管理 b/web端/需求说明/业务管理/供应商管理 new file mode 100644 index 0000000..3cfa7c7 --- /dev/null +++ b/web端/需求说明/业务管理/供应商管理 @@ -0,0 +1,35 @@ +供应商管理(2026年3月12日版本) +一个「数字化资产ONEOS运管平台」中的「供应商管理」模块 +#面包屑:业务管理-供应商管理; + +页面分为2个卡片; +1.筛选:默认显示首行,可通过展开、收起功能进行展开/收起的切换,右侧为重置、查询按钮; +#支持供应商编码、合作状态、类型、区域、城市、创建人等筛选方式; +1.1.供应商编码:选择器,支持输入框内输入内容模糊搜索,下拉显示对应选项; +1.2.合作状态:选择器,支持多选,选项为已合作、终止合作、洽谈中、合约过期; +1.3.类型:选择器,支持多选,选项为:备件供应商、保险公司、加氢站、充电站、维修站、救援车队、整车厂、其他; +1.4.区域:选择器,支持多选,选项为:华北、华东、华南、华中、东北、西南、西北; +1.4.城市:地区级联选择器,支持2级选择,格式为省-市; +1.5.创建人:选择器,选项为所有创建用户姓名; + +2.供应商列表: +#左上方为供应商名称输入框(输入供应商名称直接联动),右上角为新增、导出、导入; +2.1.供应商编码:显示供应商编码; +2.2.合作状态:显示合作状态,选项为已合作、终止合作、洽谈中、合约过期; +2.3.供应商名称:显示供应商名称; +2.4.类型:选择器,选项为:备件供应商、保险公司、加氢站、充电站、维修站、救援车队、整车厂、其他; +2.4.区域:显示供应商区域,客户区域由选择城市后自动匹配; +2.5.城市:显示供应商城市,格式为:省-市; +2.6.信用代码/身份证:显示供应商信用代码或身份证,取决于新增时输入的是什么; +2.7.联系人:显示供应商联系人; +2.8.联系人手机:显示供应商联系人手机; +2.9.联系人座机:显示供应商联系人座机,无则显示为-; +2.10.供应商地址:显示供应商地址信息; +2.11.电子邮箱:显示供应商电子邮箱; +2.12.备注:显示供应商备注信息; +2.13.创建人:显示供应商创建人用户姓名; +2.14.创建时间:显示供应商创建时间; +2.15.操作:查看、编辑、删除; + 2.16.1.查看:跳转供应商管理-查看页; + 2.16.2.编辑:跳转供应商管理-编辑页; + 2.16.3.删除:二次确认,点击确认后删除,已删除供应商无法再各类合同或账单选择时选择,但历史数据中该供应商信息依然显示; \ No newline at end of file diff --git a/web端/需求说明/业务管理/客户管理 b/web端/需求说明/业务管理/客户管理 new file mode 100644 index 0000000..8ad86c2 --- /dev/null +++ b/web端/需求说明/业务管理/客户管理 @@ -0,0 +1,35 @@ +客户管理(2026年3月12日版本) +一个「数字化资产ONEOS运管平台」中的「客户管理」模块 +#面包屑:业务管理-客户管理; + +页面分为2个卡片; +1.筛选:默认显示首行,可通过展开、收起功能进行展开/收起的切换,右侧为重置、查询按钮; +#支持客户编码、合作状态、区域、城市、业务经理、创建人等筛选方式; +1.1.客户编码:选择器,支持输入框内输入内容模糊搜索,下拉显示对应选项; +1.2.合作状态:选择器,支持多选,选项为已合作、终止合作、洽谈中、合约过期; +1.3.区域:选择器,支持多选,选项为:华北、华东、华南、华中、东北、西南、西北; +1.4.城市:地区级联选择器,支持2级选择,格式为省-市; +1.5.业务经理:选择器,选项为客户表中所有业务经理; +1.6.创建人:选择器,选项为所有创建用户姓名; + +2.客户列表: +#左上方为客户名称输入框(输入客户名称直接联动),右上角为新增、导出、导入; +2.1.客户编码:显示客户编码; +2.2.合作状态:显示合作状态,选项为已合作、终止合作、洽谈中、合约过期; +2.3.客户名称:显示客户名称; +2.4.区域:显示客户区域,客户区域由选择城市后自动匹配; +2.5.城市:显示客户城市,格式为:省-市; +2.6.信用代码/身份证:显示客户信用代码或身份证,取决于新增时输入的是什么; +2.7.联系人:显示客户联系人; +2.8.联系人手机:显示客户联系人手机; +2.9.联系人座机:显示客户联系人座机,无则显示为-; +2.10.业务经理:显示客户对应业务经理姓名,可能会存在多个业务经理维护一个客户; +2.11.客户地址:显示客户地址信息; +2.12.电子邮箱:显示客户电子邮箱; +2.13.备注:显示客户备注信息; +2.14.创建人:显示客户创建人用户姓名; +2.15.创建时间:显示客户创建时间; +2.16.操作:查看、编辑、删除; + 2.16.1.查看:跳转客户管理-查看页; + 2.16.2.编辑:跳转客户管理-编辑页; + 2.16.3.删除:二次确认,点击确认后删除,已删除客户无法再被新合同选择客户时选择,但历史数据中该客户信息依然显示; \ No newline at end of file diff --git a/web端/业务管理/车辆成本维护 b/web端/需求说明/业务管理/车辆成本维护 similarity index 100% rename from web端/业务管理/车辆成本维护 rename to web端/需求说明/业务管理/车辆成本维护 diff --git a/web端/需求说明/新增备车 b/web端/需求说明/新增备车 index e10f1ef..5c7ce53 100644 --- a/web端/需求说明/新增备车 +++ b/web端/需求说明/新增备车 @@ -1,7 +1,7 @@ 新增备车 -一个「数字化资产ONEOS运管平台」中的「新增备车」模块 +一个「数字化资产ONEOS运管平台」中的「新增」模块 1.面包屑: -1.1.运维管理-车辆业务-备车管理-新增备车 +1.1.运维管理-车辆业务-备车管理-新增 2.表单: 2.1.车牌号:必选项,选择器,支持从输入框内输入内容进行模糊搜索,默认拉取「车牌管理」中所有「车牌号」; @@ -9,15 +9,17 @@ 2.3.品牌:输入框禁用,选择车牌号后自动拉取该车牌号对应「品牌」; 2.4.型号:输入框禁用,选择车牌号后自动拉取该车牌号对应「型号」 2.5.车辆识别代码:输入框禁用,选择车牌号后自动拉取该车牌号「车辆识别代码」; -2.6.车身广告:开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中,安装时间以该条备车记录提交成功为准; -2.7.广告照片:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除; +2.6.车身广告及放大字:开关,选择车辆后拉取该车辆「后装设备」「车身广告」,如果该车辆有「车身广告」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「车身广告」中,安装时间以该条备车记录提交成功为准; +2.7.广告照片:图片上传,最多支持上传1张图片,支持主流照片格式。上传后,上传按钮切换为显示已上传图片缩略图,支持点击预览和删除,删除后,切换为上传按钮; +2.8.放大字照片:图片上传,最多支持上传1张图片,支持主流照片格式。上传后,上传按钮切换为显示已上传图片缩略图,支持点击预览和删除,删除后,切换为上传按钮; 2.8.尾板:开关,选择车辆后拉取该车辆「后装设备」「尾板」,如果该车辆有「尾板」则勾选为开,如果无则勾选为无。同时如果手动进行操作,会同步到「后装设备」「尾板」中,安装时间以该条备车记录提交成功为准; -2.9.是否有挂:输入框,提示信息为:请输入挂车牌号,不输入为无挂; -2.10.瑕疵:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除; -2.11.车辆检查:按钮,文字为备车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、选择、备注; +2.9.备胎照片:图片上传,最多支持上传1张图片,支持主流照片格式。上传时弹出卡片,提示正在识别中,请勿关闭页面,之后卡片左侧显示备胎照片,右侧输入框显示识别出的胎纹深度,后缀单位为mm,点击卡片中确认按钮,反写至备胎胎纹深度字段下; +2.10.备胎胎纹深度:输入框,反写备胎照片OCR识别胎纹深度结果,支持修改; +2.11.瑕疵:图片上传,最多支持上传4张图片,支持主流照片格式。上传后,上传按钮后方横排显示已上传图片缩略图,支持点击预览和删除; +2.12.车辆检查:按钮,文字为备车检查单,点击右侧展开抽屉,抽屉内显示列表,字段为类别、检查项目、选择、备注; 2.11.1.类别:分为车灯、仪表盘、驾驶室、轮胎、液位检查、外观检查、车辆外观、其他、随车工具、随车证件、整车、燃料电池系统、冷机、制动系统; 2.11.2.检查项目:车灯类别对应(大灯、转向灯、小灯、示廓灯、刹车灯、倒车灯、牌照灯、防雾灯、室内灯)、仪表盘对应(氢系统指示、电控系统指示、数值清晰准确、故障报警灯)、驾驶室对应(点烟器、车窗升降、按键开关、雨刮器、内后视镜是否正常、内/外门把手、安全带、空调冷暖风、仪表盘、门锁功能、手刹、车钥匙功能是否正常、喇叭、音响功能、遮阳板、主副驾座椅、方向盘、内饰干净整洁) - 2.11.3.检查情况:开关,在检查项目每项后方显示,默认为开,可手动进行关闭; + 2.11.3.检查情况:其他项为开关,在检查项目每项后方显示,默认为开,可手动进行关闭,轮胎为输入框,提示请输入胎纹深度; 2.11.4.备注:输入框; 2.12.下方为提交和保存; 2.12.1.提交:点击提交,toast提示:备车成功,同时保存该条数据并跳转至「备车管理」「已完成」; diff --git a/web端/需求说明/运维管理-车务管理/备车管理 b/web端/需求说明/运维管理-车务管理/备车管理 new file mode 100644 index 0000000..2247a31 --- /dev/null +++ b/web端/需求说明/运维管理-车务管理/备车管理 @@ -0,0 +1,55 @@ +备车管理: +一个「数字化资产ONEOS运管平台」中的「备车管理」模块 +1.面包屑: +1.1.运维管理-车辆业务-备车管理 + +2.筛选: +2.1.备车日期:日期选择器,精确至天,支持单输入框双日历选择开始日期-结束日期; +2.2.备车人:选择器,支持输入框内输入进行模糊搜索下拉选项匹配,提示信息为:请选择或输入备车人姓名; +2.3.车牌号:选择器,支持输入框内输入进行模糊搜索下拉选项匹配,提示信息为:请选择或输入车牌号; +2.4.车辆类型:选择器,选项包含:轻型厢式货车、重型厢式货车、重型半挂牵引车、轻型厢式货车、重型厢式货车、重型集装箱半挂车、小型普通客车、重型半挂牵引车、重型平板半挂车、叉车、油车、观光车; +2.5.车辆型号:级联选择器,选择品牌及型号; +2.5.停车场:选择器,支持多选,选择所有停车场; + +3.列表:分为已完成、待提交2个tab,已完成tab下显示所有已完成备车的车辆,待提交tab下显示所有仅保存待提交的车辆; +3.1.已完成,列表右侧为新增、导出: + 3.1.1.备车时间:显示备车完成时间,格式为:YYYY-MM-DD; + 3.1.2.备车人:显示备车用户姓名; + 3.1.3.车辆类型:显示备车车辆类型; + 3.1.4.品牌:显示备车车辆品牌; + 3.1.5.型号:显示备车车辆型号; + 3.1.6.车牌号:显示备车车辆车牌号; + 3.1.7.车辆识别代码:显示备车车辆识别代码; + 3.1.8.停车场:显示备车车辆所在停车场; + 3.1.9.车身广告及放大字:显示备车车辆是否有车身广告及放大字,显示为有或无; + 3.1.10.尾板:如尾板为有,显示备车车辆是否有尾板,显示为有或无; + 3.1.11.备胎胎纹深度:显示备车车辆备胎胎纹深度,格式为:xxmm; + 3.1.12.操作:查看; + 3.1.12.1.查看:点击查看,进入备车管理-待提交-查看页; + +3.2.待提交,列表右侧为新增: + 3.2.1.创建时间:显示记录创建时间,格式为:YYYY-MM-DD; + 3.2.2.创建人:显示创建用户姓名; + 3.2.3.车辆类型:显示备车车辆类型; + 3.2.4.品牌:显示备车车辆品牌; + 3.2.5.型号:显示备车车辆型号; + 3.2.6.车牌号:显示备车车辆车牌号; + 3.2.7.车辆识别代码:显示备车车辆识别代码; + 3.2.8.停车场:显示备车车辆所在停车场; + 3.2.9.车身广告及放大字:显示备车车辆是否有车身广告及放大字,显示为有或无; + 3.2.10.广告照片:如车身广告及放大字为有,则显示查看照片,如车身广告及放大字为无,则显示-; + 3.2.11.放大字照片:如车身广告及放大字为有,则显示查看照片,如车身广告及放大字为无,则显示-; + 3.2.12.尾板:如尾板为有,显示备车车辆是否有尾板,显示为有或无; + 3.2.13.备胎胎纹深度:显示备车车辆备胎胎纹深度,格式为:xxmm; + 3.2.14.备胎照片:如备胎照片为有,则显示查看照片,如备胎为无,则显示-; + 3.2.15.瑕疵照片:如瑕疵照片为有,则显示查看照片,如瑕疵照片为无,则显示-; + 3.2.16.操作:查看、编辑、删除; + 3.2.16.1.查看:点击查看,进入备车管理-待提交-查看页; + 3.2.16.2.编辑:点击编辑,进入备车管理-待提交-编辑页; + 3.2.16.3.删除:点击进行二次确认,确认后删除; + +已完成、待提交均支持分页功能,可设置单页查询数据条数; + +备车权限说明: +1.运维人员分区域,对应区域运维人员只能新增自己区域停车场的车辆进行备车; +2.运维人员只能查看自己区域停车场的备车记录; \ No newline at end of file