diff --git a/web端/运维管理/备件管理/备件库存.jsx b/web端/运维管理/备件管理/备件库存.jsx index 0dcb69f..e949456 100644 --- a/web端/运维管理/备件管理/备件库存.jsx +++ b/web端/运维管理/备件管理/备件库存.jsx @@ -1,5 +1,5 @@ // 【重要】必须使用 const Component 作为组件变量名 -// 运维管理 - 备件库 - 备件库存(列表页原型) +// 数字化资产 ONEOS 运管平台 - 运维管理 - 备件库 - 备件库存(原型) const Component = function () { var useState = React.useState; @@ -16,14 +16,55 @@ const Component = function () { var Drawer = antd.Drawer; var Form = antd.Form; var Input = antd.Input; - var Tabs = antd.Tabs; + var InputNumber = antd.InputNumber; + var DatePicker = antd.DatePicker; var message = antd.message; + var Space = antd.Space; + var Descriptions = antd.Descriptions; + var Upload = antd.Upload; + var Image = antd.Image; + + var dayjs = window.dayjs; + + function normUploadFileList(e) { + if (Array.isArray(e)) return e; + return e && e.fileList ? e.fileList : []; + } function filterOption(input, option) { var label = (option && (option.label || option.children)) || ''; return String(label).toLowerCase().indexOf(String(input || '').toLowerCase()) >= 0; } + function todayDayjs() { + if (dayjs) return dayjs().startOf('day'); + return null; + } + + function todayYMD() { + if (dayjs) return dayjs().format('YYYY-MM-DD'); + var d = new Date(); + return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0'); + } + + function csvEscape(s) { + var x = String(s == null ? '' : s); + if (/[",\n\r]/.test(x)) return '"' + x.replace(/"/g, '""') + '"'; + return x; + } + + function downloadCsv(filename, headers, rows) { + var lines = [headers.map(csvEscape).join(',')].concat( + rows.map(function (r) { return r.map(csvEscape).join(','); }) + ); + var blob = new Blob(['\uFEFF' + lines.join('\n')], { type: 'text/csv;charset=utf-8;' }); + var a = document.createElement('a'); + a.href = URL.createObjectURL(blob); + a.download = filename; + a.click(); + URL.revokeObjectURL(a.href); + } + var layoutStyle = { padding: '16px 24px', background: '#f5f5f5', minHeight: '100vh' }; var filterLabelStyle = { marginBottom: 6, fontSize: 14, color: 'rgba(0,0,0,0.65)' }; var filterItemStyle = { marginBottom: 12 }; @@ -31,9 +72,15 @@ const Component = function () { var tableStyle = '.spare-stock-table .ant-table-thead th,.spare-stock-table .ant-table-tbody td{white-space:nowrap;}' + '.spare-stock-table .col-stock-qty{text-align:left !important;}' + '.spare-stock-table th.col-stock-qty{text-align:left !important;}'; - var tabsAlignStyle = '.spare-stock-main-tabs .ant-tabs-nav{align-items:center;margin-bottom:0;}'; - // 适配车型:选项 value 与列表一致;label 附带关联型号(与基本数据-型号参数对应) + // 与「仓库管理」一致的仓库名称选项(入库/领用出库) + var warehouseManageNames = useMemo(function () { + return [ + '天河备件库', '西湖服务备件库', '上海浦东备件仓', '深圳南山备件仓', + '成都高新备件库', '宁波江北备件库', '北京朝阳备件仓', '重庆两江备件库' + ]; + }, []); + var vehicleModelSpecMap = { '飞驰49t': '海格牌KLQ5180XYKFCEV', '宇通49t': 'KLQ6129', @@ -60,8 +107,7 @@ const Component = function () { }); }, [allVehicleModelOptions]); - // 样例数据:新件库 11 条 + 易损件库 5 条(入库/出库与业务截图一致) - var listState = useState(function () { + function buildInitialStockRows() { var XJ = '新件库'; var XJCode = 'WH-XJK'; var YS = '易损件库'; @@ -84,47 +130,80 @@ const Component = function () { { id: 'ss-ys-04', warehouseCode: YSCode, warehouseName: YS, spareCode: 'TY-YS-0004', spareName: '小灯泡-易损件', vehicleModels: [], stockQty: 52, totalInbound: 52, totalOutbound: 0 }, { id: 'ss-ys-05', warehouseCode: YSCode, warehouseName: YS, spareCode: 'TY-YS-0005', spareName: '2405灯泡-易损件', vehicleModels: [], stockQty: 110, totalInbound: 110, totalOutbound: 0 } ]; - }); + } + + function buildSpareCatalogFromRows(rows) { + var map = {}; + (rows || []).forEach(function (r) { + if (!r || !r.spareCode || map[r.spareCode]) return; + var isBulb = /灯泡/.test(r.spareName || ''); + map[r.spareCode] = { + spareCode: r.spareCode, + spareName: r.spareName, + unit: isBulb ? '个' : '件', + vehicleModels: (r.vehicleModels || []).slice(), + alertThreshold: isBulb ? 20 : 8 + }; + }); + return map; + } + + var listState = useState(buildInitialStockRows); var list = listState[0]; - // 入库 / 出库记录样例(原型) - var inboundListState = useState(function () { + var spareCatalogState = useState(function () { + return buildSpareCatalogFromRows(buildInitialStockRows()); + }); + var spareCatalog = spareCatalogState[0]; + var setSpareCatalog = spareCatalogState[1]; + + function getCatalogEntry(spareCode) { + return spareCatalog[spareCode] || null; + } + + function getUnitForCode(spareCode) { + var c = getCatalogEntry(spareCode); + return (c && c.unit) || '件'; + } + + var inboundRecordsState = useState(function () { + var ph1 = 'https://via.placeholder.com/160x120.png?text=Inbound+1'; + var ph2 = 'https://via.placeholder.com/160x120.png?text=Inbound+2'; return [ - { id: 'in-001', billNo: 'RK20250227001', inboundTime: '2025-02-27 09:12', warehouseName: '新件库', spareCode: 'FC49t-XJ-0001', spareName: '去离子柱(飞驰49t-新件)', qty: 4, operator: '张三' }, - { id: 'in-002', billNo: 'RK20250226088', inboundTime: '2025-02-26 14:30', warehouseName: '新件库', spareCode: 'YT49t-XJ-0002', spareName: '燃料电池空气滤芯(宇通49t-新件)', qty: 6, operator: '李四' }, - { id: 'in-003', billNo: 'RK20250225102', inboundTime: '2025-02-25 11:05', warehouseName: '易损件库', spareCode: 'TY-YS-0001', spareName: 'H1灯泡-易损件', qty: 20, operator: '王五' }, - { id: 'in-004', billNo: 'RK20250224056', inboundTime: '2025-02-24 16:20', warehouseName: '新件库', spareCode: 'SL18t-XJ-0002', spareName: '燃料电池空气滤芯(苏龙18t-新件)', qty: 8, operator: '张三' } + { id: 'in-001', warehouseName: '新件库', spareCode: 'FC49t-XJ-0001', spareName: '去离子柱(飞驰49t-新件)', inboundType: '采购', qty: 4, inboundDate: '2025-02-27', operator: '张三', unitPrice: 128.5, remark: '季度补货', photoUrls: [ph1] }, + { id: 'in-002', warehouseName: '新件库', spareCode: 'YT49t-XJ-0002', spareName: '燃料电池空气滤芯(宇通49t-新件)', inboundType: '采购', qty: 6, inboundDate: '2025-02-26', operator: '李四', unitPrice: 86, remark: '', photoUrls: [ph1, ph2] }, + { id: 'in-003', warehouseName: '易损件库', spareCode: 'TY-YS-0001', spareName: 'H1灯泡-易损件', inboundType: '其他', qty: 20, inboundDate: '2025-02-25', operator: '王五', unitPrice: 3.5, remark: '调拨转入', photoUrls: [] }, + { id: 'in-004', warehouseName: '新件库', spareCode: 'SL18t-XJ-0002', spareName: '燃料电池空气滤芯(苏龙18t-新件)', inboundType: '采购', qty: 8, inboundDate: '2025-02-24', operator: '张三', unitPrice: 92.8, remark: '', photoUrls: [ph2] }, + { id: 'in-005', warehouseName: '新件库', spareCode: 'FC49t-XJ-0001', spareName: '去离子柱(飞驰49t-新件)', inboundType: '其他', qty: 2, inboundDate: '2025-02-20', operator: '赵六', unitPrice: 0, remark: '盘盈', photoUrls: [] } ]; }); - var inboundList = inboundListState[0]; + var inboundRecords = inboundRecordsState[0]; - var outboundListState = useState(function () { + var outboundRecordsState = useState(function () { return [ - { id: 'out-001', billNo: 'CK20250227012', outboundTime: '2025-02-27 10:08', warehouseName: '新件库', spareCode: 'FC49t-XJ-0002', spareName: '燃料电池空气滤芯(飞驰49t-新件)', qty: 2, recipient: '维修一组' }, - { id: 'out-002', billNo: 'CK20250226995', outboundTime: '2025-02-26 09:45', warehouseName: '新件库', spareCode: 'XD4.5t-XJ-0008', spareName: '后尾灯(现代4.5t-新件)', qty: 1, recipient: '维修二组' }, - { id: 'out-003', billNo: 'CK20250225880', outboundTime: '2025-02-25 13:22', warehouseName: '易损件库', spareCode: 'TY-YS-0003', spareName: 'H7灯泡-易损件', qty: 5, recipient: '场站 A' }, - { id: 'out-004', billNo: 'CK20250224771', outboundTime: '2025-02-24 08:50', warehouseName: '新件库', spareCode: 'YJ4.5t-XJ-0035', spareName: '12V蓄电池(跃进4.5t-新件)', qty: 1, recipient: '维修一组' } + { id: 'out-001', warehouseName: '新件库', spareCode: 'FC49t-XJ-0002', spareName: '燃料电池空气滤芯(飞驰49t-新件)', outboundType: '维修', qty: 2, outboundDate: '2025-02-27', operator: '周强', remark: '车辆粤A12345' }, + { id: 'out-002', warehouseName: '新件库', spareCode: 'XD4.5t-XJ-0008', spareName: '后尾灯(现代4.5t-新件)', outboundType: '保养', qty: 1, outboundDate: '2025-02-26', operator: '钱敏', remark: '' }, + { id: 'out-003', warehouseName: '易损件库', spareCode: 'TY-YS-0003', spareName: 'H7灯泡-易损件', outboundType: '其他', qty: 5, outboundDate: '2025-02-25', operator: '孙亮', remark: '场站备用' }, + { id: 'out-004', warehouseName: '新件库', spareCode: 'YJ4.5t-XJ-0035', spareName: '12V蓄电池(跃进4.5t-新件)', outboundType: '维修', qty: 1, outboundDate: '2025-02-24', operator: '周强', remark: '' }, + { id: 'out-005', warehouseName: '新件库', spareCode: 'FC49t-XJ-0001', spareName: '去离子柱(飞驰49t-新件)', outboundType: '保养', qty: 1, outboundDate: '2025-02-23', operator: '钱敏', remark: '定期更换' }, + { id: 'out-006', warehouseName: '新件库', spareCode: 'FC49t-XJ-0002', spareName: '燃料电池空气滤芯(飞驰49t-新件)', outboundType: '维修', qty: 1, outboundDate: '2025-02-22', operator: '周强', remark: '粤B88888 进站' }, + { id: 'out-007', warehouseName: '新件库', spareCode: 'YT49t-XJ-0001', spareName: '去离子柱(宇通49t-新件)', outboundType: '维修', qty: 2, outboundDate: '2025-02-21', operator: '吴刚', remark: '' }, + { id: 'out-008', warehouseName: '新件库', spareCode: 'YT49t-XJ-0002', spareName: '燃料电池空气滤芯(宇通49t-新件)', outboundType: '保养', qty: 3, outboundDate: '2025-02-20', operator: '钱敏', remark: '车队批量保养' }, + { id: 'out-009', warehouseName: '新件库', spareCode: 'YT49t-XJ-0029', spareName: '小循环滤网(宇通49t-新件)', outboundType: '维修', qty: 1, outboundDate: '2025-02-19', operator: '郑华', remark: '泄漏检修更换' }, + { id: 'out-010', warehouseName: '新件库', spareCode: 'SL18t-XJ-0001', spareName: '去离子柱(苏龙18t-新件)', outboundType: '其他', qty: 1, outboundDate: '2025-02-18', operator: '孙亮', remark: '调拨至华东服务站' }, + { id: 'out-011', warehouseName: '新件库', spareCode: 'SL18t-XJ-0002', spareName: '燃料电池空气滤芯(苏龙18t-新件)', outboundType: '维修', qty: 2, outboundDate: '2025-02-17', operator: '周强', remark: '' }, + { id: 'out-012', warehouseName: '易损件库', spareCode: 'TY-YS-0001', spareName: 'H1灯泡-易损件', outboundType: '维修', qty: 8, outboundDate: '2025-02-16', operator: '赵磊', remark: '夜间抢修多车' }, + { id: 'out-013', warehouseName: '易损件库', spareCode: 'TY-YS-0001', spareName: 'H1灯泡-易损件', outboundType: '其他', qty: 4, outboundDate: '2025-02-15', operator: '孙亮', remark: '场站灯箱维护' }, + { id: 'out-014', warehouseName: '易损件库', spareCode: 'TY-YS-0002', spareName: 'H3灯泡-易损件', outboundType: '保养', qty: 6, outboundDate: '2025-02-14', operator: '钱敏', remark: '' }, + { id: 'out-015', warehouseName: '易损件库', spareCode: 'TY-YS-0004', spareName: '小灯泡-易损件', outboundType: '其他', qty: 10, outboundDate: '2025-02-13', operator: '吴刚', remark: '库存盘点出库' }, + { id: 'out-016', warehouseName: '易损件库', spareCode: 'TY-YS-0005', spareName: '2405灯泡-易损件', outboundType: '维修', qty: 12, outboundDate: '2025-02-12', operator: '郑华', remark: '批量更换老化灯泡' }, + { id: 'out-017', warehouseName: '新件库', spareCode: 'YT49t-XJ-0030', spareName: '大循环滤网(宇通49t-新件)', outboundType: '保养', qty: 2, outboundDate: '2025-02-11', operator: '钱敏', remark: '粤C22334' }, + { id: 'out-018', warehouseName: '新件库', spareCode: 'YT49t-XJ-0005', spareName: '燃料电池去离子水(宇通49t-新件)', outboundType: '其他', qty: 1, outboundDate: '2025-02-10', operator: '孙亮', remark: '试验用料' }, + { id: 'out-019', warehouseName: '新件库', spareCode: 'FC49t-XJ-0001', spareName: '去离子柱(飞驰49t-新件)', outboundType: '维修', qty: 3, outboundDate: '2025-02-09', operator: '周强', remark: '质保期内更换' }, + { id: 'out-020', warehouseName: '新件库', spareCode: 'XD4.5t-XJ-0008', spareName: '后尾灯(现代4.5t-新件)', outboundType: '维修', qty: 2, outboundDate: '2025-02-08', operator: '赵磊', remark: '追尾事故修复' } ]; }); - var outboundList = outboundListState[0]; - - var inboundPageState = useState(1); - var inboundPage = inboundPageState[0]; - var setInboundPage = inboundPageState[1]; - var inboundPageSizeState = useState(10); - var inboundPageSize = inboundPageSizeState[0]; - var setInboundPageSize = inboundPageSizeState[1]; - - var outboundPageState = useState(1); - var outboundPage = outboundPageState[0]; - var setOutboundPage = outboundPageState[1]; - var outboundPageSizeState = useState(10); - var outboundPageSize = outboundPageSizeState[0]; - var setOutboundPageSize = outboundPageSizeState[1]; - - var mainTabState = useState('stock'); - var mainTab = mainTabState[0]; - var setMainTab = mainTabState[1]; + var outboundRecords = outboundRecordsState[0]; var defaultDraft = { warehouseName: undefined, @@ -157,7 +236,43 @@ const Component = function () { var addDrawerOpen = addDrawerState[0]; var setAddDrawerOpen = addDrawerState[1]; + var inboundDrawerState = useState(false); + var inboundDrawerOpen = inboundDrawerState[0]; + var setInboundDrawerOpen = inboundDrawerState[1]; + + var outboundDrawerState = useState(false); + var outboundDrawerOpen = outboundDrawerState[0]; + var setOutboundDrawerOpen = outboundDrawerState[1]; + + var detailRowState = useState(null); + var detailRow = detailRowState[0]; + var setDetailRow = detailRowState[1]; + + var inboundHistRowState = useState(null); + var inboundHistRow = inboundHistRowState[0]; + var setInboundHistRow = inboundHistRowState[1]; + + var outboundHistRowState = useState(null); + var outboundHistRow = outboundHistRowState[0]; + var setOutboundHistRow = outboundHistRowState[1]; + + var histInboundPageState = useState(1); + var histInboundPage = histInboundPageState[0]; + var setHistInboundPage = histInboundPageState[1]; + var histInboundPageSizeState = useState(10); + var histInboundPageSize = histInboundPageSizeState[0]; + var setHistInboundPageSize = histInboundPageSizeState[1]; + + var histOutboundPageState = useState(1); + var histOutboundPage = histOutboundPageState[0]; + var setHistOutboundPage = histOutboundPageState[1]; + var histOutboundPageSizeState = useState(10); + var histOutboundPageSize = histOutboundPageSizeState[0]; + var setHistOutboundPageSize = histOutboundPageSizeState[1]; + var addForm = Form.useForm()[0]; + var inboundForm = Form.useForm()[0]; + var outboundForm = Form.useForm()[0]; var requirementDocContent = [ '一个「数字化资产ONEOS运管平台」中的「备件库存」模块', @@ -169,36 +284,54 @@ const Component = function () { '1.3.备件名称:选择器,支持输入框内输入模糊搜索,下拉匹配正确项;', '1.4.适配车型:选择器,支持多选,显示所有型号;', '', - '2.列表;右侧为新增物料信息、导出按钮', - '2.1.序号:1.2.3.以此类推;', - '2.2.仓库编码:显示仓库编码;', - '2.3.仓库名称:显示仓库名称;', - '2.4.备件编码:显示备件编码;', - '2.5.备件名称:显示备件名称;', - '2.6.适配车型:显示适配车型,一个备件可能有多个车型;', - '2.7.库存数量:显示库存数量;', - '2.8.累积入库数量:显示该备件入库数量总和;', - '2.9.累积出库数量:显示该备件出库数量总和;', - '2.10.右下角为分页功能,支持选择单页显示数据条数;' + '2.列表;右侧为新增备件信息、入库、领用出库、批量导入、导出按钮', + '2.1~2.9 列表字段与操作见需求原文;', + '2.10 备件明细 / 入库记录(含入库照片预览与 CSV)/ 出库记录;', + '2.11 分页与每页条数;', + '3.新增备件信息 4.入库(含入库照片上传预览删除、单价后缀元)5.领用出库 6.批量导入模板 7.导出' ].join('\n'); var warehouseNameOptions = useMemo(function () { var set = new Set(); + warehouseManageNames.forEach(function (w) { set.add(w); }); (list || []).forEach(function (r) { if (r && r.warehouseName) set.add(r.warehouseName); }); return Array.from(set).map(function (v) { return { value: v, label: v }; }); - }, [list]); + }, [list, warehouseManageNames]); + + var warehouseOptionsForForm = useMemo(function () { + return warehouseManageNames.map(function (w) { return { value: w, label: w }; }); + }, [warehouseManageNames]); var spareCodeOptions = useMemo(function () { var set = new Set(); (list || []).forEach(function (r) { if (r && r.spareCode) set.add(r.spareCode); }); + Object.keys(spareCatalog).forEach(function (k) { set.add(k); }); return Array.from(set).map(function (v) { return { value: v, label: v }; }); - }, [list]); + }, [list, spareCatalog]); var spareNameOptions = useMemo(function () { var set = new Set(); (list || []).forEach(function (r) { if (r && r.spareName) set.add(r.spareName); }); + Object.keys(spareCatalog).forEach(function (k) { + var c = spareCatalog[k]; + if (c && c.spareName) set.add(c.spareName); + }); return Array.from(set).map(function (v) { return { value: v, label: v }; }); - }, [list]); + }, [list, spareCatalog]); + + var spareSelectByNameOptions = useMemo(function () { + var byCode = {}; + Object.keys(spareCatalog).forEach(function (code) { + var c = spareCatalog[code]; + if (c) byCode[code] = { value: code, label: c.spareName + '(' + code + ')' }; + }); + (list || []).forEach(function (r) { + if (r && r.spareCode && !byCode[r.spareCode]) { + byCode[r.spareCode] = { value: r.spareCode, label: r.spareName + '(' + r.spareCode + ')' }; + } + }); + return Object.keys(byCode).map(function (k) { return byCode[k]; }); + }, [list, spareCatalog]); function rowMatchesVehicleModels(row, selected) { if (!selected || selected.length === 0) return true; @@ -253,36 +386,198 @@ const Component = function () { }, [addForm]); var handleAddDrawerSubmit = useCallback(function () { - addForm.validateFields().then(function () { + addForm.validateFields().then(function (vals) { + setSpareCatalog(function (prev) { + var next = Object.assign({}, prev); + next[vals.spareCode] = { + spareCode: vals.spareCode, + spareName: vals.spareName, + unit: vals.unit, + vehicleModels: vals.vehicleModels || [], + alertThreshold: vals.alertThreshold + }; + return next; + }); message.success('已保存(原型,联调时对接建档接口)'); setAddDrawerOpen(false); addForm.resetFields(); }).catch(function () {}); - }, [addForm]); + }, [addForm, setSpareCatalog]); - var handleExport = useCallback(function () { - message.success('导出当前筛选结果(原型,联调时对接导出接口)'); + var handleOpenInboundForm = useCallback(function () { + inboundForm.resetFields(); + inboundForm.setFieldsValue({ + inboundDate: todayDayjs() || undefined, + qty: 1, + inboundType: '采购', + unitPrice: undefined, + remark: '', + inboundPhotos: [] + }); + setInboundDrawerOpen(true); + }, [inboundForm]); + + var handleInboundFormClose = useCallback(function () { + var fl = inboundForm.getFieldValue('inboundPhotos') || []; + fl.forEach(function (f) { + var u = f.url || f.thumbUrl; + if (u && String(u).indexOf('blob:') === 0) { + try { URL.revokeObjectURL(u); } catch (e) {} + } + }); + setInboundDrawerOpen(false); + inboundForm.resetFields(); + }, [inboundForm]); + + var handleInboundFormSubmit = useCallback(function () { + inboundForm.validateFields().then(function () { + message.success('入库已提交(原型,联调对接接口)'); + setInboundDrawerOpen(false); + inboundForm.resetFields(); + }).catch(function () {}); + }, [inboundForm]); + + var handleOpenOutboundForm = useCallback(function () { + outboundForm.resetFields(); + outboundForm.setFieldsValue({ + outboundDate: todayDayjs() || undefined, + qty: 1, + outboundType: '维修', + remark: '' + }); + setOutboundDrawerOpen(true); + }, [outboundForm]); + + var handleOutboundFormClose = useCallback(function () { + setOutboundDrawerOpen(false); + outboundForm.resetFields(); + }, [outboundForm]); + + var handleOutboundFormSubmit = useCallback(function () { + outboundForm.validateFields().then(function () { + message.success('领用出库已提交(原型,联调对接接口)'); + setOutboundDrawerOpen(false); + outboundForm.resetFields(); + }).catch(function () {}); + }, [outboundForm]); + + var handleDownloadImportTemplate = useCallback(function () { + var headers = ['仓库名称', '备件名称', '入库类型', '入库数量', '入库日期', '单价', '入库照片', '备注']; + var sample = [ + ['天河备件库', '示例备件名称', '采购', '10', todayYMD(), '99.99', '图片文件名或URL,可留空', '模板示例行,可删除'] + ]; + downloadCsv('备件入库导入模板.csv', headers, sample); + message.success('已下载 CSV 模板'); }, []); + var handleExportList = useCallback(function () { + var headers = ['序号', '仓库编码', '仓库名称', '备件编码', '备件名称', '适配车型', '库存数量', '累积入库数量', '累积出库数量']; + var rows = filtered.map(function (r, i) { + var vm = (r.vehicleModels || []).length ? r.vehicleModels.join('、') : '-'; + return [ + String(i + 1), + r.warehouseCode, + r.warehouseName, + r.spareCode, + r.spareName, + vm, + r.stockQty, + r.totalInbound, + r.totalOutbound + ]; + }); + downloadCsv('备件库存导出_' + todayYMD() + '.csv', headers, rows); + message.success('已按当前筛选条件导出 ' + rows.length + ' 条'); + }, [filtered]); + + var openDetail = useCallback(function (row) { + setDetailRow(row); + }, []); + + var openInboundHist = useCallback(function (row) { + setInboundHistRow(row); + setHistInboundPage(1); + }, []); + + var openOutboundHist = useCallback(function (row) { + setOutboundHistRow(row); + setHistOutboundPage(1); + }, []); + + var filteredInboundHist = useMemo(function () { + if (!inboundHistRow) return []; + return (inboundRecords || []).filter(function (r) { + return r.warehouseName === inboundHistRow.warehouseName && r.spareCode === inboundHistRow.spareCode; + }); + }, [inboundRecords, inboundHistRow]); + + var filteredOutboundHist = useMemo(function () { + if (!outboundHistRow) return []; + return (outboundRecords || []).filter(function (r) { + return r.warehouseName === outboundHistRow.warehouseName && r.spareCode === outboundHistRow.spareCode; + }); + }, [outboundRecords, outboundHistRow]); + + var exportInboundHistCsv = useCallback(function () { + if (!inboundHistRow) return; + var unit = getUnitForCode(inboundHistRow.spareCode); + var headers = ['备件编码', '备件名称', '入库类型', '入库数量', '入库日期', '入库人员', '单价', '入库照片', '备注']; + var rows = filteredInboundHist.map(function (r) { + var pics = (r.photoUrls && r.photoUrls.length) ? r.photoUrls.join(';') : '无'; + return [ + r.spareCode, + r.spareName, + r.inboundType, + String(r.qty) + '(' + unit + ')', + r.inboundDate, + r.operator, + (Number(r.unitPrice).toFixed(2)) + '元', + pics, + r.remark || '' + ]; + }); + downloadCsv('入库记录_' + inboundHistRow.spareCode + '_' + todayYMD() + '.csv', headers, rows); + message.success('已导出 ' + rows.length + ' 条'); + }, [filteredInboundHist, inboundHistRow, spareCatalog]); + + var exportOutboundHistCsv = useCallback(function () { + if (!outboundHistRow) return; + var unit = getUnitForCode(outboundHistRow.spareCode); + var headers = ['备件编码', '备件名称', '出库类型', '出库数量', '出库日期', '出库人员', '备注']; + var rows = filteredOutboundHist.map(function (r) { + return [ + r.spareCode, + r.spareName, + r.outboundType, + String(r.qty) + '(' + unit + ')', + r.outboundDate, + r.operator, + r.remark || '' + ]; + }); + downloadCsv('出库记录_' + outboundHistRow.spareCode + '_' + todayYMD() + '.csv', headers, rows); + message.success('已导出 ' + rows.length + ' 条'); + }, [filteredOutboundHist, outboundHistRow, spareCatalog]); + var columns = useMemo(function () { return [ { title: '序号', key: 'serial', - width: 70, + width: 64, fixed: 'left', render: function (_, r, idx) { return serialStart + idx + 1; } }, - { title: '仓库编码', dataIndex: 'warehouseCode', key: 'warehouseCode', width: 120, fixed: 'left' }, - { title: '仓库名称', dataIndex: 'warehouseName', key: 'warehouseName', width: 160, ellipsis: true }, - { title: '备件编码', dataIndex: 'spareCode', key: 'spareCode', width: 120 }, - { title: '备件名称', dataIndex: 'spareName', key: 'spareName', width: 260, ellipsis: true }, + { title: '仓库编码', dataIndex: 'warehouseCode', key: 'warehouseCode', width: 112, fixed: 'left' }, + { title: '仓库名称', dataIndex: 'warehouseName', key: 'warehouseName', width: 140, ellipsis: true }, + { title: '备件编码', dataIndex: 'spareCode', key: 'spareCode', width: 130 }, + { title: '备件名称', dataIndex: 'spareName', key: 'spareName', width: 240, ellipsis: true }, { title: '适配车型', key: 'vehicleModels', - width: 220, + width: 200, ellipsis: true, render: function (_, r) { var arr = r.vehicleModels || []; @@ -293,7 +588,7 @@ const Component = function () { title: '库存数量', dataIndex: 'stockQty', key: 'stockQty', - width: 100, + width: 96, align: 'left', className: 'col-stock-qty', onHeaderCell: function () { return { className: 'col-stock-qty' }; }, @@ -312,129 +607,140 @@ const Component = function () { title: '累积入库数量', dataIndex: 'totalInbound', key: 'totalInbound', - width: 130, - render: function (v) { return v != null ? Number(v).toFixed(1) : '-'; } + width: 120, + render: function (v) { return v != null ? v : '-'; } }, { title: '累积出库数量', dataIndex: 'totalOutbound', key: 'totalOutbound', - width: 130, - render: function (v) { return v != null ? Number(v).toFixed(1) : '-'; } + width: 120, + render: function (v) { return v != null ? v : '-'; } + }, + { + title: '操作', + key: 'actions', + width: 220, + fixed: 'right', + render: function (_, row) { + return React.createElement(Space, { size: 'small', wrap: true }, + React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 }, onClick: function () { openDetail(row); } }, '备件明细'), + React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 }, onClick: function () { openInboundHist(row); } }, '入库记录'), + React.createElement(Button, { type: 'link', size: 'small', style: { padding: 0 }, onClick: function () { openOutboundHist(row); } }, '出库记录') + ); + } } ]; - }, [serialStart]); + }, [serialStart, openDetail, openInboundHist, openOutboundHist]); - var inboundSerialStart = (inboundPage - 1) * inboundPageSize; - var inboundPaged = useMemo(function () { - var start = (inboundPage - 1) * inboundPageSize; - return (inboundList || []).slice(start, start + inboundPageSize); - }, [inboundList, inboundPage, inboundPageSize]); + var histInboundSerial = (histInboundPage - 1) * histInboundPageSize; + var histInboundPaged = useMemo(function () { + var s = (histInboundPage - 1) * histInboundPageSize; + return filteredInboundHist.slice(s, s + histInboundPageSize); + }, [filteredInboundHist, histInboundPage, histInboundPageSize]); - var outboundSerialStart = (outboundPage - 1) * outboundPageSize; - var outboundPaged = useMemo(function () { - var start = (outboundPage - 1) * outboundPageSize; - return (outboundList || []).slice(start, start + outboundPageSize); - }, [outboundList, outboundPage, outboundPageSize]); + var histOutboundSerial = (histOutboundPage - 1) * histOutboundPageSize; + var histOutboundPaged = useMemo(function () { + var s = (histOutboundPage - 1) * histOutboundPageSize; + return filteredOutboundHist.slice(s, s + histOutboundPageSize); + }, [filteredOutboundHist, histOutboundPage, histOutboundPageSize]); - var inboundColumns = useMemo(function () { - return [ - { title: '序号', key: 'serial', width: 70, render: function (_, r, idx) { return inboundSerialStart + idx + 1; } }, - { title: '入库单号', dataIndex: 'billNo', key: 'billNo', width: 150 }, - { title: '入库时间', dataIndex: 'inboundTime', key: 'inboundTime', width: 160 }, - { title: '仓库名称', dataIndex: 'warehouseName', key: 'warehouseName', width: 120 }, - { title: '备件编码', dataIndex: 'spareCode', key: 'spareCode', width: 130 }, - { title: '备件名称', dataIndex: 'spareName', key: 'spareName', ellipsis: true }, - { title: '入库数量', dataIndex: 'qty', key: 'qty', width: 100 }, - { title: '经办人', dataIndex: 'operator', key: 'operator', width: 100 } - ]; - }, [inboundSerialStart]); - - var outboundColumns = useMemo(function () { - return [ - { title: '序号', key: 'serial', width: 70, render: function (_, r, idx) { return outboundSerialStart + idx + 1; } }, - { title: '出库单号', dataIndex: 'billNo', key: 'billNo', width: 150 }, - { title: '出库时间', dataIndex: 'outboundTime', key: 'outboundTime', width: 160 }, - { title: '仓库名称', dataIndex: 'warehouseName', key: 'warehouseName', width: 120 }, - { title: '备件编码', dataIndex: 'spareCode', key: 'spareCode', width: 130 }, - { title: '备件名称', dataIndex: 'spareName', key: 'spareName', ellipsis: true }, - { title: '出库数量', dataIndex: 'qty', key: 'qty', width: 100 }, - { title: '领用方', dataIndex: 'recipient', key: 'recipient', width: 120 } - ]; - }, [outboundSerialStart]); - - var tabItems = useMemo(function () { - var stockTab = React.createElement(Card, { style: { marginTop: 0 } }, - React.createElement('style', null, tableStyle), - React.createElement('div', { className: 'spare-stock-table' }, - React.createElement(Table, { - rowKey: 'id', - columns: columns, - dataSource: paged, - scroll: { x: 1460 }, - size: 'small', - pagination: { - current: page, - pageSize: pageSize, - total: filtered.length, - showSizeChanger: true, - showQuickJumper: true, - showTotal: function (t) { return '共 ' + t + ' 条'; }, - onChange: function (pg, ps) { setPage(pg); if (ps) setPageSize(ps); } - } + var inboundHistColumns = useMemo(function () { + function renderInboundPhotos(_, r) { + var urls = r.photoUrls || []; + if (!urls.length) return '-'; + if (Image && Image.PreviewGroup) { + return React.createElement( + Image.PreviewGroup, + null, + urls.map(function (u, i) { + return React.createElement(Image, { + key: i, + width: 52, + height: 52, + style: { objectFit: 'cover', borderRadius: 4, marginRight: 4, cursor: 'pointer', verticalAlign: 'middle' }, + src: u, + alt: '入库照片' + }); + }) + ); + } + return React.createElement(Space, { size: 4, wrap: true }, + urls.map(function (u, i) { + return React.createElement('img', { + key: i, + src: u, + alt: '', + style: { width: 52, height: 52, objectFit: 'cover', borderRadius: 4, cursor: 'pointer' }, + onClick: function () { window.open(u, '_blank'); } + }); }) - ) - ); - - var inboundTab = React.createElement(Card, null, - React.createElement(Table, { - rowKey: 'id', - columns: inboundColumns, - dataSource: inboundPaged, - size: 'small', - scroll: { x: 1200 }, - pagination: { - current: inboundPage, - pageSize: inboundPageSize, - total: (inboundList || []).length, - showSizeChanger: true, - showQuickJumper: true, - showTotal: function (t) { return '共 ' + t + ' 条'; }, - onChange: function (pg, ps) { setInboundPage(pg); if (ps) setInboundPageSize(ps); } - } - }) - ); - - var outboundTab = React.createElement(Card, null, - React.createElement(Table, { - rowKey: 'id', - columns: outboundColumns, - dataSource: outboundPaged, - size: 'small', - scroll: { x: 1200 }, - pagination: { - current: outboundPage, - pageSize: outboundPageSize, - total: (outboundList || []).length, - showSizeChanger: true, - showQuickJumper: true, - showTotal: function (t) { return '共 ' + t + ' 条'; }, - onChange: function (pg, ps) { setOutboundPage(pg); if (ps) setOutboundPageSize(ps); } - } - }) - ); - + ); + } return [ - { key: 'stock', label: '备件库存', children: stockTab }, - { key: 'inbound', label: '入库记录', children: inboundTab }, - { key: 'outbound', label: '出库记录', children: outboundTab } + { title: '序号', key: 's', width: 60, render: function (_, r, i) { return histInboundSerial + i + 1; } }, + { title: '备件编码', dataIndex: 'spareCode', key: 'spareCode', width: 130 }, + { title: '备件名称', dataIndex: 'spareName', key: 'spareName', ellipsis: true }, + { title: '入库类型', dataIndex: 'inboundType', key: 'inboundType', width: 88 }, + { + title: '入库数量', + key: 'qtyDisp', + width: 110, + render: function (_, r) { + var u = getUnitForCode(r.spareCode); + return r.qty + '(' + u + ')'; + } + }, + { title: '入库日期', dataIndex: 'inboundDate', key: 'inboundDate', width: 112 }, + { title: '入库人员', dataIndex: 'operator', key: 'operator', width: 96 }, + { + title: '单价', + key: 'price', + width: 100, + render: function (_, r) { + return Number(r.unitPrice).toFixed(2) + '元'; + } + }, + { title: '入库照片', key: 'photos', width: 200, render: renderInboundPhotos }, + { title: '备注', dataIndex: 'remark', key: 'remark', ellipsis: true } ]; - }, [ - columns, paged, page, pageSize, filtered.length, - inboundColumns, inboundPaged, inboundPage, inboundPageSize, inboundList, - outboundColumns, outboundPaged, outboundPage, outboundPageSize, outboundList, - tableStyle - ]); + }, [histInboundSerial, spareCatalog]); + + var outboundHistColumns = useMemo(function () { + return [ + { title: '序号', key: 's', width: 60, render: function (_, r, i) { return histOutboundSerial + i + 1; } }, + { title: '备件编码', dataIndex: 'spareCode', key: 'spareCode', width: 130 }, + { title: '备件名称', dataIndex: 'spareName', key: 'spareName', ellipsis: true }, + { title: '出库类型', dataIndex: 'outboundType', key: 'outboundType', width: 88 }, + { + title: '出库数量', + key: 'qtyDisp', + width: 110, + render: function (_, r) { + var u = getUnitForCode(r.spareCode); + return r.qty + '(' + u + ')'; + } + }, + { title: '出库日期', dataIndex: 'outboundDate', key: 'outboundDate', width: 112 }, + { title: '出库人员', dataIndex: 'operator', key: 'operator', width: 96 }, + { title: '备注', dataIndex: 'remark', key: 'remark', ellipsis: true } + ]; + }, [histOutboundSerial, spareCatalog]); + + var detailDescItems = useMemo(function () { + if (!detailRow) return []; + var cat = getCatalogEntry(detailRow.spareCode); + var vm = (detailRow.vehicleModels || []).length + ? detailRow.vehicleModels.join('、') + : (cat && cat.vehicleModels && cat.vehicleModels.length ? cat.vehicleModels.join('、') : '-'); + return [ + { key: 'c', label: '备件编码', children: detailRow.spareCode }, + { key: 'n', label: '备件名称', children: detailRow.spareName }, + { key: 'u', label: '计算单位', children: (cat && cat.unit) || '-' }, + { key: 'v', label: '适配车型', children: vm }, + { key: 'a', label: '告警阈值', children: cat && cat.alertThreshold != null ? String(cat.alertThreshold) : '-' } + ]; + }, [detailRow, spareCatalog]); return React.createElement('div', { style: layoutStyle }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16, flexWrap: 'wrap', gap: 8 } }, @@ -452,7 +758,74 @@ const Component = function () { }, React.createElement('div', { style: { whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.6, color: 'rgba(0,0,0,0.85)' } }, requirementDocContent)), React.createElement(Drawer, { - title: '新增物料信息', + title: '备件明细', + placement: 'right', + width: 440, + open: !!detailRow, + onClose: function () { setDetailRow(null); }, + destroyOnClose: true + }, + detailRow ? React.createElement(Descriptions, { column: 1, bordered: true, size: 'small' }, + detailDescItems.map(function (it) { + return React.createElement(Descriptions.Item, { key: it.key, label: it.label }, it.children); + }) + ) : null + ), + + React.createElement(Drawer, { + title: inboundHistRow ? '入库记录 · ' + inboundHistRow.spareName : '入库记录', + placement: 'right', + width: 1120, + open: !!inboundHistRow, + onClose: function () { setInboundHistRow(null); }, + destroyOnClose: true, + extra: React.createElement(Button, { type: 'primary', size: 'small', onClick: exportInboundHistCsv, disabled: !filteredInboundHist.length }, '导出 CSV') + }, + React.createElement(Table, { + rowKey: 'id', + size: 'small', + columns: inboundHistColumns, + dataSource: histInboundPaged, + scroll: { x: 1180 }, + pagination: { + current: histInboundPage, + pageSize: histInboundPageSize, + total: filteredInboundHist.length, + showSizeChanger: true, + showTotal: function (t) { return '共 ' + t + ' 条'; }, + onChange: function (pg, ps) { setHistInboundPage(pg); if (ps) setHistInboundPageSize(ps); } + } + }) + ), + + React.createElement(Drawer, { + title: outboundHistRow ? '出库记录 · ' + outboundHistRow.spareName : '出库记录', + placement: 'right', + width: 880, + open: !!outboundHistRow, + onClose: function () { setOutboundHistRow(null); }, + destroyOnClose: true, + extra: React.createElement(Button, { type: 'primary', size: 'small', onClick: exportOutboundHistCsv, disabled: !filteredOutboundHist.length }, '导出 CSV') + }, + React.createElement(Table, { + rowKey: 'id', + size: 'small', + columns: outboundHistColumns, + dataSource: histOutboundPaged, + scroll: { x: 860 }, + pagination: { + current: histOutboundPage, + pageSize: histOutboundPageSize, + total: filteredOutboundHist.length, + showSizeChanger: true, + showTotal: function (t) { return '共 ' + t + ' 条'; }, + onChange: function (pg, ps) { setHistOutboundPage(pg); if (ps) setHistOutboundPageSize(ps); } + } + }) + ), + + React.createElement(Drawer, { + title: '新增备件信息', placement: 'right', width: 440, open: addDrawerOpen, @@ -463,11 +836,7 @@ const Component = function () { React.createElement(Button, { type: 'primary', onClick: handleAddDrawerSubmit }, '确定') ) }, - React.createElement(Form, { - form: addForm, - layout: 'vertical', - preserve: false - }, + React.createElement(Form, { form: addForm, layout: 'vertical', preserve: false }, React.createElement(Form.Item, { name: 'spareCode', label: '备件编码', @@ -481,15 +850,14 @@ const Component = function () { React.createElement(Form.Item, { name: 'unit', label: '计算单位', - rules: [{ required: true, message: '请输入计算单位' }] - }, React.createElement(Input, { placeholder: '请输入计算单位', allowClear: true })), + rules: [{ required: true, message: '请输入计量单位' }] + }, React.createElement(Input, { placeholder: '请输入计量单位', allowClear: true })), React.createElement(Form.Item, { name: 'vehicleModels', - label: '适配车型', - rules: [{ required: true, message: '请选择适配车型', type: 'array', min: 1 }] + label: '适配车型' }, React.createElement(Select, { mode: 'multiple', - placeholder: '请选择车型(可多选,选项含关联型号)', + placeholder: '请选择车型(选填)', allowClear: true, options: addDrawerVehicleOptions, showSearch: true, @@ -499,13 +867,185 @@ const Component = function () { React.createElement(Form.Item, { name: 'alertThreshold', label: '告警阈值', - extra: React.createElement('span', { style: { color: 'rgba(0,0,0,0.45)', fontSize: 12 } }, '备件库存低于阈值会推送提示用户') - }, React.createElement(Input, { placeholder: '请设置备件告警阈值', allowClear: true })) + rules: [{ required: true, message: '请设置备件告警阈值' }] + }, React.createElement(InputNumber, { + placeholder: '请设置备件告警阈值', + style: { width: '100%' }, + min: 1, + precision: 0, + step: 1 + })) ) ), - mainTab === 'stock' ? React.createElement(Card, { style: { marginBottom: 16 } }, - React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: '16px 24px', alignItems: 'start' } }, + React.createElement(Drawer, { + title: '入库', + placement: 'right', + width: 480, + open: inboundDrawerOpen, + onClose: handleInboundFormClose, + destroyOnClose: true, + footer: React.createElement('div', { style: { textAlign: 'right' } }, + React.createElement(Button, { onClick: handleInboundFormClose, style: { marginRight: 8 } }, '取消'), + React.createElement(Button, { type: 'primary', onClick: handleInboundFormSubmit }, '确定') + ) + }, + React.createElement(Form, { form: inboundForm, layout: 'vertical', preserve: false }, + React.createElement(Form.Item, { + name: 'warehouseName', + label: '仓库名称', + rules: [{ required: true, message: '请选择仓库名称' }] + }, React.createElement(Select, { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + options: warehouseOptionsForForm, + filterOption: filterOption + })), + React.createElement(Form.Item, { + name: 'spareCode', + label: '备件名称', + rules: [{ required: true, message: '请选择备件' }] + }, React.createElement(Select, { + placeholder: '请选择备件', + allowClear: true, + showSearch: true, + options: spareSelectByNameOptions, + filterOption: filterOption + })), + React.createElement(Form.Item, { + name: 'inboundType', + label: '入库类型', + rules: [{ required: true, message: '请选择入库类型' }] + }, React.createElement(Select, { + placeholder: '请选择', + options: [{ value: '采购', label: '采购' }, { value: '其他', label: '其他' }] + })), + React.createElement(Form.Item, { + name: 'qty', + label: '入库数量', + rules: [{ required: true, message: '请输入入库数量' }] + }, React.createElement(InputNumber, { + style: { width: '100%' }, + min: 1, + precision: 0, + step: 1 + })), + React.createElement(Form.Item, { + name: 'inboundDate', + label: '入库日期', + rules: [{ required: true, message: '请选择入库日期' }] + }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD' })), + React.createElement(Form.Item, { label: '单价' }, + React.createElement(Input.Group, { compact: true, style: { display: 'flex', width: '100%' } }, + React.createElement(Form.Item, { + name: 'unitPrice', + noStyle: true, + rules: [{ required: true, message: '请输入单价' }] + }, React.createElement(InputNumber, { + style: { width: 'calc(100% - 40px)' }, + min: 0, + precision: 2, + step: 0.01, + placeholder: '支持两位小数' + })), + React.createElement(Input, { + style: { width: 40, textAlign: 'center', padding: '0 4px' }, + disabled: true, + value: '元' + }) + ) + ), + React.createElement(Form.Item, { + name: 'inboundPhotos', + label: '入库照片', + valuePropName: 'fileList', + getValueFromEvent: normUploadFileList, + extra: React.createElement('span', { style: { fontSize: 12, color: 'rgba(0,0,0,0.45)' } }, '本地选择图片,点击缩略图可放大预览;卡片右上角可删除') + }, React.createElement(Upload, { + listType: 'picture-card', + accept: 'image/*', + beforeUpload: function () { return false; }, + maxCount: 9, + multiple: true + }, React.createElement('div', null, + React.createElement('div', { style: { fontSize: 22, lineHeight: 1 } }, '+'), + React.createElement('div', { style: { marginTop: 6, fontSize: 12, color: 'rgba(0,0,0,0.65)' } }, '上传照片') + ))), + React.createElement(Form.Item, { name: 'remark', label: '备注' }, + React.createElement(Input.TextArea, { rows: 3, placeholder: '请输入备注', allowClear: true })) + ) + ), + + React.createElement(Drawer, { + title: '领用出库', + placement: 'right', + width: 440, + open: outboundDrawerOpen, + onClose: handleOutboundFormClose, + destroyOnClose: true, + footer: React.createElement('div', { style: { textAlign: 'right' } }, + React.createElement(Button, { onClick: handleOutboundFormClose, style: { marginRight: 8 } }, '取消'), + React.createElement(Button, { type: 'primary', onClick: handleOutboundFormSubmit }, '确定') + ) + }, + React.createElement(Form, { form: outboundForm, layout: 'vertical', preserve: false }, + React.createElement(Form.Item, { + name: 'warehouseName', + label: '仓库名称', + rules: [{ required: true, message: '请选择仓库名称' }] + }, React.createElement(Select, { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + options: warehouseOptionsForForm, + filterOption: filterOption + })), + React.createElement(Form.Item, { + name: 'spareCode', + label: '备件名称', + rules: [{ required: true, message: '请选择备件' }] + }, React.createElement(Select, { + placeholder: '请选择备件', + allowClear: true, + showSearch: true, + options: spareSelectByNameOptions, + filterOption: filterOption + })), + React.createElement(Form.Item, { + name: 'outboundType', + label: '出库类型', + rules: [{ required: true, message: '请选择出库类型' }] + }, React.createElement(Select, { + placeholder: '请选择', + options: [ + { value: '维修', label: '维修' }, + { value: '保养', label: '保养' }, + { value: '其他', label: '其他' } + ] + })), + React.createElement(Form.Item, { + name: 'qty', + label: '出库数量', + rules: [{ required: true, message: '请输入出库数量' }] + }, React.createElement(InputNumber, { + style: { width: '100%' }, + min: 1, + precision: 0, + step: 1 + })), + React.createElement(Form.Item, { + name: 'outboundDate', + label: '出库日期', + rules: [{ required: true, message: '请选择出库日期' }] + }, React.createElement(DatePicker, { style: { width: '100%' }, format: 'YYYY-MM-DD' })), + React.createElement(Form.Item, { name: 'remark', label: '备注' }, + React.createElement(Input.TextArea, { rows: 3, placeholder: '请输入备注', allowClear: true })) + ) + ), + + React.createElement(Card, { style: { marginBottom: 16 } }, + React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '16px 24px', alignItems: 'start' } }, React.createElement('div', { style: filterItemStyle }, React.createElement('div', { style: filterLabelStyle }, '仓库名称'), React.createElement(Select, { @@ -554,6 +1094,8 @@ const Component = function () { value: draft.vehicleModels, onChange: function (v) { setDraft(function (p) { return Object.assign({}, p, { vehicleModels: v || [] }); }); }, allowClear: true, + showSearch: true, + filterOption: filterOption, options: allVehicleModelOptions, maxTagCount: 'responsive' }) @@ -563,22 +1105,40 @@ const Component = function () { React.createElement(Button, { onClick: handleReset }, '重置'), React.createElement(Button, { type: 'primary', onClick: handleQuery }, '查询') ) - ) : null, + ), - React.createElement('style', null, tabsAlignStyle), - React.createElement(Tabs, { - className: 'spare-stock-main-tabs', - activeKey: mainTab, - onChange: setMainTab, - items: tabItems, - destroyInactiveTabPane: false, - tabBarExtraContent: mainTab === 'stock' - ? React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, - React.createElement(Button, { type: 'primary', onClick: handleAdd }, '新增物料信息'), - React.createElement(Button, { onClick: handleExport }, '导出') - ) - : null, - tabBarStyle: { marginBottom: 0 } - }) + React.createElement(Card, { + title: '列表', + bordered: false, + style: { borderRadius: 8 }, + extra: React.createElement(Space, { wrap: true }, + React.createElement(Button, { type: 'primary', onClick: handleAdd }, '新增备件信息'), + React.createElement(Button, { onClick: handleOpenInboundForm }, '入库'), + React.createElement(Button, { onClick: handleOpenOutboundForm }, '领用出库'), + React.createElement(Button, { onClick: handleDownloadImportTemplate }, '批量导入'), + React.createElement(Button, { onClick: handleExportList }, '导出') + ) + }, + React.createElement('style', null, tableStyle), + React.createElement('div', { className: 'spare-stock-table' }, + React.createElement(Table, { + rowKey: 'id', + columns: columns, + dataSource: paged, + scroll: { x: 1680 }, + size: 'small', + pagination: { + current: page, + pageSize: pageSize, + total: filtered.length, + showSizeChanger: true, + showQuickJumper: true, + pageSizeOptions: ['10', '20', '50', '100'], + showTotal: function (t) { return '共 ' + t + ' 条'; }, + onChange: function (pg, ps) { setPage(pg); if (ps) setPageSize(ps); } + } + }) + ) + ) ); };