skyouc
2025-03-18 031e36213941ad3430da2b0429701e742f09f5d8
Merge branch 'front' into devlop

# Conflicts:
# rsf-admin/.env
16个文件已修改
6个文件已添加
907 ■■■■ 已修改文件
rsf-admin/.env 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/en.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/zh.js 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/asnOrder/AsnOrderList.jsx 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/asnOrder/AsnOrderModal.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/loc/InitButton.jsx 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/loc/InitModal.jsx 241 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/loc/LocCreate.jsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/loc/LocEdit.jsx 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/loc/LocList.jsx 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/matnr/MatnrCreate.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/matnr/MatnrList.jsx 115 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/matnr/MatnrListAside.jsx 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/matnrGroup/MatnrGroupList.jsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/whMat/WhMatList.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/whMat/whMatCreate.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/components/BatchButton.jsx 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/components/BatchModal.jsx 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/components/DictionarySelect.jsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/components/TooltipField.jsx 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreasItem/WarehouseAreasItemList.jsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/.env
@@ -1,3 +1,3 @@
VITE_BASE_IP=localhost
# VITE_BASE_IP=47.76.147.249
# VITE_BASE_IP=192.168.4.24
VITE_BASE_IP=47.76.147.249
VITE_BASE_PORT=8080
rsf-admin/src/i18n/en.js
@@ -68,6 +68,7 @@
            deprecate: 'Deprecate',
            resend: 'RESEND',
            selected: 'selected',
            batch: 'batch'
        },
        msg: {
            confirm: {
@@ -143,6 +144,13 @@
        fields: 'Extend Fields',
        fieldsItem: 'Extend Fields Items',
        warehouseAreasItem: 'WarehouseAreasItem',
        endBay: "endBay",
        endLev: "endLev",
        endRow: "endRow",
        locType: "locType",
        startBay: "startBay",
        startLev: "startLev",
        startRow: "startRow",
    },
    table: {
        field: {
@@ -334,6 +342,9 @@
                barcode: "Barcode",
                unit: "Unit",
                size: "Size",
                length: "length",
                width: "width",
                height: "height",
                row: "Row",
                col: "Col",
                lev: "Lev",
@@ -596,6 +607,9 @@
        inspection: "Inspection",
        creatcode: "creatcode",
        print: "print",
        enable: 'enable',
        unenable: 'unenable',
        locInit: 'loc init'
    },
};
rsf-admin/src/i18n/zh.js
@@ -68,6 +68,7 @@
            deprecate: '废弃',
            resend: '重发',
            selected: '项选中',
            batch: '批量编辑'
        },
        msg: {
            confirm: {
@@ -121,7 +122,7 @@
        userLogin: '登录日志',
        customer: '客户表',
        shipper: '货主信息',
        matnr: '物料表',
        matnr: '物料数据',
        matnrGroup: '物料分组',
        warehouse: '仓库信息',
        warehouseAreas: '仓库库区',
@@ -280,7 +281,7 @@
            matnrGroup: {
                name: "名称",
                code: "编码",
                parentId: "父类标识",
                parentId: "上级标识",
            },
            warehouse: {
                name: "仓库名称",
@@ -326,13 +327,16 @@
                warehouseId: "仓库",
                areaId: "库区",
                code: "编码",
                type: "类型",
                type: "库位类型",
                name: "名称",
                flagLogic: "虚拟库位",
                fucAtrrs: "功能属性",
                barcode: "容器码",
                unit: "单位",
                size: "长/宽/高",
                length: "长",
                width: "宽",
                height: "高",
                row: "排",
                col: "列",
                lev: "层",
@@ -341,6 +345,13 @@
                maxPack: "最大包装数",
                flagLabelMange: "标签管理",
                locAttrs: "属性",
                endBay: "终止列",
                endLev: "终止层",
                endRow: "终止排",
                locType: "宽窄类型",
                startBay: "起始列",
                startLev: "起始层",
                startRow: "起始排",
            },
            container: {
@@ -598,6 +609,9 @@
        inspection: "报检",
        creatcode: "生成条码",
        print: "打印",
        enable: '启用',
        unenable: '禁用',
        locInit: ' 库位初始化'
    },
};
rsf-admin/src/page/asnOrder/AsnOrderList.jsx
@@ -109,7 +109,32 @@
  const inspection = () => { };
  const print = () => { };
  const print = () => {
    const imageUrls = ['https://www.baidu.com/img/flexible/logo/pc/result@2.png',]
    const iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    document.body.appendChild(iframe);
    const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
    iframeDoc.open();
    iframeDoc.write('<html><head><title>Print Images</title></head><body>');
    // 将图片插入到 iframe 中
    imageUrls.forEach((imageUrl) => {
      iframeDoc.write(`<img src="${imageUrl}" style="margin: 10px;">`);
    });
    iframeDoc.write('</body></html>');
    iframeDoc.close();
    // 等待图片加载完成后触发打印
    iframe.contentWindow.onload = () => {
      iframe.contentWindow.print();
      // 打印完成后移除 iframe
      document.body.removeChild(iframe);
    };
  };
  return (
@@ -151,14 +176,7 @@
          omit={['id', 'createTime', 'createBy', 'memo']}
        >
          <NumberField source="id" />
          <MyField
            source="code"
            label="table.field.asnOrder.code"
            onClick={(event, record, val) => {
              event.stopPropagation();
              assign(record);
            }}
          />
          <TextField source="code" label="table.field.asnOrder.code" />
          <TextField source="poCode" label="table.field.asnOrder.poCode" />
          <NumberField source="poId" label="table.field.asnOrder.poId" />
          <TextField source="type" label="table.field.asnOrder.type" />
rsf-admin/src/page/asnOrder/AsnOrderModal.jsx
@@ -183,7 +183,7 @@
                                        onBlur={requestSetHead}
                                        variant="outlined"
                                        size="small"
                                        validate={required()}
                                        required
                                    />
                                </Grid>
rsf-admin/src/page/basicInfo/loc/InitButton.jsx
New file
@@ -0,0 +1,74 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import {
    List,
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    EditButton,
    FilterButton,
    CreateButton,
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    useRecordContext,
    useTranslate,
    useNotify,
    useListContext,
    FunctionField,
    TextField,
    NumberField,
    DateField,
    BooleanField,
    ReferenceField,
    TextInput,
    DateTimeInput,
    DateInput,
    SelectInput,
    NumberInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
    useRefresh,
    Button
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import InitModal from "./InitModal";
import LocPanel from "./LocPanel";
import EmptyData from "../../components/EmptyData";
import MyCreateButton from "../../components/MyCreateButton";
import MyExportButton from '../../components/MyExportButton';
import BatchButton from '../../components/BatchButton';
import PageDrawer from "../../components/PageDrawer";
import MyField from "../../components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
import DashboardIcon from '@mui/icons-material/Dashboard';
import request from '@/utils/request';
import CabinIcon from '@mui/icons-material/Cabin';
const InitButton = () => {
    const record = useRecordContext();
    const notify = useNotify();
    const refresh = useRefresh();
    const [createDialog, setCreateDialog] = useState(false);
    // console.log(record)
    return (<>
        <Button onClick={() => setCreateDialog(true)} label={"toolbar.locInit"}>
            <CabinIcon />
        </Button>
        <InitModal
            open={createDialog}
            setOpen={setCreateDialog}
        />
    </>
    )
}
export default InitButton;
rsf-admin/src/page/basicInfo/loc/InitModal.jsx
New file
@@ -0,0 +1,241 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
    useListContext,
    useRefresh,
} from 'react-admin';
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Grid,
    TextField,
    Box,
    Button,
    Paper,
    TableContainer,
    Table,
    TableHead,
    TableBody,
    TableRow,
    TableCell,
    Tooltip,
    IconButton,
    styled
} from '@mui/material';
import DialogCloseButton from "../../components/DialogCloseButton";
import DictionarySelect from "../../components/DictionarySelect";
import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form";
import SaveIcon from '@mui/icons-material/Save';
import request from '@/utils/request';
import { Add, Edit, Delete } from '@mui/icons-material';
import _ from 'lodash';
import { DataGrid } from '@mui/x-data-grid';
const InitModal = ({ open, setOpen }) => {
    const refresh = useRefresh();
    const translate = useTranslate();
    const notify = useNotify();
    const [formData, setFormData] = useState({
        "areaId": undefined,
        "endBay": undefined,
        "endLev": undefined,
        "endRow": undefined,
        "locType": "",
        "startBay": undefined,
        "startLev": undefined,
        "startRow": undefined,
        "type": ""
    });
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleReset = (e) => {
        e.preventDefault();
    };
    const handleChange = (value, name) => {
        setFormData((prevData) => ({
            ...prevData,
            [name]: ['locType', 'type'].includes(name) ? value : +value
        }));
    };
    const handleSubmit = async () => {
        const res = await request.post(`/loc/init`, formData);
        if (res?.data?.code === 200) {
            setOpen(false);
            refresh();
        } else {
            notify(res.data.msg);
        }
    }
    return (
        <Dialog open={open} maxWidth="md" fullWidth>
            <DialogCloseButton onClose={handleClose} />
            <DialogTitle>{translate('toolbar.locInit')}</DialogTitle>
            <DialogContent sx={{ mt: 2 }}>
                <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
                    <Form>
                        <Grid container spacing={2}>
                            <Grid item xs={4}>
                                <ReferenceInput
                                    source="areaId"
                                    reference="warehouseAreas"
                                >
                                    <AutocompleteInput
                                        label="table.field.loc.areaId"
                                        optionText="name"
                                        onChange={(value) => handleChange(value, 'areaId')}
                                        value={formData.areaId}
                                        validate={[required()]}
                                        filterToQuery={(val) => ({ name: val })}
                                    />
                                </ReferenceInput>
                            </Grid>
                            <Grid item xs={4}>
                                <DictionarySelect
                                    label={translate("table.field.loc.locType")}
                                    name="locType"
                                    value={formData.locType}
                                    onChange={(e) => handleChange(e.target.value, 'locType')}
                                    size="small"
                                    validate={[required()]}
                                    dictTypeCode="sys_width_type"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <DictionarySelect
                                    label={translate("table.field.loc.type")}
                                    name="type"
                                    value={formData.type}
                                    onChange={(e) => handleChange(e.target.value, 'type')}
                                    size="small"
                                    validate={[required()]}
                                    dictTypeCode="sys_loc_type"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextField
                                    label={translate("table.field.loc.startBay")}
                                    name="startBay"
                                    value={formData.startBay}
                                    onChange={(e) => handleChange(e.target.value, 'startBay')}
                                    size="small"
                                    type="number"
                                    required
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextField
                                    label={translate("table.field.loc.startLev")}
                                    name="startLev"
                                    value={formData.startLev}
                                    onChange={(e) => handleChange(e.target.value, 'startLev')}
                                    size="small"
                                    type="number"
                                    required
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextField
                                    label={translate("table.field.loc.startRow")}
                                    name="startRow"
                                    value={formData.startRow}
                                    onChange={(e) => handleChange(e.target.value, 'startRow')}
                                    size="small"
                                    type="number"
                                    required
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextField
                                    label={translate("table.field.loc.endBay")}
                                    name="endBay"
                                    value={formData.endBay}
                                    onChange={(e) => handleChange(e.target.value, 'endBay')}
                                    size="small"
                                    type="number"
                                    required
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextField
                                    label={translate("table.field.loc.endLev")}
                                    name="endLev"
                                    value={formData.endLev}
                                    onChange={(e) => handleChange(e.target.value, 'endLev')}
                                    size="small"
                                    type="number"
                                    required
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextField
                                    label={translate("table.field.loc.endRow")}
                                    name="endRow"
                                    value={formData.endRow}
                                    onChange={(e) => handleChange(e.target.value, 'endRow')}
                                    size="small"
                                    type="number"
                                    required
                                />
                            </Grid>
                        </Grid>
                    </Form>
                </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 />}>
                        确认
                    </Button>
                </Box>
            </DialogActions>
        </Dialog>
    );
}
export default InitModal;
rsf-admin/src/page/basicInfo/loc/LocCreate.jsx
@@ -119,20 +119,27 @@
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.loc.type"
                                    <ReferenceInput
                                        source="type"
                                        parse={v => v}
                                        validate={required()}
                                    />
                                        reference="dictData"
                                        filter={{ dictTypeCode: 'sys_loc_type' }}
                                    >
                                        <AutocompleteInput
                                            label="table.field.loc.type"
                                            optionText="label"
                                            optionValue="value"
                                            parse={v => v}
                                            validate={[required()]}
                                        />
                                    </ReferenceInput>
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                {/* <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.loc.name"
                                        source="name"
                                        parse={v => v}
                                    />
                                </Grid>
                                </Grid> */}
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.loc.flagLogic"
@@ -153,24 +160,41 @@
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                {/* <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.loc.unit"
                                        source="unit"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                </Grid> */}
                                {/* <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.loc.size"
                                        source="size"
                                        parse={v => v}
                                    />
                                </Grid> */}
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.loc.length"
                                        source="length"
                                        validate={required()}
                                    />
                                    <NumberInput
                                        label="table.field.loc.width"
                                        source="width"
                                        validate={required()}
                                    />
                                    <NumberInput
                                        label="table.field.loc.height"
                                        source="height"
                                        validate={required()}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.loc.row"
                                        source="lrow"
                                        source="row"
                                        validate={required()}
                                    />
                                    <NumberInput
