verou1
2025-03-10 50f4e2c3a3c5c9d4edafab346c58e3862b7610eb
fix:编辑规则修改
15个文件已修改
3个文件已添加
7237 ■■■■■ 已修改文件
rsf-admin/.env 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/package.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/pnpm-lock.yaml 5197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/App.jsx 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/config/MyDataProvider.js 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/layout/MyMenu.jsx 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/whMat/WhMatList.jsx 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/whMat/WhMatListAside.jsx 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/whMat/whMatCreate.jsx 502 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/whMat/whTable.jsx 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/serialRule/SerialRuleCreate.jsx 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/serialRule/SerialRuleDetail.jsx 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/serialRule/SerialRuleList.jsx 178 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/serialRuleItem/SerialRuleItemList.jsx 184 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/user/UserList.jsx 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/user/UserPanel.jsx 60 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasCreate.jsx 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/.env
@@ -1,2 +1,2 @@
VITE_BASE_IP=localhost
VITE_BASE_IP=192.168.4.24
VITE_BASE_PORT=8080
rsf-admin/package.json
@@ -14,18 +14,24 @@
    "@hello-pangea/dnd": "^16.3.0",
    "@mui/icons-material": "^5.16.7",
    "@mui/material": "^5.16.7",
    "@mui/system": "^6.4.7",
    "@mui/x-tree-view": "^7.16.0",
    "@tweenjs/tween.js": "^21.0.0",
    "axios": "^1.7.4",
    "date-fns": "^3.6.0",
    "framer-motion": "^12.4.10",
    "lodash": "^4.17.21",
    "motion": "^12.4.1",
    "papaparse": "^5.4.1",
    "pixi.js": "^7.4.0",
    "prop-types": "^15.8.1",
    "ra-i18n-polyglot": "^5.6.2",
    "ra-language-english": "^5.6.2",
    "react": "^18.3.0",
    "react-admin": "^5.1.0",
    "react-dom": "^18.3.0",
    "react-hook-form": "^7.53.0",
    "react-router": "^6.22.0",
    "react-router-dom": "^6.26.1",
    "react-syntax-highlighter": "^15.5.0",
    "three": "^0.155.0",
