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