rsf-admin/src/page/basicInfo/loc/LocEdit.jsx
@@ -66,7 +66,7 @@
                        <Grid container rowSpacing={2} columnSpacing={2}>
                            <Grid item xs={6} display="flex" gap={1}>
                                <ReferenceInput
                                    source="warehouseId$"
                                    source="warehouseId"
                                    reference="warehouse"
                                >
                                    <AutocompleteInput
@@ -99,12 +99,19 @@
                                />
                            </Grid>
                            <Grid item xs={6} display="flex" gap={1}>
                                <TextInput
                                    label="table.field.loc.type"
                                <ReferenceInput
                                    source="type"
                                    parse={v => v}
                                    validate={required()}
                                />
                                    reference="dictData"
                                    filter={{ dictTypeCode: 'sys_loc_type' }}
                                >
                                    <AutocompleteInput
                                        label="table.field.loc.type"
                                        optionText="label"
                                        optionValue="value"
                                        parse={v => v}
                                        validate={[required()]}
                                    />
                                </ReferenceInput>
                            </Grid>
                            <Grid item xs={6} display="flex" gap={1}>
                                <TextInput
@@ -141,10 +148,24 @@
                                />
                            </Grid>
                            <Grid item xs={6} display="flex" gap={1}>
                                <TextInput
                                    label="table.field.loc.size"
                                    source="size"
                                    parse={v => v}
                                <NumberInput
                                    label="table.field.loc.length"
                                    source="length"
                                    validate={required()}
                                />
                            </Grid>
                            <Grid item xs={6} display="flex" gap={1}>
                                <NumberInput
                                    label="table.field.loc.width"
                                    source="width"
                                    validate={required()}
                                />
                            </Grid>
                            <Grid item xs={6} display="flex" gap={1}>
                                <NumberInput
                                    label="table.field.loc.height"
                                    source="height"
                                    validate={required()}
                                />
                            </Grid>
                            <Grid item xs={6} display="flex" gap={1}>
