From f6881a3c81210316d56751f612346439c3ab814c Mon Sep 17 00:00:00 2001 From: zjj <3272660260@qq.com> Date: 星期三, 21 五月 2025 11:06:57 +0800 Subject: [PATCH] #出库作业 --- rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx | 233 +++++++++++++++++++++++ rsf-admin/src/page/outWork/outBound/OutBoundList.jsx | 277 +++++++++++++++++++++++++++ rsf-admin/src/i18n/zh.js | 10 + rsf-admin/src/i18n/en.js | 4 rsf-admin/src/page/ResourceContent.js | 3 rsf-admin/src/page/outWork/outBound/index.jsx | 18 + 6 files changed, 545 insertions(+), 0 deletions(-) diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js index 858bcf1..53ea47c 100644 --- a/rsf-admin/src/i18n/en.js +++ b/rsf-admin/src/i18n/en.js @@ -207,6 +207,10 @@ }, table: { field: { + outBound: { + stockWithdrawal: 'StockWithdrawal', + withdrawal:'Withdrawal' + }, basContainer: { containerType:'containerType', codeType: 'codeType', diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js index 9b0169f..6da2272 100644 --- a/rsf-admin/src/i18n/zh.js +++ b/rsf-admin/src/i18n/zh.js @@ -1,4 +1,5 @@ import basContainer from "../page/basicInfo/basContainer"; +import outBound from "../page/outWork/outBound"; import chineseMessages from "./core/chineseMessages"; const customChineseMessages = { @@ -207,9 +208,18 @@ wave: '娉㈡绠$悊', basStation: '绔欑偣淇℃伅', basContainer: '瀹瑰櫒绠$悊', + outBound: '鍑哄簱浣滀笟', }, table: { field: { + outBound: { + stockWithdrawal: '鎻愬彇搴撳瓨', + withdrawal:'鎻愬彇', + outSta: '鍑哄簱绔�', + outQty: '鍑哄簱鏁伴噺', + anfme: '鏁伴噺', + + }, basContainer: { containerType:'瀹瑰櫒绫诲瀷', codeType: '鏉$爜绫诲瀷', diff --git a/rsf-admin/src/page/ResourceContent.js b/rsf-admin/src/page/ResourceContent.js index 3f3cbb4..9636298 100644 --- a/rsf-admin/src/page/ResourceContent.js +++ b/rsf-admin/src/page/ResourceContent.js @@ -49,6 +49,7 @@ import basStation from './basicInfo/basStation'; import warehouseStock from './statistics/stockManage'; import basContainer from './basicInfo/basContainer'; +import outBound from "./outWork/outBound"; const ResourceContent = (node) => { switch (node.component) { @@ -142,6 +143,8 @@ return basStation; case 'basContainer': return basContainer; + case 'outBound': + return outBound; default: return { list: ListGuesser, diff --git a/rsf-admin/src/page/outWork/outBound/OutBoundList.jsx b/rsf-admin/src/page/outWork/outBound/OutBoundList.jsx new file mode 100644 index 0000000..6afcfcd --- /dev/null +++ b/rsf-admin/src/page/outWork/outBound/OutBoundList.jsx @@ -0,0 +1,277 @@ +import React, { useState, useRef, useEffect, useMemo } from "react"; +import { useWatch, useFormContext } from "react-hook-form"; +import { + CreateBase, + useTranslate, + TextInput, + NumberInput, + BooleanInput, + DateInput, + SaveButton, + SelectInput, + ReferenceInput, + ReferenceArrayInput, + AutocompleteInput, + Toolbar, + required, + useDataProvider, + useNotify, + Form, + useCreateController, + useListContext, + useRefresh, + Edit, +} from 'react-admin'; +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Stack, + Grid, + TextField, + Box, + Button, + Paper, + TableContainer, + Table, + TableHead, + TableBody, + TableRow, + TableCell, + Tooltip, + IconButton, + styled, + Select, + MenuItem, + Typography, + Card, +} from '@mui/material'; +import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting'; +import ConfirmButton from "../../components/ConfirmButton"; +import TreeSelectInput from "@/page/components/TreeSelectInput"; +import { DataGrid, useGridApiRef } from '@mui/x-data-grid'; +import DictSelect from "../../components/DictSelect"; +import AddIcon from '@mui/icons-material/Add'; +import DeleteIcon from '@mui/icons-material/Delete'; +import request from '@/utils/request'; +import LocItemInfoModal from "./locItemInfoModal"; +import { Delete } from '@mui/icons-material'; + +const OutBoundList = () => { + + const [createDialog, setCreateDialog] = useState(false); + const [tabelData, setTableData] = useState([]); + const [selectedRows, setSelectedRows] = useState([]); + const tableRef = useRef(); + tableRef.current = useGridApiRef(); + const translate = useTranslate(); + + const handleDeleteItem = () => { + const newTableData = _.filter(tabelData, (item) => !selectedRows.includes(item.matnrId)); + setTableData(newTableData); + } + return ( + <> + <Card sx={{ p: 2, mb: 2, mt:2}}> + <Grid container spacing={2}> + <Grid item xs={12}> + <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1 }}> + <Typography variant="h6"> + {translate('table.field.outBound.stockWithdrawal')} + </Typography> + <Stack direction='row' spacing={2}> + <Button + variant="contained" + color="primary" + startIcon={<AddIcon />} + onClick={() => setCreateDialog(true)} + > + {translate('table.field.outBound.withdrawal')} + </Button> + <ConfirmButton + label={'鍒犻櫎'} + variant="outlined" + color="error" + onConfirm={handleDeleteItem} + /> + </Stack> + </Box> + </Grid> + </Grid> + </Card> + <Card sx={{ p: 2, mb: 2, mt:2}}> + <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}> + <Grid container spacing={2}> + <Grid item md={2}> + <Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 1 }}> + <Typography width={100} vvariant="h6" gutterBottom> + {translate('table.field.outBound.outSta')} + </Typography> + <DictSelect + label={translate("table.field.outStock.wkType")} + variant="filled" + group='2' + dictTypeCode="sys_business_type" + required + /> + </Box> + </Grid> + </Grid> + </Box> + </Card> + <Card sx={{ mb: 2}}> + <Box sx={{ }}> + <ModalTable tabelData={tabelData} setTableData={setTableData} selectedRows={selectedRows} setSelectedRows={setSelectedRows} tableRef={tableRef}></ModalTable> + </Box> + </Card> + <LocItemInfoModal + open={createDialog} + setOpen={setCreateDialog} + data={tabelData} + setData={setTableData} + /> + + </> + ) +} + +export default OutBoundList; + + +const ModalTable = ({ tabelData, setTableData, selectedRows, setSelectedRows, tableRef }) => { + const translate = useTranslate(); + const notify = useNotify(); + + const [columns, setColumns] = useState([ + { + field: 'outQty', + headerName: translate('table.field.outBound.outQty'), + width: 100, + editable: true, + }, + { + field: 'anfme', + headerName: translate('table.field.locItem.anfme'), + width: 100, + editable: false, + }, + { + field: 'matnrCode', + headerName: translate('table.field.locItem.matnrCode'), + width: 130, + editable: false, + }, + { + field: 'maktx', + headerName: translate('table.field.locItem.maktx'), + width: 250, + editable: false, + }, + { + field: 'batch', + headerName: translate('table.field.locItem.batch'), + width: 250, + editable: false, + }, + ]) + + const action = { + field: 'action', + headerName: '鎿嶄綔', + width: 70, + lockPosition: 'left', + renderCell: (params) => ( + <Tooltip title="Delete"> + <IconButton onClick={() => handleDelete(params.row)}> + <Delete /> + </IconButton> + </Tooltip> + ), + + } + + let cdata = useRef([]); + + useEffect(() => { + getDynamicFields(); + }, []); + + useEffect(() => { + cdata.current = tabelData + }, [tabelData]); + + + const getDynamicFields = async () => { + const { + data: { code, data, msg }, + } = await request.get("/fields/enable/list"); + if (code === 200) { + const cols = data.map(el => ({ + field: el.fields, + headerName: el.fieldsAlise, + minWidth: 100, + flex: 1, + editable: false + })) + setColumns([...columns, ...cols, action]) + } else { + notify(msg); + } + } + + + const handleDelete = (row) => { + const newData = _.filter(cdata.current, (item) => item.matnrId !== row.matnrId); + setTableData(newData); + }; + + + const processRowUpdate = (newRow, oldRow) => { + const rows = tabelData.map((r) => + r.matnrId === newRow.matnrId ? { ...newRow } : r + ) + setTableData(rows) + return newRow; + }; + + const handleSelectionChange = (ids) => { + setSelectedRows(ids) + }; + + tableRef.current = useGridApiRef(); + + return ( + <div style={{ height: 500, width: '100%' }}> + <DataGrid + apiRef={tableRef} + rows={tabelData} + columns={columns} + disableRowSelectionOnClick + getRowId={(row) => row.matnrId ? row.matnrId : row.id} + disableColumnFilter + disableColumnSelector + disableColumnSorting + disableMultipleColumnsSorting + processRowUpdate={processRowUpdate} + initialState={{ + pagination: { + paginationModel: { + pageSize: 25, + }, + }, + }} + pageSizeOptions={[10, 25, 50, 100]} + editMode="row" + checkboxSelection + onRowSelectionModelChange={handleSelectionChange} + selectionModel={selectedRows} + sx={{ + '& .MuiDataGrid-cell input': { + border: '1px solid #ccc' + }, + }} + /> + </div> + ); +}; diff --git a/rsf-admin/src/page/outWork/outBound/index.jsx b/rsf-admin/src/page/outWork/outBound/index.jsx new file mode 100644 index 0000000..b3fa3be --- /dev/null +++ b/rsf-admin/src/page/outWork/outBound/index.jsx @@ -0,0 +1,18 @@ +import React, { useState, useRef, useEffect, useMemo } from "react"; +import { + ListGuesser, + EditGuesser, + ShowGuesser, +} from "react-admin"; + +import OutBoundList from "./OutBoundList"; + + +export default { + list: OutBoundList, + edit: EditGuesser, + show: ShowGuesser, + recordRepresentation: (record) => { + return `${record.id}` + } +}; diff --git a/rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx b/rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx new file mode 100644 index 0000000..6e3d3bc --- /dev/null +++ b/rsf-admin/src/page/outWork/outBound/locItemInfoModal.jsx @@ -0,0 +1,233 @@ +import React, { useState, useEffect } from "react"; +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Stack, + Grid, + TextField, + Box, + Button, + Paper, + styled +} from '@mui/material'; +import DialogCloseButton from "../../components/DialogCloseButton"; +import { useTranslate, useNotify, useRefresh } from 'react-admin'; +import request from '@/utils/request'; +import { DataGrid } from '@mui/x-data-grid'; +import SaveIcon from '@mui/icons-material/Save'; +import TreeSelectInput from "@/page/components/TreeSelectInput"; +const LocItemInfoModal = (props) => { + const { open, setOpen, data, setData } = props; + + const translate = useTranslate(); + const notify = useNotify(); + const refresh = useRefresh(); + + const handleClose = (event, reason) => { + if (reason !== "backdropClick") { + setOpen(false); + } + }; + + const [formData, setFormData] = useState({}); + const [tableData, setTableData] = useState([]); + const [dyFields, setDyFields] = useState([]); + const [selectedRows, setSelectedRows] = useState([]); + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData(() => ({ + [name]: value + })); + }; + + const reset = () => { + setFormData({ + name: '', + code: '', + groupId: 0 + }) + } + + const handleSubmit = () => { + const hasarr = data.map(el => +el.matnrId) + const selectedData = selectedRows.filter(item => !hasarr.includes(item)).map(id => (tableData.find(row => row.id === id))); + const value = selectedData.map((el => { + const dynamicFields = dyFields.reduce((acc, item) => { + acc[item.fields] = el['extendFields']?.[item.fields] || ''; + return acc; + }, {}); + return { + matnrId: el.id, + maktx: el.name, + matnrCode: el.code, + stockUnit: el.stockUnit || '', + purUnit: el.purchaseUnit || '', + ...dynamicFields + } + })) + setData([...data, ...value]); + setOpen(false); + reset(); + }; + + const getData = async () => { + const res = await request.post(`/locItem/page`, { + ...formData, + current: 1, + pageSize: 100, + orderBy: "create_time desc" + }); + if (res?.data?.code === 200) { + setTableData(res.data.data.records); + } else { + notify(res.data.msg); + } + }; + + useEffect(() => { + getData(); + }, [open]); + + const handleSearch = () => { + getData() + }; + + return ( + <Dialog + open={open} + onClose={handleClose} + aria-labelledby="form-dialog-title" + fullWidth + disableRestoreFocus + maxWidth="lg" + > + <DialogTitle id="form-dialog-title" sx={{ + position: 'sticky', + top: 0, + backgroundColor: 'background.paper', + zIndex: 1000 + }}> + {translate("common.action.newAddMats")} + <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}> + <DialogCloseButton onClose={handleClose} /> + </Box> + </DialogTitle> + <DialogContent sx={{ mt: 2 }}> + <Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}> + <Grid container spacing={2}> + <Grid item md={4}> + <TextField + label={translate('table.field.matnr.name')} + name="name" + value={formData.name} + onChange={handleChange} + size="small" + /> + </Grid> + <Grid item md={4}> + <TextField + label={translate('table.field.matnr.code')} + name="code" + value={formData.code} + onChange={handleChange} + size="small" + /> + </Grid> + </Grid> + </Box> + <Box sx={{ mt: 2 }}> + <Stack direction="row" spacing={2}> + <Button variant="contained" onClick={handleSearch}>鎼滅储</Button> + </Stack> + </Box> + <Box sx={{ mt: 2, height: 400, width: '100%' }}> + <AsnWareModalTable + tableData={tableData} + setTableData={setTableData} + dyFields={dyFields} + setDyFields={setDyFields} + selectedRows={selectedRows} + setSelectedRows={setSelectedRows} + /> + </Box> + </DialogContent> + <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}> + <Box sx={{ width: '100%', display: 'flex', justifyContent: 'space-between' }}> + <Button onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}> + {translate('toolbar.confirm')} + </Button> + </Box> + </DialogActions> + </Dialog> + ); +}; + +export default LocItemInfoModal; + +const AsnWareModalTable = ({ tableData, setTableData, selectedRows, setSelectedRows, dyFields, setDyFields }) => { + const translate = useTranslate(); + const notify = useNotify(); + + const [columns, setColumns] = useState([ + // { field: 'id', headerName: 'ID', width: 100 }, + { field: 'locCode', headerName: translate('table.field.locItem.locCode'), width: 100 }, + { field: 'matnrCode', headerName: translate('table.field.locItem.matnrCode'), width: 200 }, + { field: 'maktx', headerName: translate('table.field.locItem.maktx'), width: 300 }, + { field: 'batch', headerName: translate('table.field.locItem.batch'), width: 100 }, + { field: 'anfme', headerName: translate('table.field.locItem.anfme'), width: 100 }, + + { field: 'unit', headerName: translate('table.field.locItem.unit'), width: 100 }, + + ]) + + + const handleSelectionChange = (ids) => { + setSelectedRows(ids) + + }; + + useEffect(() => { + getDynamicFields(); + }, []); + + const getDynamicFields = async () => { + const { + data: { code, data, msg }, + } = await request.get("/fields/enable/list"); + if (code === 200) { + const cols = data.map(el => ({ + field: el.fields, + headerName: el.fieldsAlise, + minWidth: 100, + flex: 1, + editable: el.unique, + valueGetter: (value, row) => { + return row.extendFields?.[el.fields] || ''; + }, + })) + setDyFields(data) + setColumns([...columns, ...cols]) + } else { + notify(msg); + } + } + + return ( + <div style={{ height: 400, width: '100%' }}> + <DataGrid + size="small" + rows={tableData} + columns={columns} + checkboxSelection + onRowSelectionModelChange={handleSelectionChange} + selectionModel={selectedRows} + disableColumnMenu={true} + disableColumnSorting + disableMultipleColumnsSorting + /> + </div> + ); +}; \ No newline at end of file -- Gitblit v1.9.1