From 99f5c058c42521640d815f3d52dc722a5d5ad4c4 Mon Sep 17 00:00:00 2001
From: skyouc
Date: 星期五, 23 五月 2025 18:59:43 +0800
Subject: [PATCH] Merge branch 'devlop' of http://47.97.1.152:5880/r/wms-master into devlop

---
 rsf-admin/src/page/work/stockTransfer/index.jsx                                          |   18 ++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocServiceImpl.java |   11 +
 rsf-admin/src/page/work/stockTransfer/stockTransferList.jsx                              |  379 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocController.java    |    9 +
 rsf-admin/src/page/ResourceContent.js                                                    |    3 
 5 files changed, 419 insertions(+), 1 deletions(-)

diff --git a/rsf-admin/src/page/ResourceContent.js b/rsf-admin/src/page/ResourceContent.js
index 64483a6..416d4cc 100644
--- a/rsf-admin/src/page/ResourceContent.js
+++ b/rsf-admin/src/page/ResourceContent.js
@@ -51,6 +51,7 @@
 import basContainer from './basicInfo/basContainer';
 import outBound from "./work/outBound";
 import checkOutBound from "./work/checkOutBound";
+import stockTransfer from "./work/stockTransfer";
 
 const ResourceContent = (node) => {
     switch (node.component) {
@@ -148,6 +149,8 @@
             return outBound;
         case 'checkOutBound':
             return checkOutBound;
+        case 'stockTransfer':
+            return stockTransfer;
         default:
             return {
                 list: ListGuesser,
diff --git a/rsf-admin/src/page/work/stockTransfer/index.jsx b/rsf-admin/src/page/work/stockTransfer/index.jsx
new file mode 100644
index 0000000..caa0bd1
--- /dev/null
+++ b/rsf-admin/src/page/work/stockTransfer/index.jsx
@@ -0,0 +1,18 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    ListGuesser,
+    EditGuesser,
+    ShowGuesser,
+} from "react-admin";
+
+
+import StockTransferList from "./stockTransferList";
+
+export default {
+    list: StockTransferList,
+    edit: EditGuesser,
+    show: ShowGuesser,
+    recordRepresentation: (record) => {
+        return `${record.id}`
+    }
+};
diff --git a/rsf-admin/src/page/work/stockTransfer/stockTransferList.jsx b/rsf-admin/src/page/work/stockTransfer/stockTransferList.jsx
new file mode 100644
index 0000000..a0355b8
--- /dev/null
+++ b/rsf-admin/src/page/work/stockTransfer/stockTransferList.jsx
@@ -0,0 +1,379 @@
+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,
+    useRedirect,
+} 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,
+    Autocomplete,
+} 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 "../components/locItemInfoModal";
+import { Delete } from '@mui/icons-material';
+import _, { set } from 'lodash';
+import StaSelect from "../components/StaSelect";
+import { redirect } from "react-router";
+import { number } from "prop-types";
+
+const StockTransferList = () => {
+
+    const [createDialog, setCreateDialog] = useState(false);
+    const [tabelData, setTableData] = useState([]);
+    const [selectedRows, setSelectedRows] = useState([]);
+    const [sta, setSta] = useState("");
+    const notify = useNotify();
+    const tableRef = useRef();
+    tableRef.current = useGridApiRef();
+    const translate = useTranslate();
+
+    const [orgLoc, setOrgLoc] = useState([]);
+    const [tarLoc, setTarLoc] = useState([]);
+    const [tarLocList, setTarLocList] = useState([]);
+
+    useEffect(() => {
+        selectAreaNoUse();
+    },[orgLoc])
+
+    const selectAreaNoUse = async() =>{
+        const {
+            data: { code, data, msg },
+        } = await request.post("/loc/areaNoUse/list",{
+
+        });
+        if (code === 200) {
+            const newData = data.map((item) => {
+                return {
+                    label: item,
+                    id: item
+                }
+                
+            })  
+            console.log(newData);
+            setTarLocList(newData);         
+        }
+
+    }
+
+
+    const handleDeleteItem = () => {
+        const newTableData = _.filter(tabelData, (item) => !selectedRows.includes(item.matnrId));
+        setTableData(newTableData);
+    }
+
+    // 娣诲姞涓�涓鐞嗘柊鏁版嵁鐨勫嚱鏁帮紝璁剧疆outQty榛樿鍊�
+    const handleSetData = (newData) => {
+        // 涓烘柊娣诲姞鐨勬暟鎹缃畂utQty榛樿鍊间负anfme鐨勫��
+        const dataWithDefaultQty = newData.map(item => ({
+            ...item,
+            outQty: item.outQty || item.anfme // 濡傛灉outQty宸插瓨鍦ㄥ垯淇濈暀锛屽惁鍒欎娇鐢╝nfme鐨勫��
+        }));
+        setTableData([...tabelData, ...dataWithDefaultQty]);
+    };
+
+
+
+    return (
+        <>            
+            <Card sx={{ p: 2, mb: 2, mt: 2 }}>
+                <Form>
+                    <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.stockTransfer.inputLoc')}
+                                </Typography>
+                                <Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 1 }}>
+                                    <Stack direction='row' spacing={2} minWidth={200}>
+                                        <TextField
+                                            label={translate("table.field.stockTransfer.orgLoc")}                                            
+                                            onChange={(event) => setOrgLoc(event.target.value)}
+                                        />
+                                    </Stack>
+                                    <>{"鈥斺��"}</>
+                                    <Stack direction='row' spacing={2} minWidth={200}>
+                                        <Autocomplete
+                                            disablePortal
+                                            options={tarLocList}
+                                            renderInput={(params) => (
+                                                <TextField {...params} label={translate("table.field.stockTransfer.tarLoc")} />
+                                            )}
+                                            onChange={(event, value) => setTarLoc(value)}
+                                        />
+                                    </Stack>
+                                </Box>
+
+                                <Stack direction='row' spacing={2} minWidth={200}>
+                                    <SubmitButton
+                                        orgLoc={orgLoc}
+                                        tarLoc={tarLoc}                                        
+                                    />
+                                </Stack>
+                            </Box>
+                        </Grid>
+                    </Grid>
+                </Form>
+            </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={handleSetData}
+            />
+
+        </>
+    )
+}
+
+export default StockTransferList;
+
+const SubmitButton = (props) => {
+    const translate = useTranslate();
+    const notify = useNotify();
+    const redirect = useRedirect();
+    const refresh = useRefresh();
+    const { orgLoc, tarLoc } = props;
+    const check = () => {
+        console.log(orgLoc, tarLoc);
+        if (orgLoc === "" || orgLoc === undefined || orgLoc === null) {
+            notify("璇烽�夋嫨绔欑偣");
+            return;
+        }
+        if (tarLoc === "" || tarLoc === undefined || tarLoc === null) {
+            notify("璇烽�夋嫨绔欑偣");
+            return;
+        }        
+        
+    }
+    const http = async (sta, items) => {
+        console.log(items);
+
+        const filter = items.filter(item => (item.outQty + item.workQty) > item.anfme);
+        if (filter.length > 0) {
+            notify(translate('toolbar.request.error.out_stock_qty'))
+            return
+        }
+        const { data: { code, data, msg } } = await request.post(`/locItem/generate/task`, { siteNo: sta, items: items });
+        if (code === 200) {
+            notify(msg);
+            refresh()
+            setTableData([])
+            redirect("/task")
+        } else {
+            notify(msg);
+        }
+    }
+    return (
+        <ConfirmButton
+            variant="contained"
+            color="primary"
+            onConfirm={check}
+            label={"table.field.outBound.createTask"}
+        >
+        </ConfirmButton>
+    )
+
+}
+
+const ModalTable = ({ tabelData, setTableData, selectedRows, setSelectedRows, tableRef }) => {
+    const translate = useTranslate();
+    const notify = useNotify();
+
+    const [columns, setColumns] = useState([
+        {
+            field: 'locCode',
+            headerName: translate('table.field.locItem.locCode'),
+            width: 100,
+            editable: false,
+        },
+        {
+            field: 'anfme',
+            headerName: translate('table.field.locItem.anfme'),
+            type: 'number',
+            width: 100,
+            editable: false,
+        },
+        {
+            field: 'workQty',
+            headerName: translate('table.field.locItem.workQty'),
+            width: 100,
+            type: 'number',
+            editable: false,
+        },
+        {
+            field: 'locCode',
+            headerName: translate('table.field.locItem.locCode'),
+            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.id !== row.id);
+        setTableData(newData);
+    };
+
+
+    const processRowUpdate = (newRow, oldRow) => {
+        const rows = tabelData.map((r) =>
+            r.id === newRow.id ? { ...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-server/src/main/java/com/vincent/rsf/server/manager/controller/LocController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocController.java
index d5022a8..7b981c9 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocController.java
@@ -30,6 +30,7 @@
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import java.util.*;
+import java.util.stream.Collectors;
 
 
 @Api(tags = "搴撲綅淇℃伅")
@@ -58,6 +59,14 @@
     }
 
     @PreAuthorize("hasAuthority('manager:loc:list')")
+    @PostMapping("/loc/areaNoUse/list")
+    public R areaNoUselist(@RequestBody Map<String, Object> map) {
+        List<Loc> list = locService.list();
+        List<String> list1 = list.stream().map(obj -> obj.getCode()).collect(Collectors.toList());
+        return R.ok(list1);
+    }
+
+    @PreAuthorize("hasAuthority('manager:loc:list')")
     @PostMapping({"/loc/many/{ids}", "/locs/many/{ids}"})
     public R many(@PathVariable Long[] ids) {
         return R.ok().add(locService.listByIds(Arrays.asList(ids)));
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocServiceImpl.java
index 259612b..c2fdab5 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocServiceImpl.java
@@ -93,12 +93,21 @@
         }
         String join = StringUtils.join(param.getTypeIds(), ",");
 
+        String[] split = warehouseAreas.getCode().split("\\.");
+        if (split.length == 0){
+            throw new CoolException("搴撳尯缂栫爜閿欒锛侊紒");
+        }
+        StringBuilder locStar = new StringBuilder();
+        for (int i = 0; i < split.length; i++) {
+            locStar.append(split[i]).append("-");
+        }
+
         List<Loc> list = new ArrayList<>();
         for (int r = param.getStartRow(); r <= param.getEndRow(); r++) {
             for (int b = param.getStartBay(); b <= param.getEndBay(); b++) {
                 for (int l = param.getStartLev(); l <= param.getEndLev(); l++) {
                     // 鑾峰彇搴撲綅鍙�
-                    String locNo = String.format("%02d", r) + String.format("%03d", b) + String.format("%02d", l);
+                    String locNo = locStar +  String.format("%d", r) + String.format("-%d", b) + String.format("-%d", l);
                     Loc loc = new Loc();
                     loc.setCode(locNo)
                             .setUseStatus("O")

--
Gitblit v1.9.1