rsf-admin/src/page/basicInfo/loc/LocList.jsx
@@ -31,6 +31,8 @@
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
    useRefresh,
    Button
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
@@ -39,10 +41,13 @@
import EmptyData from "../../components/EmptyData";
import MyCreateButton from "../../components/MyCreateButton";
import MyExportButton from '../../components/MyExportButton';
import InitButton from './InitButton';
import PageDrawer from "../../components/PageDrawer";
import MyField from "../../components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
import DashboardIcon from '@mui/icons-material/Dashboard';
import request from '@/utils/request';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
@@ -60,8 +65,6 @@
const filters = [
    <SearchInput source="condition" alwaysOn />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <NumberField source="warehouseId$" label="table.field.loc.warehouseId" />,
    <NumberField source="areaId$" label="table.field.loc.areaId" />,
@@ -100,6 +103,7 @@
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
@@ -109,7 +113,7 @@
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                    marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.loc"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
@@ -118,6 +122,7 @@
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <InitButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='loc' />
                        <MyExportButton />
@@ -143,7 +148,9 @@
                    <TextField source="fucAtrrs" label="table.field.loc.fucAtrrs" />
                    <TextField source="barcode" label="table.field.loc.barcode" />
                    <TextField source="unit" label="table.field.loc.unit" />
                    <TextField source="size" label="table.field.loc.size" />
                    <TextField source="size" label="table.field.loc.length" />
                    <TextField source="size" label="table.field.loc.width" />
                    <TextField source="size" label="table.field.loc.height" />
                    <NumberField source="lrow" label="table.field.loc.row" />
                    <NumberField source="col" label="table.field.loc.col" />
                    <NumberField source="lev" label="table.field.loc.lev" />
@@ -164,7 +171,8 @@
                    <TextField source="memo" label="common.field.memo" sortable={false} />
                    <WrapperField cellClassName="opt" label="common.field.opt">
                        <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
                        <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
                        {/* <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} /> */}
                        <EnableButton />
                    </WrapperField>
                </StyledDatagrid>
            </List>
@@ -183,3 +191,29 @@
}
export default LocList;
const EnableButton = () => {
    const record = useRecordContext();
    const notify = useNotify();
    const refresh = useRefresh();
    const enable = async () => {
        const res = await request.post('/loc/update', {
            ...record,
            status: +!record.status
        });
        if (res?.data?.code === 200) {
            refresh()
        } else {
            notify(res.data.msg);
        }
    }
    return (
        record.status === 1 ?
            (<Button onClick={enable} label={"toolbar.unenable"}>
                <DashboardIcon />
            </Button>) : (<Button onClick={enable} label={"toolbar.enable"}>
                <DashboardIcon />
            </Button>)
    )
}
rsf-admin/src/page/basicInfo/matnr/MatnrCreate.jsx
@@ -94,7 +94,7 @@
    //     if (!values.shipperId) errors.shipperId = translate('form.matnr.shipper');
    //     if (!values.groupId) errors.groupId = translate('form.matnr.groupId');
    //     if (!values.code) errors.code = translate('form.matnr.code');
    //     return errors;
    // };