rsf-admin/pnpm-lock.yaml
New file
Diff too large
rsf-admin/src/App.jsx
@@ -1,5 +1,5 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Route } from 'react-router-dom'
import { Route } from "react-router-dom";
import {
  Admin,
  Resource,
@@ -9,52 +9,58 @@
  StoreContextProvider,
  resolveBrowserLocale,
} from "react-admin";
import polyglotI18nProvider from 'ra-i18n-polyglot';
import englishMessages from './i18n/en';
import polyglotI18nProvider from "ra-i18n-polyglot";
import englishMessages from "./i18n/en";
import { Layout } from "./layout";
import AuthProvider from "./config/authProvider";
import DataProvider from "./config/dataProvider";
import Dashboard from "./page/dashboard";
import Settings from "./page/settings";
import Login from "./page/login";
import * as Common from './utils/common'
import { themes } from './themes/themes';
import { SPA_NAME, SPA_VERSION, DEFAULT_THEME_NAME, DEFAULT_THEME_MODE, DATA_PROVIDER_SPRING } from "./config/setting";
import * as Common from "./utils/common";
import { themes } from "./themes/themes";
import {
  SPA_NAME,
  SPA_VERSION,
  DEFAULT_THEME_NAME,
  DEFAULT_THEME_MODE,
  DATA_PROVIDER_SPRING,
} from "./config/setting";
import ResourceContent from "./page/ResourceContent";
import { getSystemInfo } from '@/api/auth';
import { getSystemInfo } from "@/api/auth";
const i18nProvider = polyglotI18nProvider(
  locale => {
    if (locale === 'zh') {
      return import('./i18n/zh').then(messages => messages.default);
  (locale) => {
    if (locale === "zh") {
      return import("./i18n/zh").then((messages) => messages.default);
    }
    // fallback
    return englishMessages;
  },
  // default
  // 'en',
  resolveBrowserLocale('en', { fullLocale: true }),
  resolveBrowserLocale("en", { fullLocale: true }),
  [
    { locale: 'en', name: 'English' },
    { locale: 'zh', name: '简体中文' },
    { locale: "en", name: "English" },
    { locale: "zh", name: "简体中文" },
  ],
  {
    // msg in console
    allowMissing: true,
  }
  },
);
const store = localStorageStore(SPA_VERSION, SPA_NAME);
const App = () => {
  const [themeName] = useStore('themeName', DEFAULT_THEME_NAME);
  const lightTheme = themes.find(theme => theme.name === themeName)?.light;
  const darkTheme = themes.find(theme => theme.name === themeName)?.dark;
  const [themeName] = useStore("themeName", DEFAULT_THEME_NAME);
  const lightTheme = themes.find((theme) => theme.name === themeName)?.light;
  const darkTheme = themes.find((theme) => theme.name === themeName)?.dark;
  useEffect(() => {
    getSystemInfo().then(data => {
    getSystemInfo().then((data) => {
      localStorage.setItem("system", JSON.stringify(data));
    })
    });
  }, []);
  return (
@@ -72,19 +78,17 @@
        loginPage={Login}
        dashboard={Dashboard}
      >
        {permissions => (
        {(permissions) => (
          <>
            {
              Common.extractNavMenus(permissions)?.map(node => {
            {Common.extractNavMenus(permissions)?.map((node) => {
                return (
                  <Resource
                    key={node.id}
                    name={node.component}
                    {...ResourceContent(node)}
                  />
                )
              })
            }
              );
            })}
          </>
        )}
        {/* CustomRoutes don't trigger checkAuth */}
@@ -94,7 +98,7 @@
        </CustomRoutes>
      </Admin>
    </>
  )
  );
};
const AppWrapper = () => (
rsf-admin/src/config/MyDataProvider.js
@@ -1,19 +1,19 @@
import request from '../utils/request';
import * as Common from '../utils/common';
import request from "../utils/request";
import * as Common from "../utils/common";
const MyDataProvider = {
    // *** https://marmelab.com/react-admin/DataProviderWriting.html ***
    // get a list of records based on sort, filter, and pagination
    getList: async (resource, params) => {
        console.log("getList", resource, params);
        const _params = Common.integrateParams(params)
        const res = await request.post(resource + '/page', _params);
    // console.log("getList", resource, params);
    const _params = Common.integrateParams(params);
    const res = await request.post(resource + "/page", _params);
        const { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
                data: data.records,
                total: data.total
        total: data.total,
            });
        }
        return Promise.reject(new Error(msg));
@@ -21,8 +21,8 @@
    // get a single record by id
    getOne: async (resource, params) => {
        console.log("getOne", resource, params);
        const res = await request.get(resource + '/' + params.id);
    // console.log("getOne", resource, params);
    const res = await request.get(resource + "/" + params.id);
        const { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
@@ -37,12 +37,12 @@
    // get a list of records based on an array of ids
    getMany: async (resource, params) => {
        console.log("getMany", resource, params);
        const res = await request.post(resource + '/many/' + params.ids);
    // console.log("getMany", resource, params);
    const res = await request.post(resource + "/many/" + params.ids);
        const { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
                data: data.map(item => ({
        data: data.map((item) => ({
                    id: item.id,
                    ...item,
                })),
@@ -61,12 +61,12 @@
    // create a record
    create: async (resource, params) => {
        console.log("create", resource, params);
        const res = await request.post(resource + '/save', params?.data);
    const res = await request.post(resource + "/save", params?.data);
        const { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
                data: {
                    id: data.id
          id: data.id,
                },
            });
        }
@@ -76,7 +76,10 @@
    // update a record based on a patch
    update: async (resource, params) => {
        console.log("update", resource, params);
        const res = await request.post(resource + '/update', { id: params.id, ...params.data });
    const res = await request.post(resource + "/update", {
      id: params.id,
      ...params.data,
    });
        const { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
@@ -90,8 +93,8 @@
    updateMany: async (resource, params) => {
        console.log("updateMany", resource, params);
        const res = await request.post(
            resource + '/update/many'
            , params.ids.map(id => ({ id, ...params.data }))
      resource + "/update/many",
      params.ids.map((id) => ({ id, ...params.data })),
        );
        const { code, msg, data } = res.data;
        if (code === 200) {
@@ -105,12 +108,12 @@
    // delete a record by id
    delete: async (resource, params) => {
        console.log("delete", resource, params);
        const res = await request.post(resource + '/remove/' + [params.id]);
    const res = await request.post(resource + "/remove/" + [params.id]);
        const { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
                data: {
                    id: params.id
          id: params.id,
                },
            });
        }
@@ -120,7 +123,7 @@
    // delete a list of records based on an array of ids
    deleteMany: async (resource, params) => {
        console.log("deleteMany", resource, params);
        const res = await request.post(resource + '/remove/' + params?.ids);
    const res = await request.post(resource + "/remove/" + params?.ids);
        const { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
@@ -136,13 +139,13 @@
        const _params = Common.integrateParams(params);
        try {
            const res = await request.post(`${resource}/export`, _params, {
                responseType: 'blob',
        responseType: "blob",
            });
            return res;
        } catch (error) {
            return Promise.reject(new Error(error));
        }
    }
}
  },
};
export default MyDataProvider;
rsf-admin/src/index.jsx
@@ -2,11 +2,9 @@
import ReactDOM from "react-dom/client";
import App from "./App";
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import "@fontsource/roboto/300.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/500.css";
import "@fontsource/roboto/700.css";
ReactDOM.createRoot(document.getElementById("root")).render(
  <App />
);
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
rsf-admin/src/layout/MyMenu.jsx
@@ -6,15 +6,15 @@
    Menu,
    useSidebarState,
    usePermissions,
} from 'react-admin';
import { useLocation } from 'react-router-dom';
import { Box } from '@mui/material';
import SubMenu from './SubMenu';
import SettingsIcon from '@mui/icons-material/Settings';
import DashboardIcon from '@mui/icons-material/Dashboard';
import HorizontalRuleIcon from '@mui/icons-material/HorizontalRule';
import PersonIcon from '@mui/icons-material/Person';
import * as Icons from '@mui/icons-material';
} from "react-admin";
import { useLocation } from "react-router-dom";
import { Box } from "@mui/material";
import SubMenu from "./SubMenu";
import SettingsIcon from "@mui/icons-material/Settings";
import DashboardIcon from "@mui/icons-material/Dashboard";
import HorizontalRuleIcon from "@mui/icons-material/HorizontalRule";
import PersonIcon from "@mui/icons-material/Person";
import * as Icons from "@mui/icons-material";
const getIconComponent = (iconStr) => {
    return Icons[iconStr] || HorizontalRuleIcon;
@@ -29,10 +29,14 @@
    useEffect(() => {
        // default open sub menu
        const defaultExpandMenu = ["menu.system", "menu.dispatcher", "menu.equipment"];
        permissions?.forEach(item => {
    const defaultExpandMenu = [
      "menu.system",
      "menu.dispatcher",
      "menu.equipment",
    ];
    permissions?.forEach((item) => {
            if (defaultExpandMenu.includes(item.name)) {
                setState(state => ({ ...state, [item.route]: true }));
        setState((state) => ({ ...state, [item.route]: true }));
            }
        });
    }, [permissions]);
@@ -40,15 +44,14 @@
    useEffect(() => {
        // expand this parent menu
        const currentPath = location.pathname;
        const parentRoutes = findParentRoutes(currentPath, permissions)
    const parentRoutes = findParentRoutes(currentPath, permissions);
        for (const parentRoute of parentRoutes) {
            setState(state => ({ ...state, [parentRoute]: true }));
      setState((state) => ({ ...state, [parentRoute]: true }));
        }
    }, [location.pathname]);
    const handleToggle = (menu) => {
        setState(state => ({ ...state, [menu]: !state[menu] }));
    setState((state) => ({ ...state, [menu]: !state[menu] }));
    };
    const getIcon = (iconStr) => {
@@ -93,16 +96,16 @@
        });
    };
    return isPending
        ? (<div>Waiting for permissions...</div>) :
        (
  return isPending ? (
    <div>Waiting for permissions...</div>
  ) : (
            <Box
                sx={{
                    width: sidebarIsOpen ? 200 : 50,
                    marginTop: 1,
                    marginBottom: 1,
                    transition: theme =>
                        theme.transitions.create('width', {
        transition: (theme) =>
          theme.transitions.create("width", {
                            easing: theme.transitions.easing.sharp,
                            duration: theme.transitions.duration.leavingScreen,
                        }),
@@ -113,7 +116,7 @@
                    primaryText="menu.dashboard"
                    leftIcon={<DashboardIcon />}
                />
                {permissions && (generateMenu(permissions))}
      {permissions && generateMenu(permissions)}
                {/* <Menu.ResourceItems /> */}
                <Menu.Item
                    to="/settings"
@@ -121,8 +124,8 @@
                    leftIcon={<PersonIcon />}
                />
            </Box>
        )
}
  );
};
const findParentRoutes = (pathname, permissions) => {
    if (!pathname || !permissions) {
@@ -147,7 +150,9 @@
        const parentRoutes = [];
        let current = item;
        while (current && current.parentId) {
            const parent = allPermissions.find(permission => permission.id === current.parentId);
      const parent = allPermissions.find(
        (permission) => permission.id === current.parentId,
      );
            if (parent) {
                parentRoutes.push(parent.route);
                current = parent;
rsf-admin/src/page/basicInfo/whMat/WhMatList.jsx
@@ -1,100 +1,162 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, LinearProgress, TextField, Button, Typography } from "@mui/material";
import * as Icons from '@mui/icons-material';
import { List, useTranslate, useListContext, Title, } from "react-admin";
import {
  Box,
  Card,
  CardContent,
  LinearProgress,
  TextField,
  Button,
  Typography,
} from "@mui/material";
import * as Icons from "@mui/icons-material";
import {
  List,
  useTranslate,
  useListContext,
  Title,
  useGetList,
  useNotify,
} from "react-admin";
import WhMatListAside from "./WhMatListAside";
import { RichTreeView } from "@mui/x-tree-view/RichTreeView";
import { TreeItem2 } from "@mui/x-tree-view/TreeItem2";
import request from '@/utils/request';
import { Add, Edit, Delete, Padding, Save } from '@mui/icons-material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import RefreshIcon from '@mui/icons-material/Refresh';
import { useTreeItem2Utils } from '@mui/x-tree-view/hooks';
import request from "@/utils/request";
import { Add, Edit, Delete, Padding, Save } from "@mui/icons-material";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight";
import RefreshIcon from "@mui/icons-material/Refresh";
import { useTreeItem2Utils } from "@mui/x-tree-view/hooks";
import WhMatCreate from "./whMatCreate";
// const RESOURCE = 'dept';
const TITLE = 'menu.whMat';
const TITLE = "menu.whMat";
const WhMatListContent = () => {
    const translate = useTranslate();
    const [searchVal, setSearchVal] = useState('');
  const [searchVal, setSearchVal] = useState("");
    const [createDialog, setCreateDialog] = React.useState(false);
    const [editRecord, setEditRecord] = React.useState(null);
    const treeData = [
        {
            id: '19',
            label: '半成品',
            secondaryLabel: 'RM001',
      id: "19",
      label: "半成品",
      secondaryLabel: "RM001",
            editable: true,
            children: [
                {
                    id: 'grid-community', primaryText: '半成品',
                    secondaryText: 'RM001', label: '@mui/x-data-grid', editable: true, children: [
          id: "grid-community",
          primaryText: "半成品",
          secondaryText: "RM001",
          label: "@mui/x-data-grid",
          editable: true,
          children: [
                        {
                            id: 'grid-community22', primaryText: '半成品',
                            secondaryText: 'RM001', label: '@mui/x-data-grid', editable: true
                        },]
                },
                {
                    id: 'grid-pro', primaryText: '半成品',
                    secondaryText: 'RM001', label: '@mui/x-data-grid-pro', editable: true
                },
                {
                    id: 'grid-premium', primaryText: '半成品',
                    secondaryText: 'RM001', label: '@mui/x-data-grid-premium', editable: true
              id: "grid-community22",
              primaryText: "半成品",
              secondaryText: "RM001",
              label: "@mui/x-data-grid",
              editable: true,
                },
            ],
        },
        {
            id: '18',
            label: '原材料',
            primaryText: '半成品',
            secondaryText: 'RM001',
          id: "grid-pro",
          primaryText: "半成品",
          secondaryText: "RM001",
          label: "@mui/x-data-grid-pro",
          editable: true,
        },
        {
            id: 'charts',
            label: 'Charts',
            primaryText: '半成品',
            secondaryText: 'RM001',
            children: [{
                id: 'charts-community', primaryText: '半成品',
                secondaryText: 'RM001', label: '@mui/x-charts'
            }],
          id: "grid-premium",
          primaryText: "半成品",
          secondaryText: "RM001",
          label: "@mui/x-data-grid-premium",
          editable: true,
        },
      ],
        },
        {
            id: 'tree-view',
            label: 'Tree View',
            primaryText: '半成品',
            secondaryLabel: 'RM001',
            children: [{
                id: 'tree-view-community', primaryText: '半成品',
                secondaryText: 'RM001', label: '@mui/x-tree-view'
            }],
      id: "18",
      label: "原材料",
      primaryText: "半成品",
      secondaryText: "RM001",
        },
        {
            id: 'tree-view2',
            label: 'Tree View3',
            primaryText: '半成品',
            secondaryText: 'RM001',
            children: [{
                id: 'tree-view-community1', primaryText: '半成品',
                secondaryText: 'RM001', label: '@mui/x-tree-view'
            }],
      id: "charts",
      label: "Charts",
      primaryText: "半成品",
      secondaryText: "RM001",
      children: [
        {
          id: "charts-community",
          primaryText: "半成品",
          secondaryText: "RM001",
          label: "@mui/x-charts",
        },
      ],
    },
    {
      id: "tree-view",
      label: "Tree View",
      primaryText: "半成品",
      secondaryLabel: "RM001",
      children: [
        {
          id: "tree-view-community",
          primaryText: "半成品",
          secondaryText: "RM001",
          label: "@mui/x-tree-view",
        },
      ],
    },
    {
      id: "tree-view2",
      label: "Tree View3",
      primaryText: "半成品",
      secondaryText: "RM001",
      children: [
        {
          id: "tree-view-community1",
          primaryText: "半成品",
          secondaryText: "RM001",
          label: "@mui/x-tree-view",
        },
      ],
        },
    ];
  const notify = useNotify();
    const handleNodeSelect = (event, nodeId) => {
        console.log('Selected Node ID:', nodeId);
    console.log("Selected Node ID:", nodeId);
        // 在这里可以根据 nodeId 更新主内容区域
    };
    const handleSearch = () => {
        console.log('Search Input:', selectedOption);
    console.log("Search Input:", selectedOption);
    };
  const handleInput = (value) => {
    setSearchVal(value);
  };
    const CustomCheckbox = React.forwardRef(function CustomCheckbox(props, ref) {
        return <input type="checkbox" ref={ref} {...props} />;
    });
  const getMatnrList = async () => {
    const {
      data: { code, data, msg },
    } = await request.post("/matnr/list", {}).then();
    if (code === 200) {
      console.log(data);
    } else {
      notify(msg);
    }
  };
  React.useEffect(() => {
    getMatnrList();
  }, []);
    function CustomLabel({ children, className, secondaryLabel }) {
        return (
            <Box display={"flex"} alignItems={"end"}>
@@ -124,9 +186,8 @@
                    label: CustomLabel,
                }}
                slotProps={{
                    label: { secondaryLabel: item?.secondaryLabel || '' },
          label: { secondaryLabel: item?.secondaryLabel || "" },
                }}
            />
        );
    });
@@ -134,20 +195,20 @@
    const isLoading = false;
    React.useEffect(() => {
        request.post('/matnrGroup/tree', {})
            .then(res => {
    request
      .post("/matnrGroup/tree", {})
      .then((res) => {
                if (res?.data?.code === 200) {
                    let data = res.data.data;
                    console.log(data);
                } else {
                    notify(res.data.msg);
                }
            })
            .catch(error => {
                notify('Error fetching tree data');
      .catch((error) => {
        notify("Error fetching tree data");
            });
    }, [searchVal])
  }, [searchVal]);
    const handleAdd = () => {
        setCreateDialog(true);
@@ -155,18 +216,23 @@
    return (
        <>
            <Box sx={{ mt: 1, mr: 3, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
      <Box
        sx={{
          mt: 1,
          mr: 3,
          display: "flex",
          alignItems: "center",
          justifyContent: "space-between",
        }}
      >
                <WhMatCreate
                    editRecord={editRecord}
                    open={createDialog}
                    setOpen={setCreateDialog}
                />
                <Box
                    width={300}
                    mb={1}
                >
        <Box width={300} mb={1}>
                    <TextField
                        label={translate('ra.action.search')}
            label={translate("ra.action.search")}
                        value={searchVal}
                        onChange={(e) => handleInput(e.target.value)}
                    />
@@ -178,7 +244,7 @@
                        startIcon={<Add />}
                        onClick={handleAdd}
                    >
                        {translate('ra.action.add')}
            {translate("ra.action.add")}
                    </Button>
                    <Button
                        variant="outlined"
@@ -186,7 +252,7 @@
                        startIcon={<Delete />}
                        sx={{ ml: 1 }}
                    >
                        {translate('ra.action.delete')}
            {translate("ra.action.delete")}
                    </Button>
                    <Button
                        variant="outlined"
@@ -194,7 +260,7 @@
                        sx={{ ml: 1 }}
                        startIcon={<Save />}
                    >
                        {translate('ra.action.save')}
            {translate("ra.action.save")}
                    </Button>
                </Box>
            </Box>
@@ -202,7 +268,7 @@
            <Card>
                <CardContent>
                    <RichTreeView
                        defaultExpandedItems={['grid', 'pickers']}
            defaultExpandedItems={["grid", "pickers"]}
                        checkboxSelection
                        items={treeData}
                        slots={{ item: CustomTreeItem }}
@@ -211,17 +277,19 @@
                </CardContent>
            </Card>
        </>
    )
}
  );
};
const WhMatList = () => {
    const translate = useTranslate();
    return (
        <>
            <Box sx={{
                display: 'flex',
                marginBottom: 24
            }}>
      <Box
        sx={{
          display: "flex",
          marginBottom: 24,
        }}
      >
                <Title title={TITLE} />
                <Box>
                    <WhMatListAside />
@@ -231,7 +299,6 @@
                </Box>
            </Box>
        </>
    )
}
  );
};
export default WhMatList;
rsf-admin/src/page/basicInfo/whMat/WhMatListAside.jsx
@@ -1,58 +1,81 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { Card, useTheme, List, CardContent, Input, InputAdornment, IconButton, TextField } from "@mui/material";
import { useForm } from 'react-hook-form';
import React, {
  useState,
  useRef,
  useEffect,
  useMemo,
  useCallback,
} from "react";
import {
  Card,
  useTheme,
  List,
  CardContent,
  Input,
  InputAdornment,
  IconButton,
  TextField,
} from "@mui/material";
import { useForm } from "react-hook-form";
import Warehouse from "./warehouse";
import { Filter, SearchInput, useListContext,useTranslate } from 'react-admin';
import request from '@/utils/request';
import {
  Filter,
  SearchInput,
  useListContext,
  useTranslate,
  useNotify,
} from "react-admin";
import request from "@/utils/request";
const WhMatListAside = () => {
    const theme = useTheme();
    const translate = useTranslate();
    const [searchVal, setSearchVal] = useState('');
  const [searchVal, setSearchVal] = useState("");
    const { control, getValues } = useForm();
  const notify = useNotify();
    const [map, setMap] = useState([
        {
            id: 1,
            name: '仓库1',
            code: 'WH1',
            icon: 'Warehouse',
      name: "仓库1",
      code: "WH1",
      icon: "Warehouse",
            locCount: 350,
        },
        {
            id: 2,
            name: '仓库2',
            code: 'WH1',
            icon: 'Warehouse',
      name: "仓库2",
      code: "WH1",
      icon: "Warehouse",
            locCount: 237,
        },
        {
            id: 3,
            name: '仓库3',
            code: 'WH1',
            icon: 'Warehouse',
      name: "仓库3",
      code: "WH1",
      icon: "Warehouse",
            locCount: 590,
        }
    },
    ]);
    
    const handleInput = (value) => {
        console.log(value);
        setSearchVal(value);
    }
    React.useEffect(() => {
        request.post('/matnrGroup/tree', {})
            .then(res => {
                if (res?.data?.code === 200) {
                    let data = res.data.data;
                    console.log(data);
  };
  const getMatnrList = async () => {
    const {
      data: { code, data, msg },
    } = await request.post("/warehouse/list", {}).then();
    if (code === 200) {
      console.log(data);
                } else {
                    notify(res.data.msg);
      notify(msg);
                }
            })
            .catch(error => {
                notify('Error fetching tree data');
            });
    }, [searchVal])
  };
  React.useEffect(() => {
    getMatnrList();
  }, [searchVal]);
    return (
        <Card
@@ -60,27 +83,25 @@
                order: -1,
                mr: 2,
                mt: 4,
                width: 250
        width: 250,
            }}
        >
            <CardContent>
                <div>
                    <TextField
                        label={translate('ra.action.search')}
            label={translate("ra.action.search")}
                        value={searchVal}
                        onChange={(e) => handleInput(e.target.value)}
                    />
                </div>
                <List>
                    {map.map(record => (
          {map.map((record) => (
                        <Warehouse key={record.id} record={record} />
                    ))}
                </List>
            </CardContent>
        </Card>
    )
}
  );
};
export default WhMatListAside;
rsf-admin/src/page/basicInfo/whMat/whMatCreate.jsx
@@ -10,7 +10,7 @@
    AutocompleteInput,
    required,
    NumberInput,
} from 'react-admin';
} from "react-admin";
import {
    Dialog,
    DialogActions,
@@ -28,49 +28,48 @@
    TableHead,
    Paper,
    Checkbox,
    Button
} from '@mui/material';
  Button,
} from "@mui/material";
import DialogCloseButton from "@/page/components/DialogCloseButton";
import { styled } from '@mui/material/styles';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import { styled } from "@mui/material/styles";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight";
import { Search } from "@mui/icons-material";
import { tr } from "date-fns/locale";
import WhTable from "./whTable";
const columns = [
    {
        id: 'matnrCode',
        label: 'table.field.whMat.matnrCode',
    id: "matnrCode",
    label: "table.field.whMat.matnrCode",
        minWidth: 160,
    },
    {
        id: 'matnrName',
        label: 'table.field.whMat.matnrName',
    id: "matnrName",
    label: "table.field.whMat.matnrName",
    },
    {
        id: 'matnrGroupId',
        label: 'table.field.whMat.matnrGroupId',
    id: "matnrGroupId",
    label: "table.field.whMat.matnrGroupId",
    },
    {
        id: 'spec',
        label: 'table.field.whMat.spec',
    id: "spec",
    label: "table.field.whMat.spec",
    },
    {
        id: 'color',
        label: 'table.field.whMat.color',
    id: "color",
    label: "table.field.whMat.color",
    },
    {
        id: 'size',
        label: 'table.field.whMat.size',
    id: "size",
    label: "table.field.whMat.size",
    },
    {
        id: 'minWeight',
        label: 'table.field.whMat.minWeight',
    id: "minWeight",
    label: "table.field.whMat.minWeight",
    },
    {
        id: 'maxWeight',
        label: 'table.field.whMat.maxWeight',
    id: "maxWeight",
    label: "table.field.whMat.maxWeight",
    },
];
@@ -100,23 +99,36 @@
                    maxWidth="lg"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form record={editRecord} onSubmit={onSubmit}>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
            <DialogTitle
              id="form-dialog-title"
              sx={{
                position: "sticky",
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                backgroundColor: "background.paper",
                zIndex: 1000,
                        }}
                        >
                            {editRecord ? translate('update.title') : translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
              {editRecord
                ? translate("update.title")
                : translate("create.title")}
              <Box
                sx={{ position: "absolute", top: 8, right: 8, zIndex: 1001 }}
              >
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent sx={{ mt: 2 }}>
                            <WhMatCreateContent />
                        </DialogContent>
                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
            <DialogActions
              sx={{
                position: "sticky",
                bottom: 0,
                backgroundColor: "background.paper",
                zIndex: 1000,
              }}
            >
              <Toolbar sx={{ width: "100%", justifyContent: "space-between" }}>
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
@@ -127,18 +139,18 @@
    );
};
const StyledTableRow = styled(TableRow)(({ theme }) => ({
    '& .MuiButtonBase-root': {
        padding: '0px 0px'
    }
  "& .MuiButtonBase-root": {
    padding: "0px 0px",
  },
}));
const StyledTableCell = styled(TableCell)(({ theme }) => ({
    '& .MuiButtonBase-root': {
        padding: '0px 0px'
  "& .MuiButtonBase-root": {
    padding: "0px 0px",
    },
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    whiteSpace: 'nowrap',
  overflow: "hidden",
  textOverflow: "ellipsis",
  whiteSpace: "nowrap",
    maxWidth: 600,
}));
@@ -147,7 +159,7 @@
    const isOpen = openNodes[row.id] || false;
    const [checked, setChecked] = useState(false);
    const toggleNode = (id) => {
        setOpenNodes(prevState => ({ ...prevState, [id]: !prevState[id] }));
    setOpenNodes((prevState) => ({ ...prevState, [id]: !prevState[id] }));
    };
    return (
        <React.Fragment>
@@ -157,14 +169,14 @@
                        <IconButton
                            aria-label="expand row"
                            size="small"
                            style={{ marginLeft: (depth * 16 + 8) }}
              style={{ marginLeft: depth * 16 + 8 }}
                            onClick={() => toggleNode(row.id)}
                        >
                            {isOpen ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
                        </IconButton>
                    )}
                </StyledTableCell>
                <StyledTableCell style={{ paddingLeft: (depth * 16 + 16) }}>
        <StyledTableCell style={{ paddingLeft: depth * 16 + 16 }}>
                    <Checkbox
                        key={row.id}
                        checked={row.checked}
@@ -172,25 +184,25 @@
                    />
                </StyledTableCell>
                {columns.map((column, idx) => {
                    if (column.id !== 'actions') {
          if (column.id !== "actions") {
                        const value = row[column.id];
                        return (
                            <>
                                <StyledTableCell
                                    key={column.id}
                                    align={column.align || 'left'}
                  align={column.align || "left"}
                                    // style={{ paddingLeft: idx === 0 && (depth * 16 + 16) }}
                                    onClick={() => toggleNode(row.id)}
                                >
                                    {column.format ? column.format(value) : value}
                                </StyledTableCell>
                            </>
                        )
            );
                    }
                })}
            </StyledTableRow>
            {row.children && isOpen && (
      {row.children &&
        isOpen &&
                row.children.map((child) => (
                    <TreeTableRow
                        key={child.id}
@@ -199,8 +211,7 @@
                        openNodes={openNodes}
                        setOpenNodes={setOpenNodes}
                    />
                ))
            )}
        ))}
        </React.Fragment>
    );
};
@@ -210,64 +221,166 @@
    const [selAll, setSelAll] = useState(false);
    const [treeData, setTreeData] = useState([
        {
            id: 1, matnrCode: 'root1', matnrName: '根节点', matnrGroupId: 'admin', sort: 1, checked: false, children: [
      id: 1,
      matnrCode: "root1",
      matnrName: "根节点",
      matnrGroupId: "admin",
      sort: 1,
      checked: false,
      children: [
                {
                    id: 29, matnrCode: 'root29', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: [
                        { id: 30, matnrCode: 'root30', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
                        { id: 31, matnrCode: 'root31', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
                        { id: 32, matnrCode: 'root32', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
                        { id: 33, matnrCode: 'root33', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
                        { id: 34, matnrCode: 'root34', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
                        { id: 35, matnrCode: 'root35', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
                    ]
          id: 29,
          matnrCode: "root29",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: [
            {
              id: 30,
              matnrCode: "root30",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
                },
                { id: 24, matnrCode: 'root24', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
                { id: 25, matnrCode: 'root25', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
                { id: 26, matnrCode: 'root26', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
                { id: 27, matnrCode: 'root27', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
                { id: 28, matnrCode: 'root28', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
            ]
            {
              id: 31,
              matnrCode: "root31",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
        },
        { id: 2, matnrCode: 'root2', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 3, matnrCode: 'root3', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 4, matnrCode: 'root4', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 5, matnrCode: 'root5', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 6, matnrCode: 'root6', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 7, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 8, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 9, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 10, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 11, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 12, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 13, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 14, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 15, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 16, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 17, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 18, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 19, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 20, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 21, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 22, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
        { id: 23, matnrCode: 'root', fullName: '根节点', leader: 'admin', sort: 1, checked: false, children: null },
            {
              id: 32,
              matnrCode: "root32",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
            },
            {
              id: 33,
              matnrCode: "root33",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
            },
            {
              id: 34,
              matnrCode: "root34",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
            },
            {
              id: 35,
              matnrCode: "root35",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
            },
          ],
        },
        {
          id: 24,
          matnrCode: "root24",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: null,
        },
        {
          id: 25,
          matnrCode: "root25",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: null,
        },
        {
          id: 26,
          matnrCode: "root26",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: null,
        },
        {
          id: 27,
          matnrCode: "root27",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: null,
        },
        {
          id: 28,
          matnrCode: "root28",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: null,
        },
      ],
    },
    {
      id: 2,
      matnrCode: "root2",
      fullName: "根节点",
      leader: "admin",
      sort: 1,
      checked: false,
      children: null,
    },
    {
      id: 3,
      matnrCode: "root3",
      fullName: "根节点",
      leader: "admin",
      sort: 1,
      checked: false,
      children: null,
    },
    {
      id: 4,
      matnrCode: "root4",
      fullName: "根节点",
      leader: "admin",
      sort: 1,
      checked: false,
      children: null,
    },
    ]);
    const [openNodes, setOpenNodes] = React.useState({});
    const handleSearch = () => {
        console.log('handleSearch');
    }
    console.log("handleSearch");
  };
    const selectAll = () => {
        selAll ? setSelAll(false) : setSelAll(true);
    }
  };
    useEffect(() => {
        console.log(selAll);
        const setAllChecked = (nodes, checked) => {
            nodes.forEach(node => {
      nodes.forEach((node) => {
                node.checked = !checked;
                if (node.children) {
                    setAllChecked(node.children, checked);
@@ -282,26 +395,166 @@
        }
    }, [selAll]);
  const [treeDatas, setTreeDatas] = useState([
    {
      id: 1,
      matnrCode: "root1",
      matnrName: "根节点",
      matnrGroupId: "admin",
      sort: 1,
      checked: false,
      children: [
        {
          id: 29,
          matnrCode: "root29",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: [
            {
              id: 30,
              matnrCode: "root30",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
            },
            {
              id: 31,
              matnrCode: "root31",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
            },
            {
              id: 32,
              matnrCode: "root32",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
            },
            {
              id: 33,
              matnrCode: "root33",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
            },
            {
              id: 34,
              matnrCode: "root34",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
            },
            {
              id: 35,
              matnrCode: "root35",
              fullName: "根节点",
              leader: "admin",
              sort: 1,
              checked: false,
              children: null,
            },
          ],
        },
        {
          id: 24,
          matnrCode: "root24",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: null,
        },
        {
          id: 25,
          matnrCode: "root25",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: null,
        },
        {
          id: 26,
          matnrCode: "root26",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: null,
        },
        {
          id: 27,
          matnrCode: "root27",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: null,
        },
        {
          id: 28,
          matnrCode: "root28",
          fullName: "根节点",
          leader: "admin",
          sort: 1,
          checked: false,
          children: null,
        },
      ],
    },
    {
      id: 2,
      matnrCode: "root2",
      fullName: "根节点",
      leader: "admin",
      sort: 1,
      checked: false,
      children: null,
    },
    {
      id: 3,
      matnrCode: "root3",
      fullName: "根节点",
      leader: "admin",
      sort: 1,
      checked: false,
      children: null,
    },
    {
      id: 4,
      matnrCode: "root4",
      fullName: "根节点",
      leader: "admin",
      sort: 1,
      checked: false,
      children: null,
    },
  ]);
    return (
        <>
            <Grid container rowSpacing={2} columnSpacing={2}>
                <Grid item xs={3} display="flex" gap={1}>
                    <TextInput
                        label="table.field.whMat.matnrCode"
                        source="matnrCode"
                    />
          <TextInput label="table.field.whMat.matnrCode" source="matnrCode" />
                </Grid>
                <Grid item xs={3} display="flex" gap={1}>
                    <TextInput
                        label="table.field.whMat.matnrName"
                        source="matnrName"
                    />
          <TextInput label="table.field.whMat.matnrName" source="matnrName" />
                </Grid>
                <Grid item xs={3} display="flex" gap={1}>
                    <ReferenceInput
                        source="groupId"
                        reference="matnrGroup"
                    >
          <ReferenceInput source="groupId" reference="matnrGroup">
                        <AutocompleteInput
                            label="table.field.whMat.matnrGroupId"
                            optionText="name"
@@ -310,35 +563,20 @@
                    </ReferenceInput>
                </Grid>
                <Grid item xs={3} display="flex" gap={1}>
                    <TextInput
                        label="table.field.whMat.spec"
                        source="spec"
                    />
          <TextInput label="table.field.whMat.spec" source="spec" />
                </Grid>
                <Grid item xs={3} display="flex" gap={1}>
                    <TextInput
                        label="table.field.whMat.color"
                        source="color"
                    />
          <TextInput label="table.field.whMat.color" source="color" />
                </Grid>
                <Grid item xs={3} display="flex" gap={1}>
                    <TextInput
                        label="table.field.whMat.size"
                        source="size"
                    />
          <TextInput label="table.field.whMat.size" source="size" />
                </Grid>
                <Grid item xs={3} display="flex" gap={1}>
                    <TextInput
                        label="table.field.whMat.minWeight"
                        source="minWeight"
                    />
          <TextInput label="table.field.whMat.minWeight" source="minWeight" />
                </Grid>
                <Grid item xs={3} display="flex" gap={1}>
                    <TextInput
                        label="table.field.whMat.maxWeight"
                        source="maxWeight"
                    />
          <TextInput label="table.field.whMat.maxWeight" source="maxWeight" />
                </Grid>
            </Grid>
            <Box sx={{ mt: 2, mb: 2 }}>
@@ -349,7 +587,7 @@
                        startIcon={<Search />}
                        onClick={handleSearch}
                    >
                        {translate('ra.action.search')}
            {translate("ra.action.search")}
                    </Button>
                </Stack>
            </Box>
@@ -364,7 +602,7 @@
                            {columns.map((column, idx) => (
                                <StyledTableCell
                                    key={idx}
                                    align={column.align || 'left'}
                  align={column.align || "left"}
                                    style={{
                                        minWidth: column.minWidth,
                                    }}
@@ -375,7 +613,8 @@
                        </TableRow>
                    </TableHead>
                    <TableBody>
                        {treeData && treeData.length > 0 && (
            {treeData &&
              treeData.length > 0 &&
                            treeData.map((row) => (
                                <TreeTableRow
                                    key={row.id}
@@ -383,13 +622,12 @@
                                    openNodes={openNodes}
                                    setOpenNodes={setOpenNodes}
                                />
                            ))
                        )}
              ))}
                    </TableBody>
                </Table>
            </TableContainer>
      <WhTable data={treeDatas} />
        </>
    );
};
rsf-admin/src/page/basicInfo/whMat/whTable.jsx
New file
@@ -0,0 +1,189 @@
import React, { useState } from "react";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableRow,
  IconButton,
  Checkbox,
} from "@mui/material";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight";
// 递归收集所有节点的 ID
const collectAllNodeIds = (nodes) => {
  let allIds = [];
  nodes.forEach((node) => {
    allIds.push(node.id);
    if (node.children) {
      allIds = allIds.concat(collectAllNodeIds(node.children));
    }
  });
  return allIds;
};
// 递归收集某个节点及其所有子节点的 ID
const collectNodeAndChildrenIds = (node) => {
  let ids = [node.id];
  if (node.children) {
    node.children.forEach((child) => {
      ids = ids.concat(collectNodeAndChildrenIds(child));
    });
  }
  return ids;
};
// 检查某个节点的所有子节点是否都被选中
const areAllChildrenSelected = (node, selectedNodeIds) => {
  const childrenIds = collectAllNodeIds(node.children || []);
  return childrenIds.every((id) => selectedNodeIds.includes(id));
};
// 递归渲染树状表格行
const renderTreeRows = (
  nodes,
  level = 0,
  openNodes,
  setOpenNodes,
  selectedNodeIds,
  setSelectedNodeIds,
) => {
  return nodes.map((node) => {
    const isOpen = openNodes.includes(node.id);
    const toggleOpen = () => {
      if (isOpen) {
        setOpenNodes(openNodes.filter((id) => id !== node.id));
      } else {
        setOpenNodes([...openNodes, node.id]);
      }
    };
    const allChildrenIds = collectNodeAndChildrenIds(node);
    const allChildrenSelected = allChildrenIds.every((id) =>
      selectedNodeIds.includes(id),
    );
    const someChildrenSelected =
      allChildrenIds.some((id) => selectedNodeIds.includes(id)) &&
      !allChildrenSelected;
    const handleCheckboxChange = () => {
      let newSelectedNodeIds = [...selectedNodeIds];
      if (allChildrenSelected) {
        allChildrenIds.forEach((id) => {
          newSelectedNodeIds = newSelectedNodeIds.filter(
            (selectedId) => selectedId !== id,
          );
        });
      } else {
        allChildrenIds.forEach((id) => {
          if (!newSelectedNodeIds.includes(id)) {
            newSelectedNodeIds.push(id);
          }
        });
      }
      setSelectedNodeIds(newSelectedNodeIds);
    };
    return (
      <React.Fragment key={node.id}>
        <TableRow size="small">
          <TableCell padding="none" width={20}>
            {node.children && (
              <IconButton size="small" onClick={toggleOpen}>
                {isOpen ? (
                  <KeyboardArrowDownIcon fontSize="small" />
                ) : (
                  <KeyboardArrowRightIcon fontSize="small" />
                )}
              </IconButton>
            )}
          </TableCell>
          <TableCell width={20} style={{ paddingLeft: 20 * level }}>
            <Checkbox
              size="small"
              checked={allChildrenSelected}
              indeterminate={someChildrenSelected}
              onChange={handleCheckboxChange}
            />
          </TableCell>
          <TableCell>{node.matnrCode}</TableCell>
          <TableCell>{node.fullName || node.matnrName}</TableCell>
          <TableCell>{node.matnrGroupId}</TableCell>
          <TableCell>{node.specification || "-"}</TableCell>
          <TableCell>{node.color || "-"}</TableCell>
          <TableCell>{node.size || "-"}</TableCell>
          <TableCell>{node.minWeight || "-"}</TableCell>
          <TableCell>{node.maxWeight || "-"}</TableCell>
        </TableRow>
        {isOpen &&
          node.children &&
          renderTreeRows(
            node.children,
            level + 1,
            openNodes,
            setOpenNodes,
            selectedNodeIds,
            setSelectedNodeIds,
          )}
      </React.Fragment>
    );
  });
};
const TreeTable = ({ data }) => {
  const [openNodes, setOpenNodes] = useState([]);
  const [selectedNodeIds, setSelectedNodeIds] = useState([]);
  const allNodeIds = collectAllNodeIds(data);
  const handleSelectAll = (event) => {
    if (event.target.checked) {
      setSelectedNodeIds(allNodeIds);
    } else {
      setSelectedNodeIds([]);
    }
  };
  const isAllSelected = selectedNodeIds.length === allNodeIds.length;
  return (
    <Table size="small" style={{ backgroundColor: "#121317" }}>
      <TableHead>
        <TableRow size="small">
          <TableCell width={20}></TableCell>
          <TableCell width={20}>
            <Checkbox
              size="small"
              checked={isAllSelected}
              indeterminate={
                selectedNodeIds.length > 0 &&
                selectedNodeIds.length < allNodeIds.length
              }
              onChange={handleSelectAll}
            />
          </TableCell>
          <TableCell>物料编码</TableCell>
          <TableCell>物料名称</TableCell>
          <TableCell>物料分组</TableCell>
          <TableCell>规格</TableCell>
          <TableCell>颜色</TableCell>
          <TableCell>尺寸</TableCell>
          <TableCell>最小重量</TableCell>
          <TableCell>最大重量</TableCell>
        </TableRow>
      </TableHead>
      <TableBody>
        {renderTreeRows(
          data,
          0,
          openNodes,
          setOpenNodes,
          selectedNodeIds,
          setSelectedNodeIds,
        )}
      </TableBody>
    </Table>
  );
};
export default TreeTable;
rsf-admin/src/page/serialRule/SerialRuleCreate.jsx
@@ -17,7 +17,7 @@
    useNotify,
    Form,
    useCreateController,
} from 'react-admin';
} from "react-admin";
import {
    Dialog,
    DialogActions,
@@ -26,7 +26,7 @@
    Stack,
    Grid,
    Box,
} from '@mui/material';
} from "@mui/material";
import DialogCloseButton from "../components/DialogCloseButton";
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
@@ -45,11 +45,14 @@
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    notify("common.response.success");
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    notify(error.message || "common.response.fail", {
      type: "error",
      messageArgs: { _: error.message },
    });
    };
    return (
@@ -67,18 +70,22 @@
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
          maxWidth="xl" // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
            <DialogTitle
              id="form-dialog-title"
              sx={{
                position: "sticky",
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                backgroundColor: "background.paper",
                zIndex: 1000,
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
              {translate("create.title")}
              <Box
                sx={{ position: "absolute", top: 8, right: 8, zIndex: 1001 }}
              >
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
@@ -88,7 +95,7 @@
                                    <TextInput
                                        label="table.field.serialRule.code"
                                        source="code"
                                        parse={v => v}
                    parse={(v) => v}
                                        autoFocus
                                    />
                                </Grid>
@@ -96,21 +103,21 @@
                                    <TextInput
                                        label="table.field.serialRule.name"
                                        source="name"
                                        parse={v => v}
                    parse={(v) => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.delimit"
                                        source="delimit"
                                        parse={v => v}
                    parse={(v) => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.reset"
                                        source="reset"
                                        parse={v => v}
                    parse={(v) => v}
                                        validate={required()}
                                    />
                                </Grid>
@@ -118,21 +125,21 @@
                                    <TextInput
                                        label="table.field.serialRule.resetDep"
                                        source="resetDep"
                                        parse={v => v}
                    parse={(v) => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.currValue"
                                        source="currValue"
                                        parse={v => v}
                    parse={(v) => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.lastCode"
                                        source="lastCode"
                                        parse={v => v}
                    parse={(v) => v}
                                    />
                                </Grid>
@@ -140,14 +147,21 @@
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <Stack direction="column" spacing={1} width={'100%'}>
                  <Stack direction="column" spacing={1} width={"100%"}>
                                        <MemoInput />
                                    </Stack>
                                </Grid>
                            </Grid>
                        </DialogContent>
                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
            <DialogActions
              sx={{
                position: "sticky",
                bottom: 0,
                backgroundColor: "background.paper",
                zIndex: 1000,
              }}
            >
              <Toolbar sx={{ width: "100%", justifyContent: "space-between" }}>
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
@@ -155,7 +169,7 @@
                </Dialog>
            </CreateBase>
        </>
    )
}
  );
};
export default SerialRuleCreate;
rsf-admin/src/page/serialRule/SerialRuleDetail.jsx
New file
@@ -0,0 +1,92 @@
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,
} from "react-admin";
import {
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Stack,
  Grid,
  Box,
} from "@mui/material";
import DialogCloseButton from "../components/DialogCloseButton";
import SerialRuleItem from "../serialRuleItem/index";
const SerialRuleDetail = (props) => {
  const { open, setOpen } = props;
  const translate = useTranslate();
  const notify = useNotify();
  const handleClose = (event, reason) => {
    if (reason !== "backdropClick") {
      setOpen(false);
    }
  };
  const handleSuccess = async (data) => {
    setOpen(false);
    notify("common.response.success");
  };
  const handleError = async (error) => {
    notify(error.message || "common.response.fail", {
      type: "error",
      messageArgs: { _: error.message },
    });
  };
  console.log(SerialRuleItem);
  return (
    <>
      <Dialog
        open={open}
        onClose={handleClose}
        aria-labelledby="form-dialog-title"
        fullWidth
        disableRestoreFocus
        maxWidth="xl" // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
      >
        <DialogTitle
          id="form-dialog-title"
          sx={{
            position: "sticky",
            top: 0,
            backgroundColor: "background.paper",
            zIndex: 1000,
          }}
        >
          {translate("create.title")}
          <Box sx={{ position: "absolute", top: 8, right: 8, zIndex: 1001 }}>
            <DialogCloseButton onClose={handleClose} />
          </Box>
        </DialogTitle>
        <DialogContent sx={{ mt: 2 }}>
          <SerialRuleItem.list />
        </DialogContent>
      </Dialog>
    </>
  );
};
export default SerialRuleDetail;
rsf-admin/src/page/serialRule/SerialRuleList.jsx
@@ -1,5 +1,11 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import React, {
  useState,
  useRef,
  useEffect,
  useMemo,
  useCallback,
} from "react";
import { useNavigate } from "react-router-dom";
import {
    List,
    DatagridConfigurable,
@@ -31,37 +37,41 @@
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
} from "react-admin";
import { Box, Typography, Card, Stack } from "@mui/material";
import { styled } from "@mui/material/styles";
import SerialRuleCreate from "./SerialRuleCreate";
import SerialRuleDetail from "./SerialRuleDetail";
import SerialRulePanel from "./SerialRulePanel";
import EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import MyExportButton from "../components/MyExportButton";
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 {
  PAGE_DRAWER_WIDTH,
  OPERATE_MODE,
  DEFAULT_PAGE_SIZE,
} from "@/config/setting";
import * as Common from "@/utils/common";
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
        height: '.9em'
  "& .css-1vooibu-MuiSvgIcon-root": {
    height: ".9em",
    },
    '& .RaDatagrid-row': {
        cursor: 'auto'
  "& .RaDatagrid-row": {
    cursor: "auto",
    },
    '& .column-name': {
    },
    '& .opt': {
        width: 200
  "& .column-name": {},
  "& .opt": {
    width: 200,
    },
}));
const filters = [
    <SearchInput source="condition" alwaysOn />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
  <DateInput label="common.time.after" source="timeStart" alwaysOn />,
  <DateInput label="common.time.before" source="timeEnd" alwaysOn />,
    <TextInput source="code" label="table.field.serialRule.code" />,
    <TextInput source="name" label="table.field.serialRule.name" />,
@@ -76,18 +86,24 @@
        label="common.field.status"
        source="status"
        choices={[
            { id: '1', name: 'common.enums.statusTrue' },
            { id: '0', name: 'common.enums.statusFalse' },
      { id: "1", name: "common.enums.statusTrue" },
      { id: "0", name: "common.enums.statusFalse" },
        ]}
        resettable
    />,
]
];
const SerialRuleList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
  const [detailDialog, setDetailDialog] = useState(false);
  const navigate = useNavigate();
  const assign = (record) => {
    navigate(`/serialRuleItem?ruleId=${record.id}`);
  };
    return (
        <Box display="flex">
@@ -95,70 +111,130 @@
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
            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.serialRule"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
        empty={
          <EmptyData
            onClick={() => {
              setCreateDialog(true);
            }}
          />
        }
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
        actions={
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='serialRule' />
            <MyCreateButton
              onClick={() => {
                setCreateDialog(true);
              }}
            />
            <SelectColumnsButton preferenceKey="serialRule" />
                        <MyExportButton />
                    </TopToolbar>
                )}
        }
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='serialRule'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
          preferenceKey="serialRule"
          bulkActionButtons={() => (
            <BulkDeleteButton mutationMode={OPERATE_MODE} />
          )}
                    rowClick={(id, resource, record) => false}
                    expand={() => <SerialRulePanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
          omit={["id", "createTime", "createBy", "memo"]}
                >
                    <NumberField source="id" />
                    <TextField source="code" label="table.field.serialRule.code" />
          <MyField
            source="code"
            label="table.field.serialRule.code"
            onClick={(event, record, val) => {
              event.stopPropagation();
              assign(record);
            }}
          />
          {/* <MyField
            source="code"
            label="table.field.serialRule.code"
            onClick={() => {
              setDetailDialog(true);
            }}
          /> */}
                    <TextField source="name" label="table.field.serialRule.name" />
                    <TextField source="delimit" label="table.field.serialRule.delimit" />
                    <TextField source="reset" label="table.field.serialRule.reset" />
                    <TextField source="resetDep" label="table.field.serialRule.resetDep" />
                    <TextField source="currValue" label="table.field.serialRule.currValue" />
                    <TextField source="lastCode" label="table.field.serialRule.lastCode" />
          <TextField
            source="resetDep"
            label="table.field.serialRule.resetDep"
          />
          <TextField
            source="currValue"
            label="table.field.serialRule.currValue"
          />
          <TextField
            source="lastCode"
            label="table.field.serialRule.lastCode"
          />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
          <ReferenceField
            source="updateBy"
            label="common.field.updateBy"
            reference="user"
            link={false}
            sortable={false}
          >
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="updateTime" label="common.field.updateTime" showTime />
                    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
          <DateField
            source="updateTime"
            label="common.field.updateTime"
            showTime
          />
          <ReferenceField
            source="createBy"
            label="common.field.createBy"
            reference="user"
            link={false}
            sortable={false}
          >
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="createTime" label="common.field.createTime" showTime />
                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
          <DateField
            source="createTime"
            label="common.field.createTime"
            showTime
          />
          <BooleanField
            source="statusBool"
            label="common.field.status"
            sortable={false}
          />
                    <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} />
            <EditButton sx={{ padding: "1px", fontSize: ".75rem" }} />
            <DeleteButton
              sx={{ padding: "1px", fontSize: ".75rem" }}
              mutationMode={OPERATE_MODE}
            />
                    </WrapperField>
                </StyledDatagrid>
            </List>
            <SerialRuleCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
      <SerialRuleCreate open={createDialog} setOpen={setCreateDialog} />
      <SerialRuleDetail open={detailDialog} setOpen={setDetailDialog} />
            <PageDrawer
                title='SerialRule Detail'
        title="SerialRule Detail"
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
      ></PageDrawer>
        </Box>
    )
}
  );
};
export default SerialRuleList;
rsf-admin/src/page/serialRuleItem/SerialRuleItemList.jsx
@@ -1,5 +1,11 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import React, {
  useState,
  useRef,
  useEffect,
  useMemo,
  useCallback,
} from "react";
import { useNavigate, useLocation } from "react-router-dom";
import {
    List,
    DatagridConfigurable,
@@ -31,41 +37,48 @@
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
} from "react-admin";
import { Box, Typography, Card, Stack } from "@mui/material";
import { styled } from "@mui/material/styles";
import SerialRuleItemCreate from "./SerialRuleItemCreate";
import SerialRuleItemPanel from "./SerialRuleItemPanel";
import EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import MyExportButton from "../components/MyExportButton";
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 {
  PAGE_DRAWER_WIDTH,
  OPERATE_MODE,
  DEFAULT_PAGE_SIZE,
} from "@/config/setting";
import * as Common from "@/utils/common";
import CustomerTopToolBar from "../components/EditTopToolBar";
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
        height: '.9em'
  "& .css-1vooibu-MuiSvgIcon-root": {
    height: ".9em",
    },
    '& .RaDatagrid-row': {
        cursor: 'auto'
  "& .RaDatagrid-row": {
    cursor: "auto",
    },
    '& .column-name': {
    },
    '& .opt': {
        width: 200
  "& .column-name": {},
  "& .opt": {
    width: 200,
    },
}));
const filters = [
    <SearchInput source="condition" alwaysOn />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
  <DateInput label="common.time.after" source="timeStart" alwaysOn />,
  <DateInput label="common.time.before" source="timeEnd" alwaysOn />,
    <NumberInput source="ruleId" label="table.field.serialRuleItem.ruleId" />,
    <TextInput source="wkType" label="table.field.serialRuleItem.wkType" />,
    <TextInput source="feildValue" label="table.field.serialRuleItem.feildValue" />,
  <TextInput
    source="feildValue"
    label="table.field.serialRuleItem.feildValue"
  />,
    <NumberInput source="len" label="table.field.serialRuleItem.len" />,
    <NumberInput source="lenStr" label="table.field.serialRuleItem.lenStr" />,
    <NumberInput source="sort" label="table.field.serialRuleItem.sort" />,
@@ -75,88 +88,149 @@
        label="common.field.status"
        source="status"
        choices={[
            { id: '1', name: 'common.enums.statusTrue' },
            { id: '0', name: 'common.enums.statusFalse' },
      { id: "1", name: "common.enums.statusTrue" },
      { id: "0", name: "common.enums.statusFalse" },
        ]}
        resettable
    />,
]
];
const SerialRuleItemList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
  const location = useLocation();
  const queryParams = new URLSearchParams(location.search);
  const ruleId = queryParams.get("ruleId");
    return (
    <>
      {ruleId && <CustomerTopToolBar backPrevious={true} />}
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
              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.serialRuleItem"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
          empty={
            <EmptyData
              onClick={() => {
                setCreateDialog(true);
              }}
            />
          }
                filters={filters}
          filter={ruleId ? { ruleId } : undefined}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
          actions={
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='serialRuleItem' />
              <MyCreateButton
                onClick={() => {
                  setCreateDialog(true);
                }}
              />
              <SelectColumnsButton preferenceKey="serialRuleItem" />
                        <MyExportButton />
                    </TopToolbar>
                )}
          }
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='serialRuleItem'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
            preferenceKey="serialRuleItem"
            bulkActionButtons={() => (
              <BulkDeleteButton mutationMode={OPERATE_MODE} />
            )}
                    rowClick={(id, resource, record) => false}
                    expand={() => <SerialRuleItemPanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
            omit={["id", "createTime", "createBy", "memo"]}
                >
                    <NumberField source="id" />
                    <NumberField source="ruleId" label="table.field.serialRuleItem.ruleId" />
                    <TextField source="wkType" label="table.field.serialRuleItem.wkType" />
                    <TextField source="feildValue" label="table.field.serialRuleItem.feildValue" />
            <NumberField
              source="ruleId"
              label="table.field.serialRuleItem.ruleId"
            />
            <TextField
              source="wkType"
              label="table.field.serialRuleItem.wkType"
            />
            <TextField
              source="feildValue"
              label="table.field.serialRuleItem.feildValue"
            />
                    <NumberField source="len" label="table.field.serialRuleItem.len" />
                    <NumberField source="lenStr" label="table.field.serialRuleItem.lenStr" />
                    <NumberField source="sort" label="table.field.serialRuleItem.sort" />
            <NumberField
              source="lenStr"
              label="table.field.serialRuleItem.lenStr"
            />
            <NumberField
              source="sort"
              label="table.field.serialRuleItem.sort"
            />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
            <ReferenceField
              source="updateBy"
              label="common.field.updateBy"
              reference="user"
              link={false}
              sortable={false}
            >
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="updateTime" label="common.field.updateTime" showTime />
                    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
            <DateField
              source="updateTime"
              label="common.field.updateTime"
              showTime
            />
            <ReferenceField
              source="createBy"
              label="common.field.createBy"
              reference="user"
              link={false}
              sortable={false}
            >
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="createTime" label="common.field.createTime" showTime />
                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
                    <TextField source="memo" label="common.field.memo" sortable={false} />
            <DateField
              source="createTime"
              label="common.field.createTime"
              showTime
            />
            <BooleanField
              source="statusBool"
              label="common.field.status"
              sortable={false}
            />
            <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} />
              <EditButton sx={{ padding: "1px", fontSize: ".75rem" }} />
              <DeleteButton
                sx={{ padding: "1px", fontSize: ".75rem" }}
                mutationMode={OPERATE_MODE}
              />
                    </WrapperField>
                </StyledDatagrid>
            </List>
            <SerialRuleItemCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
        <SerialRuleItemCreate open={createDialog} setOpen={setCreateDialog} />
            <PageDrawer
                title='SerialRuleItem Detail'
          title="SerialRuleItem Detail"
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        ></PageDrawer>
        </Box>
    )
}
    </>
  );
};
export default SerialRuleItemList;
rsf-admin/src/page/system/user/UserList.jsx
@@ -1,5 +1,11 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import React, {
  useState,
  useRef,
  useEffect,
  useMemo,
  useCallback,
} from "react";
import { useNavigate } from "react-router-dom";
import {
    List,
    DatagridConfigurable,
@@ -31,67 +37,73 @@
    AutocompleteInput,
    DeleteButton,
    FunctionField,
} from 'react-admin';
import { Box, Typography, Card, Stack, LinearProgress } from '@mui/material';
import { styled } from '@mui/material/styles';
} from "react-admin";
import { Box, Typography, Card, Stack, LinearProgress } from "@mui/material";
import { styled } from "@mui/material/styles";
import UserCreate from "./UserCreate";
import UserPanel from "./UserPanel";
import EmptyData from "@/page/components/EmptyData";
import MyCreateButton from "@/page/components/MyCreateButton";
import MyExportButton from '@/page/components/MyExportButton';
import MyExportButton from "@/page/components/MyExportButton";
import PageDrawer from "@/page/components/PageDrawer";
import MyField from "@/page/components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
import {
  PAGE_DRAWER_WIDTH,
  OPERATE_MODE,
  DEFAULT_PAGE_SIZE,
} from "@/config/setting";
import * as Common from "@/utils/common";
import UserListAside from "./UserListAside";
import RolesField from './RolesField';
import RolesField from "./RolesField";
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
        height: '.9em'
  "& .css-1vooibu-MuiSvgIcon-root": {
    height: ".9em",
    },
    '& .RaDatagrid-row': {
        cursor: 'auto'
  "& .RaDatagrid-row": {
    cursor: "auto",
    },
    '& .column-username': {
        maxWidth: '10em',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
  "& .column-username": {
    maxWidth: "10em",
    overflow: "hidden",
    textOverflow: "ellipsis",
    whiteSpace: "nowrap",
    },
    '& .column-nickname': {
        maxWidth: '10em',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
  "& .column-nickname": {
    maxWidth: "10em",
    overflow: "hidden",
    textOverflow: "ellipsis",
    whiteSpace: "nowrap",
    },
    '& .column-deptId': {
        maxWidth: '10em',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
  "& .column-deptId": {
    maxWidth: "10em",
    overflow: "hidden",
    textOverflow: "ellipsis",
    whiteSpace: "nowrap",
    },
    '& .opt': {
        width: 200
  "& .opt": {
    width: 200,
    },
    '& .column-statusBool': {
        maxWidth: 60
  "& .column-statusBool": {
    maxWidth: 60,
    },
}));
const filters = [
    // <SearchInput source="condition" alwaysOn />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
  <DateInput label="common.time.after" source="timeStart" alwaysOn />,
  <DateInput label="common.time.before" source="timeEnd" alwaysOn />,
    <TextInput source="username" label="table.field.user.username" />,
    <TextInput source="nickname" label="table.field.user.nickname" />,
    <TextInput source="code" label="table.field.user.code" />,
    <SelectInput source="sex" label="table.field.user.sex"
  <SelectInput
    source="sex"
    label="table.field.user.sex"
        choices={[
            { id: 0, name: 'table.field.user.sexes.unknown' },
      { id: 0, name: "table.field.user.sexes.unknown" },
            { id: 1, name: "table.field.user.sexes.male" },
            { id: 2, name: 'table.field.user.sexes.female' },
      { id: 2, name: "table.field.user.sexes.female" },
        ]}
    />,
    <TextInput source="phone" label="table.field.user.phone" />,
@@ -107,23 +119,23 @@
        label="common.field.status"
        source="status"
        choices={[
            { id: '1', name: 'common.enums.statusTrue' },
            { id: '0', name: 'common.enums.statusFalse' },
      { id: "1", name: "common.enums.statusTrue" },
      { id: "0", name: "common.enums.statusFalse" },
        ]}
    />,
]
];
const UserListContent = (props) => {
    const translate = useTranslate();
    const { isLoading } = useListContext();
    return (
        <Box sx={{ position: 'relative' }}>
    <Box sx={{ position: "relative" }}>
            {isLoading && (
                <LinearProgress
                    sx={{
                        height: "2px",
                        position: 'absolute',
            position: "absolute",
                        top: 0,
                        left: 0,
                        right: 0,
@@ -131,16 +143,33 @@
                />
            )}
            <StyledDatagrid
                preferenceKey='user'
                bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
        preferenceKey="user"
        bulkActionButtons={() => (
          <BulkDeleteButton mutationMode={OPERATE_MODE} />
        )}
                rowClick={(id, resource, record) => false}
                expand={() => <UserPanel />}
                expandSingle={true}
                omit={['id', 'email', 'idCard', 'birthday', 'realName', 'updateTime', 'createTime', 'memo']}
        omit={[
          "id",
          "email",
          "idCard",
          "birthday",
          "realName",
          "updateTime",
          "createTime",
          "memo",
        ]}
            >
                <NumberField source="id" />
                <TextField source="username" label="table.field.user.username" />
                <ReferenceField source="deptId" label="table.field.user.deptId" reference="dept" link={false} sortable={false}>
        <ReferenceField
          source="deptId"
          label="table.field.user.deptId"
          reference="dept"
          link={false}
          sortable={false}
        >
                    <TextField source="name" />
                </ReferenceField>
                <TextField source="nickname" label="table.field.user.nickname" />
@@ -151,13 +180,13 @@
                    render={(record) => {
                        switch (record.sex) {
                            case 0:
                                return translate('table.field.user.sexes.unknown');
                return translate("table.field.user.sexes.unknown");
                            case 1:
                                return translate('table.field.user.sexes.male');
                return translate("table.field.user.sexes.male");
                            case 2:
                                return translate('table.field.user.sexes.female');
                return translate("table.field.user.sexes.female");
                            default:
                                return '';
                return "";
                        }
                    }}
                />
@@ -166,19 +195,38 @@
                <TextField source="realName" label="table.field.user.realName" />
                <TextField source="idCard" label="table.field.user.idCard" />
                <TextField source="birthday" label="table.field.user.birthday" />
                <RolesField source="roles" label="table.field.user.role" sortable={false} />
                <DateField source="updateTime" label="common.field.updateTime" showTime />
                <DateField source="createTime" label="common.field.createTime" showTime />
                <BooleanField source="statusBool" label="common.field.status" sortable={false} />
        <RolesField
          source="roles"
          label="table.field.user.role"
          sortable={false}
        />
        <DateField
          source="updateTime"
          label="common.field.updateTime"
          showTime
        />
        <DateField
          source="createTime"
          label="common.field.createTime"
          showTime
        />
        <BooleanField
          source="statusBool"
          label="common.field.status"
          sortable={false}
        />
                <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} />
          <EditButton sx={{ padding: "1px", fontSize: ".75rem" }} />
          <DeleteButton
            sx={{ padding: "1px", fontSize: ".75rem" }}
            mutationMode={OPERATE_MODE}
          />
                </WrapperField>
            </StyledDatagrid>
        </Box>
    )
}
  );
};
const UserList = () => {
    const translate = useTranslate();
@@ -192,40 +240,46 @@
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
            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.user"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
        empty={
          <EmptyData
            onClick={() => {
              setCreateDialog(true);
            }}
          />
        }
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
        actions={
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='user' />
            <MyCreateButton
              onClick={() => {
                setCreateDialog(true);
              }}
            />
            <SelectColumnsButton preferenceKey="user" />
                        <MyExportButton />
                    </TopToolbar>
                )}
        }
                perPage={DEFAULT_PAGE_SIZE}
                aside={<UserListAside />}
            >
                <UserListContent />
            </List>
            <UserCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
      <UserCreate open={createDialog} setOpen={setCreateDialog} />
            <PageDrawer
                title='User Detail'
        title="User Detail"
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
      ></PageDrawer>
        </Box>
    )
}
  );
};
export default UserList;
rsf-admin/src/page/system/user/UserPanel.jsx
@@ -1,11 +1,15 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
import {
    useTranslate,
    useRecordContext,
} from 'react-admin';
  Box,
  Card,
  CardContent,
  Grid,
  Typography,
  Tooltip,
} from "@mui/material";
import { useTranslate, useRecordContext } from "react-admin";
import PanelTypography from "@/page/components/PanelTypography";
import * as Common from '@/utils/common'
import * as Common from "@/utils/common";
const UserPanel = () => {
    const record = useRecordContext();
@@ -13,17 +17,36 @@
    const translate = useTranslate();
    return (
        <>
            <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
      <Card
        sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: "auto" }}
      >
                <CardContent>
                    <Grid container spacing={2}>
                        <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
                            <Typography variant="h6" gutterBottom align="left" sx={{
                                maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
                                whiteSpace: 'nowrap',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}>
                                {Common.camelToPascalWithSpaces(translate('table.field.user.nickname'))}: {record.nickname}
            <Grid
              item
              xs={12}
              sx={{ display: "flex", justifyContent: "space-between" }}
            >
              <Typography
                variant="h6"
                gutterBottom
                align="left"
                sx={{
                  maxWidth: {
                    xs: "100px",
                    sm: "180px",
                    md: "260px",
                    lg: "360px",
                  },
                  whiteSpace: "nowrap",
                  overflow: "hidden",
                  textOverflow: "ellipsis",
                }}
              >
                {Common.camelToPascalWithSpaces(
                  translate("table.field.user.nickname"),
                )}
                : {record.nickname}
                            </Typography>
                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
                            <Typography variant="h6" gutterBottom align="right" >
@@ -33,8 +56,13 @@
                    </Grid>
                    <Grid container spacing={2}>
                        <Grid item xs={12} container alignContent="flex-end">
                            <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
                                {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
              <Typography
                variant="caption"
                color="textSecondary"
                sx={{ wordWrap: "break-word", wordBreak: "break-all" }}
              >
                {Common.camelToPascalWithSpaces(translate("common.field.memo"))}
                :{record.memo}
                            </Typography>
                        </Grid>
                    </Grid>
rsf-admin/src/page/warehouseAreas/WarehouseAreasCreate.jsx
@@ -17,7 +17,7 @@
    useNotify,
    Form,
    useCreateController,
} from 'react-admin';
} from "react-admin";
import {
    Dialog,
    DialogActions,
@@ -26,7 +26,7 @@
    Stack,
    Grid,
    Box,
} from '@mui/material';
} from "@mui/material";
import DialogCloseButton from "../components/DialogCloseButton";
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
@@ -45,11 +45,14 @@
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    notify("common.response.success");
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    notify(error.message || "common.response.fail", {
      type: "error",
      messageArgs: { _: error.message },
    });
    };
    return (
@@ -70,15 +73,19 @@
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
            <DialogTitle
              id="form-dialog-title"
              sx={{
                position: "sticky",
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                backgroundColor: "background.paper",
                zIndex: 1000,
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
              {translate("create.title")}
              <Box
                sx={{ position: "absolute", top: 8, right: 8, zIndex: 1001 }}
              >
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
@@ -88,7 +95,7 @@
                                    <TextInput
                                        label="table.field.warehouseAreas.uuid"
                                        source="uuid"
                                        parse={v => v}
                    parse={(v) => v}
                                        validate={[required()]}
                                        autoFocus
                                    />
@@ -98,7 +105,7 @@
                                        label="table.field.warehouseAreas.name"
                                        source="name"
                                        validate={[required()]}
                                        parse={v => v}
                    parse={(v) => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
@@ -106,14 +113,11 @@
                                        label="table.field.warehouseAreas.code"
                                        source="code"
                                        validate={[required()]}
                                        parse={v => v}
                    parse={(v) => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <ReferenceInput
                                        source="shipperId"
                                        reference="shipper"
                                    >
                  <ReferenceInput source="shipperId" reference="shipper">
                                        <AutocompleteInput
                                            label="table.field.warehouseAreas.shipperId"
                                            optionText="name"
@@ -134,8 +138,8 @@
                                        source="flagMinus"
                                        validate={[required()]}
                                        choices={[
                                            { id: 0, name: '否' },
                                            { id:  1, name: '是' },
                      { id: 0, name: "否" },
                      { id: 1, name: "是" },
                                        ]}
                                    />
                                </Grid>
@@ -145,8 +149,8 @@
                                        source="flagLabelMange"
                                        validate={[required()]}
                                        choices={[
                                            { id: 0, name: ' 否' },
                                            { id:  1, name: ' 是' },
                      { id: 0, name: " 否" },
                      { id: 1, name: " 是" },
                                        ]}
                                    />
                                </Grid>
@@ -156,8 +160,8 @@
                                        source="flagMix"
                                        validate={[required()]}
                                        choices={[
                                            { id: 0, name: '否' },
                                            { id:  1, name: '是' },
                      { id: 0, name: "否" },
                      { id: 1, name: "是" },
                                        ]}
                                    />
                                </Grid>
@@ -166,14 +170,21 @@
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <Stack direction="column" spacing={1} width={'100%'}>
                  <Stack direction="column" spacing={1} width={"100%"}>
                                        <MemoInput />
                                    </Stack>
                                </Grid>
                            </Grid>
                        </DialogContent>
                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
            <DialogActions
              sx={{
                position: "sticky",
                bottom: 0,
                backgroundColor: "background.paper",
                zIndex: 1000,
              }}
            >
              <Toolbar sx={{ width: "100%", justifyContent: "space-between" }}>
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
@@ -181,7 +192,7 @@
                </Dialog>
            </CreateBase>
        </>
    )
}
  );
};
export default WarehouseAreasCreate;