rsf-admin/src/page/basicInfo/matnr/MatnrList.jsx
@@ -31,8 +31,11 @@
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
    useRefresh,
    Button
} from 'react-admin';
import { Box, Typography, Card, Stack,LinearProgress } from '@mui/material';
import { Box, Typography, Card, Stack, LinearProgress, Tooltip } from '@mui/material';
import { styled } from '@mui/material/styles';
import MatnrCreate from "./MatnrCreate";
import MatnrPanel from "./MatnrPanel";
@@ -40,33 +43,52 @@
import MyCreateButton from "@/page/components/MyCreateButton";
import MyExportButton from '@/page/components/MyExportButton';
import PageDrawer from "@/page/components/PageDrawer";
import MyField from "@/page/components/MyField";
import TooltipField from "@/page/components/TooltipField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
import ImportButton from "../../components/ImportButton";
import MatListAside from './MatnrListAside';
import { display, height } from "@mui/system";
import DashboardIcon from '@mui/icons-material/Dashboard';
import request from '@/utils/request';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
        height: '.9em'
    },
    '& .RaDatagrid-row': {
        cursor: 'auto'
    },
    '& .RaList-content': {
        width: '200px'
    },
    '& .RaList-main': {
        minHeight: '80vh'
    },
    '& .column-name': {
    },
    '& .opt': {
        width: 200
    },
    '& .name': {
        width: '100px',
    },
    '& .name .MuiTypography-root': {
        whiteSpace: 'nowrap',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        display: 'block',
        width: '100px',
    }
}));
const filters = [
    <SearchInput source="condition" alwaysOn />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <TextInput source="name" label="table.field.matnr.name" />,
    <TextInput source="code" label="table.field.matnr.code" />,
    <TextInput source="name" label="table.field.matnr.name" alwaysOn />,
    <TextInput source="code" label="table.field.matnr.code" alwaysOn />,
    <ReferenceInput source="shipperId$" label="table.field.matnr.shipperId" reference="shipper">
        <AutocompleteInput label="table.field.matnr.shipperId" optionText="name" filterToQuery={(val) => ({ name: val })} />
    </ReferenceInput>,
@@ -90,14 +112,14 @@
    <SelectInput source="stockLeval" label="table.field.matnr.stockLevel"
        choices={[
            { id: 0, name: ' A' },
            { id:   1, name: ' B' },
            { id:   2, name: 'C' },
            { id: 1, name: ' B' },
            { id: 2, name: 'C' },
        ]}
    />,
    <SelectInput source="isLabelMange" label="table.field.matnr.isLabelMange"
        choices={[
            { id: 0, name: ' 否' },
            { id:  1, name: ' 是' },
            { id: 1, name: ' 是' },
        ]}
    />,
    <NumberInput source="safeQty" label="table.field.matnr.safeQty" />,
@@ -125,31 +147,33 @@
    const { isLoading } = useListContext();
    return (
        <Box sx={{ position: 'relative' }}>
                {isLoading && (
                    <LinearProgress
                        sx={{
                            height: "2px",
                            position: 'absolute',
                            top: 0,
                            left: 0,
                            right: 0,
                        }}
                    />
                )}
            <StyledDatagrid
        <Box sx={{ position: 'relative', minHeight: "40vh", }}>
            {isLoading && (
                <LinearProgress
                    sx={{
                        height: "2px",
                        position: 'absolute',
                        top: 0,
                        left: 0,
                        right: 0,
                    }}
                />
            )}
            <StyledDatagrid
                preferenceKey='matnr'
                bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                rowClick={(id, resource, record) => false}
                expand={() => <MatnrPanel />}
                expandSingle={true}
                omit={['id','shipperId','platCode','spec','model','weight','color','size','describle'
                    ,'nromNum','unit','purchaseUnit','stockUnit','stockLeval','isLabelMange','safeQty'
                    ,'minQty','maxQty','stagn','valid','validWarn','flagCheck','updateTime', 'updateBy'
                    , 'createTime', 'createBy', 'memo']}
                omit={['id', 'shipperId', 'platCode', 'spec', 'model', 'weight', 'color', 'size', 'describle'
                    , 'nromNum', 'unit', 'purchaseUnit', 'stockUnit', 'stockLeval', 'isLabelMange', 'safeQty'
                    , 'minQty', 'maxQty', 'stagn', 'valid', 'validWarn', 'flagCheck', 'updateTime', 'updateBy'
                    , 'createTime', 'createBy', 'memo', 'rglarId', 'groupId', 'stockLevel', 'isLabelMange']}
            >
                <NumberField source="id" />
                <TextField source="name" label="table.field.matnr.name" />
                <TooltipField source="name" label="table.field.matnr.name" cellClassName="name" />
                <TextField source="code" label="table.field.matnr.code" />
                <ReferenceField source="shipperId" label="table.field.matnr.shipperId" reference="shipper" link={false} sortable={false}>
                    <TextField source="name" />
@@ -193,12 +217,13 @@
                <TextField source="memo" label="common.field.memo" sortable={false} />
                <WrapperField cellClassName="opt" label="common.field.opt">
                    <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
                    <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
                    {/* <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} /> */}
                    <EnableButton />
                </WrapperField>
            </StyledDatagrid>
        </Box>
    );
}
const MatnrList = () => {
@@ -217,7 +242,7 @@
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                    marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.matnr"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
@@ -235,7 +260,7 @@
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
                aside={ <MatListAside /> }
                aside={<MatListAside />}
            >
                <MatnrListContent />
            </List>
@@ -255,4 +280,30 @@
MatnrList.Context = React.createContext()
export default MatnrList;
export default MatnrList;
const EnableButton = () => {
    const record = useRecordContext();
    const notify = useNotify();
    const refresh = useRefresh();
    const enable = async () => {
        const res = await request.post('/loc/update', {
            ...record,
            status: +!record.status
        });
        if (res?.data?.code === 200) {
            refresh()
        } else {
            notify(res.data.msg);
        }
    }
    return (
        record.status === 1 ?
            (<Button onClick={enable} label={"toolbar.unenable"}>
                <DashboardIcon />
            </Button>) : (<Button onClick={enable} label={"toolbar.enable"}>
                <DashboardIcon />
            </Button>)
    )
}
rsf-admin/src/page/basicInfo/matnr/MatnrListAside.jsx
@@ -11,14 +11,27 @@
import { RichTreeView } from "@mui/x-tree-view/RichTreeView";
import { TreeItem2 } from "@mui/x-tree-view/TreeItem2";
const MatListAside = () => {
    const theme = useTheme();
    const notify = useNotify();
    const { setFilters } = useListContext(); // 获取列表上下文
    const [selectedOption, setSelectedOption] = useState(null);
    const [treeData, setTreeData] = useState([]);
    const trees = []
    // 用于管理展开项的状态
    const [expandedItems, setExpandedItems] = useState([]);
    // 递归收集所有节点的 id
    const collectAllNodeIds = (nodes) => {
        let allIds = [];
        nodes.forEach((node) => {
            allIds.push(node.id.toString());
            if (node.children && Array.isArray(node.children)) {
                allIds = allIds.concat(collectAllNodeIds(node.children));
            }
        });
        return allIds;
    };
    const haveChildren = (item) => {
        // 如果 item 是一个数组,遍历数组中的每个元素
        if (Array.isArray(item)) {
@@ -40,13 +53,16 @@
        return item;
    };
    useEffect(() => {
        request.post('/matnrGroup/tree')
            .then(res => {
                if (res?.data?.code === 200) {
                    let data = res.data.data;
                    let items = haveChildren(data)
                    setTreeData(items)
                    let items = haveChildren(data);
                    setTreeData(items);
                    // 当树数据更新时,更新展开项状态
                    setExpandedItems(collectAllNodeIds(items));
                } else {
                    notify(res.data.msg);
                }
@@ -54,12 +70,7 @@
            .catch(error => {
                notify('Error fetching tree data');
            });
    }, []);
    const treeData1 = [
        {
@@ -68,8 +79,12 @@
            editable: true,
            children: [
                {
                    id: 'grid-community', label: '@mui/x-data-grid', editable: true, children: [
                        { id: 'grid-community22', label: '@mui/x-data-grid', editable: true },]
                    id: 'grid-community',
                    label: '@mui/x-data-grid',
                    editable: true,
                    children: [
                        { id: 'grid-community22', label: '@mui/x-data-grid', editable: true },
                    ],
                },
                { id: 'grid-pro', label: '@mui/x-data-grid-pro', editable: true },
                { id: 'grid-premium', label: '@mui/x-data-grid-premium', editable: true },
@@ -95,15 +110,16 @@
            children: [{ id: 'tree-view-community1', label: '@mui/x-tree-view' }],
        },
    ];
    const handleNodeSelect = (event, nodeId) => {
        console.log('Selected Node ID:', nodeId);
        setFilters({ groupId: nodeId });
        // 在这里可以根据 nodeId 更新主内容区域
    };
    const handleSearch = () => {
        console.log('Search Input:', selectedOption);
    };
    const CustomCheckbox = React.forwardRef(function CustomCheckbox(props, ref) {
        return <input type="checkbox" ref={ref} {...props} />;
@@ -121,33 +137,35 @@
        );
    });
    return (
        <Card
            sx={{
                display: { xs: 'none', md: 'block', },
                order: -1,
                mr: 2,
                mt: 8,
                alignSelf: 'flex-start',
                border: theme.palette.mode === 'light' && '1px solid #e0e0e3',
                width: 250
                width: 250,
                minWidth: 150,
                height: `calc(100% - 120px)`,
            }}
        >
            <CardContent>
                <SavedQueriesList icon={<BookmarkIcon />} />
                <FilterLiveSearch source="condition" />
                <RichTreeView
                    defaultExpandedItems={['grid', 'pickers']}
                    // 使用 expandedItems 控制展开状态
                    expandedItems={expandedItems}
                    // 处理展开项状态的变化
                    onExpandedItemsChange={(newExpandedItems) => setExpandedItems(newExpandedItems)}
                    expansionTrigger="iconContainer"
                    items={treeData}
                    slots={CustomTreeItem}
                    onItemClick={handleNodeSelect} // 监听节点点击事件
                />
            </CardContent>
        </Card>
    )
}
    );
};
export default MatListAside;
export default MatListAside;
rsf-admin/src/page/basicInfo/matnrGroup/MatnrGroupList.jsx
@@ -160,7 +160,7 @@
    const [createDialog, setCreateDialog] = React.useState(false);
    const [editRecord, setEditRecord] = React.useState(null);
    const [openNodes, setOpenNodes] = React.useState({});
    const [expandAll, setExpandAll] = React.useState(false);
    const [expandAll, setExpandAll] = React.useState(true);
    const http = async () => {
        const res = await request.post(RESOURCE + '/tree', {
@@ -226,6 +226,23 @@
            return newExpandAll;
        });
    };
    // 初始化 openNodes 以展开所有节点
    React.useEffect(() => {
        if (treeData) {
            const newOpenNodes = {};
            const updateOpenNodes = (nodes) => {
                nodes.forEach(node => {
                    newOpenNodes[node.id] = true;
                    if (node.children) {
                        updateOpenNodes(node.children);
                    }
                });
            };
            updateOpenNodes(treeData);
            setOpenNodes(newOpenNodes);
        }
    }, [treeData]);
    return (
        <div>
@@ -336,4 +353,4 @@
    );
}
export default MatnrGroupList;
export default MatnrGroupList;
rsf-admin/src/page/basicInfo/whMat/WhMatList.jsx
@@ -145,7 +145,7 @@
  const getMatnrList = async () => {
    const {
      data: { code, data, msg },
    } = await request.post("/matnr/list", {}).then();
    } = await request.post("/matnr/list", {});
    if (code === 200) {
      console.log(data);
    } else {
rsf-admin/src/page/basicInfo/whMat/whMatCreate.jsx
@@ -180,7 +180,7 @@
          <Checkbox
            key={row.id}
            checked={row.checked}
            // onChange={() => { row.checked = !row.checked; }}
          // onChange={() => { row.checked = !row.checked; }}
          />
        </StyledTableCell>
        {columns.map((column, idx) => {
@@ -626,7 +626,7 @@
          </TableBody>
        </Table>
      </TableContainer>
      <WhTable data={treeDatas} />
      {/* <WhTable data={treeDatas} /> */}
    </>
  );
};
rsf-admin/src/page/components/BatchButton.jsx
New file
@@ -0,0 +1,31 @@
import EditIcon from '@mui/icons-material/Edit';
import { useState } from 'react';
import { Button, useListContext } from 'react-admin';
import BatchModal from './BatchModal';
const ImportButton = (props) => {
    const [modalOpen, setModalOpen] = useState(false);
    const { selectedIds } = useListContext();
    console.log(selectedIds)
    const handleOpenModal = () => {
        setModalOpen(true);
    };
    const handleCloseModal = () => {
        setModalOpen(false);
    };
    return (
        <>
            <Button
                startIcon={<EditIcon />}
                label="common.action.batch"
                onClick={handleOpenModal}
            />
            <BatchModal open={modalOpen} onClose={handleCloseModal} {...props} />
        </>
    );
};
export default ImportButton;
rsf-admin/src/page/components/BatchModal.jsx
New file
@@ -0,0 +1,65 @@
import { useEffect, useState, createContext, useContext } from 'react';
import { Box, CircularProgress, Stack, Typography } from '@mui/material';
import Alert from '@mui/material/Alert';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import MuiLink from '@mui/material/Link';
import {
    Button,
    FileField,
    FileInput,
    Form,
    Toolbar,
    useRefresh,
    useTranslate,
    useNotify
} from 'react-admin';
import { Link } from 'react-router-dom';
import DialogCloseButton from './DialogCloseButton';
import { usePapaParse } from './usePapaParse';
import MatnrList from '../basicInfo/matnr/MatnrList';
import request from '@/utils/request'
import SaveIcon from '@mui/icons-material/Save';
const ImportModal = ({ open, onClose, importTemp, useCodeImport, onceBatch = 10, value, parmas = {} }) => {
    const refresh = useRefresh();
    const translate = useTranslate();
    const notify = useNotify();
    const handleClose = () => {
        onClose();
    };
    const handleReset = (e) => {
        e.preventDefault();
    };
    const handleSubmit = () => {
    };
    return (
        <Dialog open={open} maxWidth="md" fullWidth>
            <DialogCloseButton onClose={handleClose} />
            <DialogTitle>{translate('common.action.batch')}</DialogTitle>
            <DialogContent>
                <div>1</div>
            </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 />}>
                        确认
                    </Button>
                </Box>
            </DialogActions>
        </Dialog>
    );
}
export default ImportModal;
rsf-admin/src/page/components/DictionarySelect.jsx
New file
@@ -0,0 +1,45 @@
import EditIcon from '@mui/icons-material/Edit';
import { useState, useEffect } from 'react';
import {
    Button, useListContext, SelectInput,
    required,
    useTranslate, useNotify
} from 'react-admin';
import request from '@/utils/request';
const DictionarySelect = (props) => {
    const { dictTypeCode, name, ...parmas } = props;
    const { selectedIds } = useListContext();
    const translate = useTranslate();
    const notify = useNotify();
    const [list, setList] = useState([])
    useEffect(() => {
        http()
    }, [dictTypeCode]);
    const http = async () => {
        const res = await request.post('/dictData/page', { dictTypeCode });
        if (res?.data?.code === 200) {
            setList(res.data.data.records.map((item) => {
                return {
                    id: item.value,
                    name: item.value
                }
            }))
        } else {
            notify(res.data.msg);
        }
    }
    return (
        <SelectInput
            source={name}
            choices={list}
            {...parmas}
        />
    );
};
export default DictionarySelect;
rsf-admin/src/page/components/TooltipField.jsx
New file
@@ -0,0 +1,28 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import {
    useRecordContext,
} from 'react-admin';
import { Box, Typography, Card, Stack, Tooltip } from '@mui/material';
import { useTheme } from '@mui/material/styles';
const MyField = ({ source, onClick }) => {
    const record = useRecordContext();
    const theme = useTheme();
    return record ? (
        <Box
        >
            <Typography
                variant="body2"
            >
                <Tooltip title={record[source]} placement="top">
                    <span>{record[source]}</span>
                </Tooltip>
            </Typography >
        </Box>
    ) : null;
}
export default MyField;
rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx
@@ -60,8 +60,6 @@
const filters = [
    <SearchInput source="condition" alwaysOn />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <TextInput source="uuid" label="table.field.warehouseAreas.uuid" />,
    <TextInput source="name" label="table.field.warehouseAreas.name" />,
@@ -73,19 +71,19 @@
    <SelectInput source="flagMinus" label="table.field.warehouseAreas.flagMinus"
        choices={[
            { id: 0, name: '否' },
            { id:  1, name: '是' },
            { id: 1, name: '是' },
        ]}
    />,
    <SelectInput source="flagLabelMange" label="table.field.warehouseAreas.flagLabelMange"
        choices={[
            { id: 0, name: ' 否' },
            { id:  1, name: ' 是' },
            { id: 1, name: ' 是' },
        ]}
    />,
    <SelectInput source="flagMix" label="table.field.warehouseAreas.flagMix"
        choices={[
            { id: 0, name: '否' },
            { id:  1, name: '是' },
            { id: 1, name: '是' },
        ]}
    />,
@@ -116,7 +114,7 @@
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                    marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.warehouseAreas"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
rsf-admin/src/page/warehouseAreasItem/WarehouseAreasItemList.jsx
@@ -60,8 +60,6 @@
const filters = [
    <SearchInput source="condition" alwaysOn />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <NumberInput source="areaId" label="table.field.warehouseAreasItem.areaId" />,
    <TextInput source="areaName" label="table.field.warehouseAreasItem.areaName" />,
@@ -109,7 +107,7 @@
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                    marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.warehouseAreasItem"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}