skyouc
2025-03-10 8eaee61fbbdea1eaa294b0f1a7d9f2a10da9ade9
Merge branch 'dev' of http://47.97.1.152:5880/r/wms-master into dev
3个文件已添加
15个文件已修改
9899 ■■■■ 已修改文件
rsf-admin/.env 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/package.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/pnpm-lock.yaml 5197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/App.jsx 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/config/MyDataProvider.js 289 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/layout/MyMenu.jsx 313 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/whMat/WhMatList.jsx 479 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/whMat/WhMatListAside.jsx 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/whMat/whMatCreate.jsx 958 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/whMat/whTable.jsx 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/serialRule/SerialRuleCreate.jsx 308 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/serialRule/SerialRuleDetail.jsx 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/serialRule/SerialRuleList.jsx 368 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/serialRuleItem/SerialRuleItemList.jsx 362 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/user/UserList.jsx 466 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/user/UserPanel.jsx 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasCreate.jsx 351 ●●●● 补丁 | 查看 | 原始文档 | 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 => {
                return (
                  <Resource
                    key={node.id}
                    name={node.component}
                    {...ResourceContent(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 = () => (
@@ -103,4 +107,4 @@
  </StoreContextProvider>
);
export default AppWrapper;
export default AppWrapper;
rsf-admin/src/config/MyDataProvider.js
@@ -1,148 +1,151 @@
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 ***
  // *** 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);
        const { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
                data: data.records,
                total: data.total
            });
        }
        return Promise.reject(new Error(msg));
    },
    // get a single record by id
    getOne: async (resource, params) => {
        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({
                data: {
                    id: data.id,
                    ...data,
                },
            });
        }
        return Promise.reject(new Error(msg));
    },
    // 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);
        const { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
                data: data.map(item => ({
                    id: item.id,
                    ...item,
                })),
            });
        }
        return Promise.reject(new Error(msg));
    },
    // get the records referenced to another record, e.g. comments for a post
    getManyReference: async (resource, params) => {
        console.log("getManyReference", resource, params);
        return Promise.resolve();
    },
    // create a record
    create: async (resource, params) => {
        console.log("create", resource, params);
        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
                },
            });
        }
        return Promise.reject(new Error(msg));
    },
    // 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 { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
                data: data,
            });
        }
        return Promise.reject(new Error(msg));
    },
    // update a list of records based on an array of ids and a common patch
    updateMany: async (resource, params) => {
        console.log("updateMany", resource, params);
        const res = await request.post(
            resource + '/update/many'
            , params.ids.map(id => ({ id, ...params.data }))
        );
        const { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
                data: data,
            });
        }
        return Promise.reject(new Error(msg));
    },
    // delete a record by id
    delete: async (resource, params) => {
        console.log("delete", resource, params);
        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
                },
            });
        }
        return Promise.reject(new Error(msg));
    },
    // 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 { code, msg, data } = res.data;
        if (code === 200) {
            return Promise.resolve({
                data: data,
            });
        }
        return Promise.reject(new Error(msg));
    },
    // export excel from all data
    export: async (resource, params) => {
        console.log("export", resource, params);
        const _params = Common.integrateParams(params);
        try {
            const res = await request.post(`${resource}/export`, _params, {
                responseType: 'blob',
            });
            return res;
        } catch (error) {
            return Promise.reject(new Error(error));
        }
  // 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);
    const { code, msg, data } = res.data;
    if (code === 200) {
      return Promise.resolve({
        data: data.records,
        total: data.total,
      });
    }
}
    return Promise.reject(new Error(msg));
  },
export default MyDataProvider;
  // get a single record by id
  getOne: async (resource, params) => {
    // 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({
        data: {
          id: data.id,
          ...data,
        },
      });
    }
    return Promise.reject(new Error(msg));
  },
  // 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);
    const { code, msg, data } = res.data;
    if (code === 200) {
      return Promise.resolve({
        data: data.map((item) => ({
          id: item.id,
          ...item,
        })),
      });
    }
    return Promise.reject(new Error(msg));
  },
  // get the records referenced to another record, e.g. comments for a post
  getManyReference: async (resource, params) => {
    console.log("getManyReference", resource, params);
    return Promise.resolve();
  },
  // create a record
  create: async (resource, params) => {
    console.log("create", resource, params);
    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,
        },
      });
    }
    return Promise.reject(new Error(msg));
  },
  // 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 { code, msg, data } = res.data;
    if (code === 200) {
      return Promise.resolve({
        data: data,
      });
    }
    return Promise.reject(new Error(msg));
  },
  // update a list of records based on an array of ids and a common patch
  updateMany: async (resource, params) => {
    console.log("updateMany", resource, params);
    const res = await request.post(
      resource + "/update/many",
      params.ids.map((id) => ({ id, ...params.data })),
    );
    const { code, msg, data } = res.data;
    if (code === 200) {
      return Promise.resolve({
        data: data,
      });
    }
    return Promise.reject(new Error(msg));
  },
  // delete a record by id
  delete: async (resource, params) => {
    console.log("delete", resource, params);
    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,
        },
      });
    }
    return Promise.reject(new Error(msg));
  },
  // 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 { code, msg, data } = res.data;
    if (code === 200) {
      return Promise.resolve({
        data: data,
      });
    }
    return Promise.reject(new Error(msg));
  },
  // export excel from all data
  export: async (resource, params) => {
    console.log("export", resource, params);
    const _params = Common.integrateParams(params);
    try {
      const res = await request.post(`${resource}/export`, _params, {
        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
@@ -1,168 +1,173 @@
import React, { useState, useEffect, useMemo, Suspense } from "react";
import {
    useTranslate,
    DashboardMenuItem,
    MenuItemLink,
    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';
  useTranslate,
  DashboardMenuItem,
  MenuItemLink,
  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";
const getIconComponent = (iconStr) => {
    return Icons[iconStr] || HorizontalRuleIcon;
  return Icons[iconStr] || HorizontalRuleIcon;
};
export const MyMenu = ({ dense = false }) => {
    const [state, setState] = useState({});
    const translate = useTranslate();
    const location = useLocation();
    const [sidebarIsOpen] = useSidebarState();
    const { isPending, permissions } = usePermissions();
  const [state, setState] = useState({});
  const translate = useTranslate();
  const location = useLocation();
  const [sidebarIsOpen] = useSidebarState();
  const { isPending, permissions } = usePermissions();
    useEffect(() => {
        // default open sub menu
        const defaultExpandMenu = ["menu.system", "menu.dispatcher", "menu.equipment"];
        permissions?.forEach(item => {
            if (defaultExpandMenu.includes(item.name)) {
                setState(state => ({ ...state, [item.route]: true }));
            }
        });
    }, [permissions]);
  useEffect(() => {
    // default open sub menu
    const defaultExpandMenu = [
      "menu.system",
      "menu.dispatcher",
      "menu.equipment",
    ];
    permissions?.forEach((item) => {
      if (defaultExpandMenu.includes(item.name)) {
        setState((state) => ({ ...state, [item.route]: true }));
      }
    });
  }, [permissions]);
    useEffect(() => {
        // expand this parent menu
        const currentPath = location.pathname;
        const parentRoutes = findParentRoutes(currentPath, permissions)
        for (const parentRoute of parentRoutes) {
            setState(state => ({ ...state, [parentRoute]: true }));
  useEffect(() => {
    // expand this parent menu
    const currentPath = location.pathname;
    const parentRoutes = findParentRoutes(currentPath, permissions);
    for (const parentRoute of parentRoutes) {
      setState((state) => ({ ...state, [parentRoute]: true }));
    }
  }, [location.pathname]);
  const handleToggle = (menu) => {
    setState((state) => ({ ...state, [menu]: !state[menu] }));
  };
  const getIcon = (iconStr) => {
    const IconComponent = getIconComponent(iconStr);
    if (IconComponent) {
      return <IconComponent />;
    }
  };
  const generateMenu = (permissions) => {
    return permissions.map((node) => {
      if (node.children) {
        return (
          <SubMenu
            key={node.id}
            handleToggle={() => handleToggle(node.route)}
            isOpen={state[node.route]}
            name={node.name}
            dense={dense}
            icon={getIcon(node.icon)}
          >
            {generateMenu(node.children)}
          </SubMenu>
        );
      } else {
        if (node.component) {
          return (
            <MenuItemLink
              key={node.id}
              to={node.component} // correspond to Resource.name
              state={{ _scrollToTop: true }}
              // primaryText={translate(`resources.orders.name`, {
              //     smart_count: 2,
              // })}
              primaryText={node.name}
              leftIcon={getIcon(node.icon)}
              dense={dense}
            />
          );
        }
      }
    });
  };
    }, [location.pathname]);
    const handleToggle = (menu) => {
        setState(state => ({ ...state, [menu]: !state[menu] }));
    };
    const getIcon = (iconStr) => {
        const IconComponent = getIconComponent(iconStr);
        if (IconComponent) {
            return <IconComponent />;
        }
    };
    const generateMenu = (permissions) => {
        return permissions.map((node) => {
            if (node.children) {
                return (
                    <SubMenu
                        key={node.id}
                        handleToggle={() => handleToggle(node.route)}
                        isOpen={state[node.route]}
                        name={node.name}
                        dense={dense}
                        icon={getIcon(node.icon)}
                    >
                        {generateMenu(node.children)}
                    </SubMenu>
                );
            } else {
                if (node.component) {
                    return (
                        <MenuItemLink
                            key={node.id}
                            to={node.component} // correspond to Resource.name
                            state={{ _scrollToTop: true }}
                            // primaryText={translate(`resources.orders.name`, {
                            //     smart_count: 2,
                            // })}
                            primaryText={node.name}
                            leftIcon={getIcon(node.icon)}
                            dense={dense}
                        />
                    );
                }
            }
        });
    };
    return isPending
        ? (<div>Waiting for permissions...</div>) :
        (
            <Box
                sx={{
                    width: sidebarIsOpen ? 200 : 50,
                    marginTop: 1,
                    marginBottom: 1,
                    transition: theme =>
                        theme.transitions.create('width', {
                            easing: theme.transitions.easing.sharp,
                            duration: theme.transitions.duration.leavingScreen,
                        }),
                }}
            >
                <Menu.Item
                    to="/dashboard"
                    primaryText="menu.dashboard"
                    leftIcon={<DashboardIcon />}
                />
                {permissions && (generateMenu(permissions))}
                {/* <Menu.ResourceItems /> */}
                <Menu.Item
                    to="/settings"
                    primaryText="menu.settings"
                    leftIcon={<PersonIcon />}
                />
            </Box>
        )
}
  return isPending ? (
    <div>Waiting for permissions...</div>
  ) : (
    <Box
      sx={{
        width: sidebarIsOpen ? 200 : 50,
        marginTop: 1,
        marginBottom: 1,
        transition: (theme) =>
          theme.transitions.create("width", {
            easing: theme.transitions.easing.sharp,
            duration: theme.transitions.duration.leavingScreen,
          }),
      }}
    >
      <Menu.Item
        to="/dashboard"
        primaryText="menu.dashboard"
        leftIcon={<DashboardIcon />}
      />
      {permissions && generateMenu(permissions)}
      {/* <Menu.ResourceItems /> */}
      <Menu.Item
        to="/settings"
        primaryText="menu.settings"
        leftIcon={<PersonIcon />}
      />
    </Box>
  );
};
const findParentRoutes = (pathname, permissions) => {
    if (!pathname || !permissions) {
        return [];
    }
    const findMenu = (currentPermissions, path) => {
        for (const item of currentPermissions) {
            if (item.component === path) {
                return item;
            }
            if (item.children) {
                const found = findMenu(item.children, path);
                if (found) {
                    return found;
                }
            }
        }
        return null;
    };
    const findParentRoutesRecursive = (item, allPermissions) => {
        const parentRoutes = [];
        let current = item;
        while (current && current.parentId) {
            const parent = allPermissions.find(permission => permission.id === current.parentId);
            if (parent) {
                parentRoutes.push(parent.route);
                current = parent;
            } else {
                break;
            }
        }
        return parentRoutes;
    };
    const currentMenu = findMenu(permissions, pathname.replace("/", ""));
    if (currentMenu) {
        return findParentRoutesRecursive(currentMenu, permissions);
    }
  if (!pathname || !permissions) {
    return [];
};
  }
  const findMenu = (currentPermissions, path) => {
    for (const item of currentPermissions) {
      if (item.component === path) {
        return item;
      }
      if (item.children) {
        const found = findMenu(item.children, path);
        if (found) {
          return found;
        }
      }
    }
    return null;
  };
  const findParentRoutesRecursive = (item, allPermissions) => {
    const parentRoutes = [];
    let current = item;
    while (current && current.parentId) {
      const parent = allPermissions.find(
        (permission) => permission.id === current.parentId,
      );
      if (parent) {
        parentRoutes.push(parent.route);
        current = parent;
      } else {
        break;
      }
    }
    return parentRoutes;
  };
  const currentMenu = findMenu(permissions, pathname.replace("/", ""));
  if (currentMenu) {
    return findParentRoutesRecursive(currentMenu, permissions);
  }
  return [];
};
rsf-admin/src/page/basicInfo/whMat/WhMatList.jsx
@@ -1,237 +1,304 @@
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 [createDialog, setCreateDialog] = React.useState(false);
    const [editRecord, setEditRecord] = React.useState(null);
    const treeData = [
  const translate = useTranslate();
  const [searchVal, setSearchVal] = useState("");
  const [createDialog, setCreateDialog] = React.useState(false);
  const [editRecord, setEditRecord] = React.useState(null);
  const treeData = [
    {
      id: "19",
      label: "半成品",
      secondaryLabel: "RM001",
      editable: true,
      children: [
        {
            id: '19',
            label: '半成品',
            secondaryLabel: 'RM001',
            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-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: '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: "18",
      label: "原材料",
      primaryText: "半成品",
      secondaryText: "RM001",
    },
    {
      id: "charts",
      label: "Charts",
      primaryText: "半成品",
      secondaryText: "RM001",
      children: [
        {
            id: 'tree-view',
            label: 'Tree View',
            primaryText: '半成品',
            secondaryLabel: 'RM001',
            children: [{
                id: 'tree-view-community', primaryText: '半成品',
                secondaryText: 'RM001', label: '@mui/x-tree-view'
            }],
          id: "charts-community",
          primaryText: "半成品",
          secondaryText: "RM001",
          label: "@mui/x-charts",
        },
      ],
    },
    {
      id: "tree-view",
      label: "Tree View",
      primaryText: "半成品",
      secondaryLabel: "RM001",
      children: [
        {
            id: 'tree-view2',
            label: 'Tree View3',
            primaryText: '半成品',
            secondaryText: 'RM001',
            children: [{
                id: 'tree-view-community1', primaryText: '半成品',
                secondaryText: 'RM001', label: '@mui/x-tree-view'
            }],
          id: "tree-view-community",
          primaryText: "半成品",
          secondaryText: "RM001",
          label: "@mui/x-tree-view",
        },
    ];
    const handleNodeSelect = (event, nodeId) => {
        console.log('Selected Node ID:', nodeId);
        // 在这里可以根据 nodeId 更新主内容区域
    };
    const handleSearch = () => {
        console.log('Search Input:', selectedOption);
    };
      ],
    },
    {
      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);
    // 在这里可以根据 nodeId 更新主内容区域
  };
  const handleSearch = () => {
    console.log("Search Input:", selectedOption);
  };
    const CustomCheckbox = React.forwardRef(function CustomCheckbox(props, ref) {
        return <input type="checkbox" ref={ref} {...props} />;
    });
    function CustomLabel({ children, className, secondaryLabel }) {
        return (
            <Box display={"flex"} alignItems={"end"}>
                <Typography fontWeight={500}>{children}</Typography>
                <Box width={10}></Box>
                {secondaryLabel && (
                    <Typography variant="caption" color="secondary">
                        {secondaryLabel}
                    </Typography>
                )}
            </Box>
        );
  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);
    }
    const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) {
        const { publicAPI } = useTreeItem2Utils({
            itemId: props.itemId,
            children: props.children,
        });
  };
        const item = publicAPI.getItem(props.itemId);
  React.useEffect(() => {
    getMatnrList();
  }, []);
        return (
            <TreeItem2
                {...props}
                ref={ref}
                slots={{
                    label: CustomLabel,
                }}
                slotProps={{
                    label: { secondaryLabel: item?.secondaryLabel || '' },
                }}
            />
        );
  function CustomLabel({ children, className, secondaryLabel }) {
    return (
      <Box display={"flex"} alignItems={"end"}>
        <Typography fontWeight={500}>{children}</Typography>
        <Box width={10}></Box>
        {secondaryLabel && (
          <Typography variant="caption" color="secondary">
            {secondaryLabel}
          </Typography>
        )}
      </Box>
    );
  }
  const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) {
    const { publicAPI } = useTreeItem2Utils({
      itemId: props.itemId,
      children: props.children,
    });
    const isLoading = false;
    React.useEffect(() => {
        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');
            });
    }, [searchVal])
    const handleAdd = () => {
        setCreateDialog(true);
    };
    const item = publicAPI.getItem(props.itemId);
    return (
        <>
            <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}
                >
                    <TextField
                        label={translate('ra.action.search')}
                        value={searchVal}
                        onChange={(e) => handleInput(e.target.value)}
                    />
                </Box>
                <Box>
                    <Button
                        variant="outlined"
                        color="primary"
                        startIcon={<Add />}
                        onClick={handleAdd}
                    >
                        {translate('ra.action.add')}
                    </Button>
                    <Button
                        variant="outlined"
                        color="error"
                        startIcon={<Delete />}
                        sx={{ ml: 1 }}
                    >
                        {translate('ra.action.delete')}
                    </Button>
                    <Button
                        variant="outlined"
                        color="primary"
                        sx={{ ml: 1 }}
                        startIcon={<Save />}
                    >
                        {translate('ra.action.save')}
                    </Button>
                </Box>
            </Box>
      <TreeItem2
        {...props}
        ref={ref}
        slots={{
          label: CustomLabel,
        }}
        slotProps={{
          label: { secondaryLabel: item?.secondaryLabel || "" },
        }}
      />
    );
  });
            <Card>
                <CardContent>
                    <RichTreeView
                        defaultExpandedItems={['grid', 'pickers']}
                        checkboxSelection
                        items={treeData}
                        slots={{ item: CustomTreeItem }}
                        onItemClick={handleNodeSelect} // 监听节点点击事件
                    />
                </CardContent>
            </Card>
        </>
    )
}
  const isLoading = false;
  React.useEffect(() => {
    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");
      });
  }, [searchVal]);
  const handleAdd = () => {
    setCreateDialog(true);
  };
  return (
    <>
      <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}>
          <TextField
            label={translate("ra.action.search")}
            value={searchVal}
            onChange={(e) => handleInput(e.target.value)}
          />
        </Box>
        <Box>
          <Button
            variant="outlined"
            color="primary"
            startIcon={<Add />}
            onClick={handleAdd}
          >
            {translate("ra.action.add")}
          </Button>
          <Button
            variant="outlined"
            color="error"
            startIcon={<Delete />}
            sx={{ ml: 1 }}
          >
            {translate("ra.action.delete")}
          </Button>
          <Button
            variant="outlined"
            color="primary"
            sx={{ ml: 1 }}
            startIcon={<Save />}
          >
            {translate("ra.action.save")}
          </Button>
        </Box>
      </Box>
      <Card>
        <CardContent>
          <RichTreeView
            defaultExpandedItems={["grid", "pickers"]}
            checkboxSelection
            items={treeData}
            slots={{ item: CustomTreeItem }}
            onItemClick={handleNodeSelect} // 监听节点点击事件
          />
        </CardContent>
      </Card>
    </>
  );
};
const WhMatList = () => {
    const translate = useTranslate();
    return (
        <>
            <Box sx={{
                display: 'flex',
                marginBottom: 24
            }}>
                <Title title={TITLE} />
                <Box>
                    <WhMatListAside />
                </Box>
                <Box sx={{ flexGrow: 1 }}>
                    <WhMatListContent />
                </Box>
            </Box>
        </>
    )
}
export default WhMatList;
  const translate = useTranslate();
  return (
    <>
      <Box
        sx={{
          display: "flex",
          marginBottom: 24,
        }}
      >
        <Title title={TITLE} />
        <Box>
          <WhMatListAside />
        </Box>
        <Box sx={{ flexGrow: 1 }}>
          <WhMatListContent />
        </Box>
      </Box>
    </>
  );
};
export default WhMatList;
rsf-admin/src/page/basicInfo/whMat/WhMatListAside.jsx
@@ -1,86 +1,107 @@
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 { control, getValues } = useForm();
    const [map, setMap] = useState([
        {
            id: 1,
            name: '仓库1',
            code: 'WH1',
            icon: 'Warehouse',
            locCount: 350,
        },
        {
            id: 2,
            name: '仓库2',
            code: 'WH1',
            icon: 'Warehouse',
            locCount: 237,
        },
        {
            id: 3,
            name: '仓库3',
            code: 'WH1',
            icon: 'Warehouse',
            locCount: 590,
        }
    ]);
    const handleInput = (value) => {
        console.log(value);
        setSearchVal(value);
  const theme = useTheme();
  const translate = useTranslate();
  const [searchVal, setSearchVal] = useState("");
  const { control, getValues } = useForm();
  const notify = useNotify();
  const [map, setMap] = useState([
    {
      id: 1,
      name: "仓库1",
      code: "WH1",
      icon: "Warehouse",
      locCount: 350,
    },
    {
      id: 2,
      name: "仓库2",
      code: "WH1",
      icon: "Warehouse",
      locCount: 237,
    },
    {
      id: 3,
      name: "仓库3",
      code: "WH1",
      icon: "Warehouse",
      locCount: 590,
    },
  ]);
  const handleInput = (value) => {
    console.log(value);
    setSearchVal(value);
  };
  const getMatnrList = async () => {
    const {
      data: { code, data, msg },
    } = await request.post("/warehouse/list", {}).then();
    if (code === 200) {
      console.log(data);
    } else {
      notify(msg);
    }
    React.useEffect(() => {
        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');
            });
    }, [searchVal])
  React.useEffect(() => {
    getMatnrList();
  }, [searchVal]);
    return (
        <Card
            sx={{
                order: -1,
                mr: 2,
                mt: 4,
                width: 250
            }}
        >
            <CardContent>
                <div>
                    <TextField
                        label={translate('ra.action.search')}
                        value={searchVal}
                        onChange={(e) => handleInput(e.target.value)}
  return (
    <Card
      sx={{
        order: -1,
        mr: 2,
        mt: 4,
        width: 250,
      }}
    >
      <CardContent>
        <div>
          <TextField
            label={translate("ra.action.search")}
            value={searchVal}
            onChange={(e) => handleInput(e.target.value)}
          />
        </div>
        <List>
          {map.map((record) => (
            <Warehouse key={record.id} record={record} />
          ))}
        </List>
      </CardContent>
    </Card>
  );
};
                    />
                </div>
                <List>
                    {map.map(record => (
                        <Warehouse key={record.id} record={record} />
                    ))}
                </List>
            </CardContent>
        </Card>
    )
}
export default WhMatListAside;
export default WhMatListAside;
rsf-admin/src/page/basicInfo/whMat/whMatCreate.jsx
@@ -1,396 +1,634 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    Form,
    useTranslate,
    Toolbar,
    SaveButton,
    TextInput,
    ReferenceInput,
    AutocompleteInput,
    required,
    NumberInput,
} from 'react-admin';
  CreateBase,
  Form,
  useTranslate,
  Toolbar,
  SaveButton,
  TextInput,
  ReferenceInput,
  AutocompleteInput,
  required,
  NumberInput,
} from "react-admin";
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Stack,
    Grid,
    Box,
    TableRow,
    IconButton,
    Table,
    TableBody,
    TableCell,
    TableContainer,
    TableHead,
    Paper,
    Checkbox,
    Button
} from '@mui/material';
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Stack,
  Grid,
  Box,
  TableRow,
  IconButton,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  Paper,
  Checkbox,
  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',
        minWidth: 160,
    },
    {
        id: 'matnrName',
        label: 'table.field.whMat.matnrName',
    },
    {
        id: 'matnrGroupId',
        label: 'table.field.whMat.matnrGroupId',
    },
    {
        id: 'spec',
        label: 'table.field.whMat.spec',
    },
    {
        id: 'color',
        label: 'table.field.whMat.color',
    },
    {
        id: 'size',
        label: 'table.field.whMat.size',
    },
    {
        id: 'minWeight',
        label: 'table.field.whMat.minWeight',
    },
    {
        id: 'maxWeight',
        label: 'table.field.whMat.maxWeight',
    },
  {
    id: "matnrCode",
    label: "table.field.whMat.matnrCode",
    minWidth: 160,
  },
  {
    id: "matnrName",
    label: "table.field.whMat.matnrName",
  },
  {
    id: "matnrGroupId",
    label: "table.field.whMat.matnrGroupId",
  },
  {
    id: "spec",
    label: "table.field.whMat.spec",
  },
  {
    id: "color",
    label: "table.field.whMat.color",
  },
  {
    id: "size",
    label: "table.field.whMat.size",
  },
  {
    id: "minWeight",
    label: "table.field.whMat.minWeight",
  },
  {
    id: "maxWeight",
    label: "table.field.whMat.maxWeight",
  },
];
const WhMatCreate = (props) => {
    const translate = useTranslate();
    const { editRecord, open, setOpen, callback, resource } = props;
  const translate = useTranslate();
  const { editRecord, open, setOpen, callback, resource } = props;
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
  const handleClose = (event, reason) => {
    if (reason !== "backdropClick") {
      setOpen(false);
    }
  };
    const onSubmit = (data) => {
        const _params = { ...data };
    };
  const onSubmit = (data) => {
    const _params = { ...data };
  };
    return (
        <>
            <CreateBase>
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="lg"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form record={editRecord} onSubmit={onSubmit}>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                            {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' }}  >
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
                    </Form>
                </Dialog>
            </CreateBase>
        </>
    );
  return (
    <>
      <CreateBase>
        <Dialog
          open={open}
          onClose={handleClose}
          aria-labelledby="form-dialog-title"
          fullWidth
          disableRestoreFocus
          maxWidth="lg" // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
        >
          <Form record={editRecord} onSubmit={onSubmit}>
            <DialogTitle
              id="form-dialog-title"
              sx={{
                position: "sticky",
                top: 0,
                backgroundColor: "background.paper",
                zIndex: 1000,
              }}
            >
              {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" }}>
                <SaveButton />
              </Toolbar>
            </DialogActions>
          </Form>
        </Dialog>
      </CreateBase>
    </>
  );
};
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'
    },
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    whiteSpace: 'nowrap',
    maxWidth: 600,
  "& .MuiButtonBase-root": {
    padding: "0px 0px",
  },
  overflow: "hidden",
  textOverflow: "ellipsis",
  whiteSpace: "nowrap",
  maxWidth: 600,
}));
const TreeTableRow = (props) => {
    const { row, depth = 0, openNodes, setOpenNodes, onEdit, onDelete } = props;
    const isOpen = openNodes[row.id] || false;
    const [checked, setChecked] = useState(false);
    const toggleNode = (id) => {
        setOpenNodes(prevState => ({ ...prevState, [id]: !prevState[id] }));
    };
    return (
        <React.Fragment>
            <StyledTableRow hover tabIndex={-1} key={row.id}>
                <StyledTableCell sx={{ padding: 0, width: 20 }}>
                    {row.children && (
                        <IconButton
                            aria-label="expand row"
                            size="small"
                            style={{ marginLeft: (depth * 16 + 8) }}
                            onClick={() => toggleNode(row.id)}
                        >
                            {isOpen ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
                        </IconButton>
                    )}
  const { row, depth = 0, openNodes, setOpenNodes, onEdit, onDelete } = props;
  const isOpen = openNodes[row.id] || false;
  const [checked, setChecked] = useState(false);
  const toggleNode = (id) => {
    setOpenNodes((prevState) => ({ ...prevState, [id]: !prevState[id] }));
  };
  return (
    <React.Fragment>
      <StyledTableRow hover tabIndex={-1} key={row.id}>
        <StyledTableCell sx={{ padding: 0, width: 20 }}>
          {row.children && (
            <IconButton
              aria-label="expand row"
              size="small"
              style={{ marginLeft: depth * 16 + 8 }}
              onClick={() => toggleNode(row.id)}
            >
              {isOpen ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
            </IconButton>
          )}
        </StyledTableCell>
        <StyledTableCell style={{ paddingLeft: depth * 16 + 16 }}>
          <Checkbox
            key={row.id}
            checked={row.checked}
            // onChange={() => { row.checked = !row.checked; }}
          />
        </StyledTableCell>
        {columns.map((column, idx) => {
          if (column.id !== "actions") {
            const value = row[column.id];
            return (
              <>
                <StyledTableCell
                  key={column.id}
                  align={column.align || "left"}
                  // style={{ paddingLeft: idx === 0 && (depth * 16 + 16) }}
                  onClick={() => toggleNode(row.id)}
                >
                  {column.format ? column.format(value) : value}
                </StyledTableCell>
                <StyledTableCell style={{ paddingLeft: (depth * 16 + 16) }}>
                    <Checkbox
                        key={row.id}
                        checked={row.checked}
                        // onChange={() => { row.checked = !row.checked; }}
                    />
                </StyledTableCell>
                {columns.map((column, idx) => {
                    if (column.id !== 'actions') {
                        const value = row[column.id];
                        return (
                            <>
                                <StyledTableCell
                                    key={column.id}
                                    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.map((child) => (
                    <TreeTableRow
                        key={child.id}
                        row={child}
                        depth={depth + 1}
                        openNodes={openNodes}
                        setOpenNodes={setOpenNodes}
                    />
                ))
            )}
        </React.Fragment>
    );
              </>
            );
          }
        })}
      </StyledTableRow>
      {row.children &&
        isOpen &&
        row.children.map((child) => (
          <TreeTableRow
            key={child.id}
            row={child}
            depth={depth + 1}
            openNodes={openNodes}
            setOpenNodes={setOpenNodes}
          />
        ))}
    </React.Fragment>
  );
};
const WhMatCreateContent = (props) => {
    const translate = useTranslate();
    const [selAll, setSelAll] = useState(false);
    const [treeData, setTreeData] = useState([
  const translate = useTranslate();
  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: 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: 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: 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: 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");
  };
  const selectAll = () => {
    selAll ? setSelAll(false) : setSelAll(true);
  };
    ]);
    const [openNodes, setOpenNodes] = React.useState({});
  useEffect(() => {
    console.log(selAll);
    const handleSearch = () => {
        console.log('handleSearch');
    }
    const selectAll = () => {
        selAll ? setSelAll(false) : setSelAll(true);
    }
    useEffect(() => {
        console.log(selAll);
        const setAllChecked = (nodes, checked) => {
            nodes.forEach(node => {
                node.checked = !checked;
                if (node.children) {
                    setAllChecked(node.children, checked);
                }
            });
        };
        if (selAll) {
            setAllChecked(treeData, true);
        } else {
            setAllChecked(treeData, false);
    const setAllChecked = (nodes, checked) => {
      nodes.forEach((node) => {
        node.checked = !checked;
        if (node.children) {
          setAllChecked(node.children, checked);
        }
    }, [selAll]);
      });
    };
    return (
        <>
            <Grid container rowSpacing={2} columnSpacing={2}>
                <Grid item xs={3} display="flex" gap={1}>
                    <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"
                    />
                </Grid>
                <Grid item xs={3} display="flex" gap={1}>
                    <ReferenceInput
                        source="groupId"
                        reference="matnrGroup"
                    >
                        <AutocompleteInput
                            label="table.field.whMat.matnrGroupId"
                            optionText="name"
                            filterToQuery={(val) => ({ name: val })}
                        />
                    </ReferenceInput>
                </Grid>
                <Grid item xs={3} display="flex" gap={1}>
                    <TextInput
                        label="table.field.whMat.spec"
                        source="spec"
                    />
                </Grid>
    if (selAll) {
      setAllChecked(treeData, true);
    } else {
      setAllChecked(treeData, false);
    }
  }, [selAll]);
                <Grid item xs={3} display="flex" gap={1}>
                    <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"
                    />
                </Grid>
                <Grid item xs={3} display="flex" gap={1}>
                    <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"
                    />
                </Grid>
            </Grid>
            <Box sx={{ mt: 2, mb: 2 }}>
                <Stack direction="row" spacing={1} justifyContent="flex-end">
                    <Button
                        variant="outlined"
                        color="primary"
                        startIcon={<Search />}
                        onClick={handleSearch}
                    >
                        {translate('ra.action.search')}
                    </Button>
                </Stack>
            </Box>
            <TableContainer component={Paper}>
                <Table size="small">
                    <TableHead>
                        <TableRow>
                            <StyledTableCell sx={{ padding: 0, width: 0 }} />
                            <StyledTableCell sx={{ width: 0 }} >
                                <Checkbox checked={selAll} onClick={selectAll} />
                            </StyledTableCell>
                            {columns.map((column, idx) => (
                                <StyledTableCell
                                    key={idx}
                                    align={column.align || 'left'}
                                    style={{
                                        minWidth: column.minWidth,
                                    }}
                                >
                                    {translate(column.label)}
                                </StyledTableCell>
                            ))}
                        </TableRow>
                    </TableHead>
                    <TableBody>
                        {treeData && treeData.length > 0 && (
                            treeData.map((row) => (
                                <TreeTableRow
                                    key={row.id}
                                    row={row}
                                    openNodes={openNodes}
                                    setOpenNodes={setOpenNodes}
                                />
                            ))
                        )}
                    </TableBody>
                </Table>
            </TableContainer>
        </>
  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" />
        </Grid>
        <Grid item xs={3} display="flex" gap={1}>
          <TextInput label="table.field.whMat.matnrName" source="matnrName" />
        </Grid>
        <Grid item xs={3} display="flex" gap={1}>
          <ReferenceInput source="groupId" reference="matnrGroup">
            <AutocompleteInput
              label="table.field.whMat.matnrGroupId"
              optionText="name"
              filterToQuery={(val) => ({ name: val })}
            />
          </ReferenceInput>
        </Grid>
        <Grid item xs={3} display="flex" gap={1}>
          <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" />
        </Grid>
        <Grid item xs={3} display="flex" gap={1}>
          <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" />
        </Grid>
        <Grid item xs={3} display="flex" gap={1}>
          <TextInput label="table.field.whMat.maxWeight" source="maxWeight" />
        </Grid>
      </Grid>
      <Box sx={{ mt: 2, mb: 2 }}>
        <Stack direction="row" spacing={1} justifyContent="flex-end">
          <Button
            variant="outlined"
            color="primary"
            startIcon={<Search />}
            onClick={handleSearch}
          >
            {translate("ra.action.search")}
          </Button>
        </Stack>
      </Box>
      <TableContainer component={Paper}>
        <Table size="small">
          <TableHead>
            <TableRow>
              <StyledTableCell sx={{ padding: 0, width: 0 }} />
              <StyledTableCell sx={{ width: 0 }}>
                <Checkbox checked={selAll} onClick={selectAll} />
              </StyledTableCell>
              {columns.map((column, idx) => (
                <StyledTableCell
                  key={idx}
                  align={column.align || "left"}
                  style={{
                    minWidth: column.minWidth,
                  }}
                >
                  {translate(column.label)}
                </StyledTableCell>
              ))}
            </TableRow>
          </TableHead>
          <TableBody>
            {treeData &&
              treeData.length > 0 &&
              treeData.map((row) => (
                <TreeTableRow
                  key={row.id}
                  row={row}
                  openNodes={openNodes}
                  setOpenNodes={setOpenNodes}
                />
              ))}
          </TableBody>
        </Table>
      </TableContainer>
      <WhTable data={treeDatas} />
    </>
  );
};
export default WhMatCreate;
export default WhMatCreate;
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/system/serialRule/SerialRuleCreate.jsx
@@ -1,161 +1,175 @@
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';
  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 StatusSelectInput from "../../components/StatusSelectInput";
import MemoInput from "../../components/MemoInput";
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Stack,
  Grid,
  Box,
} from "@mui/material";
import DialogCloseButton from "../components/DialogCloseButton";
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
const SerialRuleCreate = (props) => {
    const { open, setOpen } = props;
  const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
  const translate = useTranslate();
  const notify = useNotify();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
  const handleClose = (event, reason) => {
    if (reason !== "backdropClick") {
      setOpen(false);
    }
  };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
  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 } });
    };
  const handleError = async (error) => {
    notify(error.message || "common.response.fail", {
      type: "error",
      messageArgs: { _: error.message },
    });
  };
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
  return (
    <>
      <CreateBase
        record={{}}
        transform={(data) => {
          return data;
        }}
        mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
      >
        <Dialog
          open={open}
          onClose={handleClose}
          aria-labelledby="form-dialog-title"
          fullWidth
          disableRestoreFocus
          maxWidth="xl" // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
        >
          <Form>
            <DialogTitle
              id="form-dialog-title"
              sx={{
                position: "sticky",
                top: 0,
                backgroundColor: "background.paper",
                zIndex: 1000,
              }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <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 }}>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.code"
                                        source="code"
                                        parse={v => v}
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.name"
                                        source="name"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.delimit"
                                        source="delimit"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.reset"
                                        source="reset"
                                        parse={v => v}
                                        validate={required()}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.resetDep"
                                        source="resetDep"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.currValue"
                                        source="currValue"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.serialRule.lastCode"
                                        source="lastCode"
                                        parse={v => v}
                                    />
                                </Grid>
              {translate("create.title")}
              <Box
                sx={{ position: "absolute", top: 8, right: 8, zIndex: 1001 }}
              >
                <DialogCloseButton onClose={handleClose} />
              </Box>
            </DialogTitle>
            <DialogContent sx={{ mt: 2 }}>
              <Grid container rowSpacing={2} columnSpacing={2}>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.serialRule.code"
                    source="code"
                    parse={(v) => v}
                    autoFocus
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.serialRule.name"
                    source="name"
                    parse={(v) => v}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.serialRule.delimit"
                    source="delimit"
                    parse={(v) => v}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.serialRule.reset"
                    source="reset"
                    parse={(v) => v}
                    validate={required()}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.serialRule.resetDep"
                    source="resetDep"
                    parse={(v) => v}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.serialRule.currValue"
                    source="currValue"
                    parse={(v) => v}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.serialRule.lastCode"
                    source="lastCode"
                    parse={(v) => v}
                  />
                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <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' }}  >
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
                    </Form>
                </Dialog>
            </CreateBase>
        </>
    )
}
                <Grid item xs={6} display="flex" gap={1}>
                  <StatusSelectInput />
                </Grid>
                <Grid item xs={12} display="flex" gap={1}>
                  <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" }}>
                <SaveButton />
              </Toolbar>
            </DialogActions>
          </Form>
        </Dialog>
      </CreateBase>
    </>
  );
};
export default SerialRuleCreate;
rsf-admin/src/page/system/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/system/serialRule/SerialRuleList.jsx
@@ -1,164 +1,240 @@
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,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    EditButton,
    FilterButton,
    CreateButton,
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    useRecordContext,
    useTranslate,
    useNotify,
    useListContext,
    FunctionField,
    TextField,
    NumberField,
    DateField,
    BooleanField,
    ReferenceField,
    TextInput,
    DateTimeInput,
    DateInput,
    SelectInput,
    NumberInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
  List,
  DatagridConfigurable,
  SearchInput,
  TopToolbar,
  SelectColumnsButton,
  EditButton,
  FilterButton,
  CreateButton,
  ExportButton,
  BulkDeleteButton,
  WrapperField,
  useRecordContext,
  useTranslate,
  useNotify,
  useListContext,
  FunctionField,
  TextField,
  NumberField,
  DateField,
  BooleanField,
  ReferenceField,
  TextInput,
  DateTimeInput,
  DateInput,
  SelectInput,
  NumberInput,
  ReferenceInput,
  ReferenceArrayInput,
  AutocompleteInput,
  DeleteButton,
} 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 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 EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
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";
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
        height: '.9em'
    },
    '& .RaDatagrid-row': {
        cursor: 'auto'
    },
    '& .column-name': {
    },
    '& .opt': {
        width: 200
    },
  "& .css-1vooibu-MuiSvgIcon-root": {
    height: ".9em",
  },
  "& .RaDatagrid-row": {
    cursor: "auto",
  },
  "& .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 />,
  <SearchInput source="condition" 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" />,
    <TextInput source="delimit" label="table.field.serialRule.delimit" />,
    <TextInput source="reset" label="table.field.serialRule.reset" />,
    <TextInput source="resetDep" label="table.field.serialRule.resetDep" />,
    <TextInput source="currValue" label="table.field.serialRule.currValue" />,
    <TextInput source="lastCode" label="table.field.serialRule.lastCode" />,
  <TextInput source="code" label="table.field.serialRule.code" />,
  <TextInput source="name" label="table.field.serialRule.name" />,
  <TextInput source="delimit" label="table.field.serialRule.delimit" />,
  <TextInput source="reset" label="table.field.serialRule.reset" />,
  <TextInput source="resetDep" label="table.field.serialRule.resetDep" />,
  <TextInput source="currValue" label="table.field.serialRule.currValue" />,
  <TextInput source="lastCode" label="table.field.serialRule.lastCode" />,
    <TextInput label="common.field.memo" source="memo" />,
    <SelectInput
        label="common.field.status"
        source="status"
        choices={[
            { id: '1', name: 'common.enums.statusTrue' },
            { id: '0', name: 'common.enums.statusFalse' },
        ]}
        resettable
    />,
]
  <TextInput label="common.field.memo" source="memo" />,
  <SelectInput
    label="common.field.status"
    source="status"
    choices={[
      { id: "1", name: "common.enums.statusTrue" },
      { id: "0", name: "common.enums.statusFalse" },
    ]}
    resettable
  />,
];
const SerialRuleList = () => {
    const translate = useTranslate();
  const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
  const [createDialog, setCreateDialog] = useState(false);
  const [drawerVal, setDrawerVal] = useState(false);
  const [detailDialog, setDetailDialog] = useState(false);
    return (
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.serialRule"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='serialRule' />
                        <MyExportButton />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='serialRule'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    expand={() => <SerialRulePanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                >
                    <NumberField source="id" />
                    <TextField source="code" label="table.field.serialRule.code" />
                    <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" />
  const navigate = useNavigate();
  const assign = (record) => {
    navigate(`/serialRuleItem?ruleId=${record.id}`);
  };
                    <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}>
                        <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} />
                    <WrapperField cellClassName="opt" label="common.field.opt">
                        <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
                        <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
                    </WrapperField>
                </StyledDatagrid>
            </List>
            <SerialRuleCreate
                open={createDialog}
                setOpen={setCreateDialog}
  return (
    <Box display="flex">
      <List
        sx={{
          flexGrow: 1,
          transition: (theme) =>
            theme.transitions.create(["all"], {
              duration: theme.transitions.duration.enteringScreen,
            }),
          marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
        }}
        title={"menu.serialRule"}
        empty={
          <EmptyData
            onClick={() => {
              setCreateDialog(true);
            }}
          />
        }
        filters={filters}
        sort={{ field: "create_time", order: "desc" }}
        actions={
          <TopToolbar>
            <FilterButton />
            <MyCreateButton
              onClick={() => {
                setCreateDialog(true);
              }}
            />
            <PageDrawer
                title='SerialRule Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
            <SelectColumnsButton preferenceKey="serialRule" />
            <MyExportButton />
          </TopToolbar>
        }
        perPage={DEFAULT_PAGE_SIZE}
      >
        <StyledDatagrid
          preferenceKey="serialRule"
          bulkActionButtons={() => (
            <BulkDeleteButton mutationMode={OPERATE_MODE} />
          )}
          rowClick={(id, resource, record) => false}
          expand={() => <SerialRulePanel />}
          expandSingle={true}
          omit={["id", "createTime", "createBy", "memo"]}
        >
          <NumberField source="id" />
          <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"
          />
          <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}
          >
            <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} />
          <WrapperField cellClassName="opt" label="common.field.opt">
            <EditButton sx={{ padding: "1px", fontSize: ".75rem" }} />
            <DeleteButton
              sx={{ padding: "1px", fontSize: ".75rem" }}
              mutationMode={OPERATE_MODE}
            />
          </WrapperField>
        </StyledDatagrid>
      </List>
      <SerialRuleCreate open={createDialog} setOpen={setCreateDialog} />
      <SerialRuleDetail open={detailDialog} setOpen={setDetailDialog} />
      <PageDrawer
        title="SerialRule Detail"
        drawerVal={drawerVal}
        setDrawerVal={setDrawerVal}
      ></PageDrawer>
    </Box>
  );
};
export default SerialRuleList;
rsf-admin/src/page/system/serialRuleItem/SerialRuleItemList.jsx
@@ -1,162 +1,236 @@
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,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    EditButton,
    FilterButton,
    CreateButton,
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    useRecordContext,
    useTranslate,
    useNotify,
    useListContext,
    FunctionField,
    TextField,
    NumberField,
    DateField,
    BooleanField,
    ReferenceField,
    TextInput,
    DateTimeInput,
    DateInput,
    SelectInput,
    NumberInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
  List,
  DatagridConfigurable,
  SearchInput,
  TopToolbar,
  SelectColumnsButton,
  EditButton,
  FilterButton,
  CreateButton,
  ExportButton,
  BulkDeleteButton,
  WrapperField,
  useRecordContext,
  useTranslate,
  useNotify,
  useListContext,
  FunctionField,
  TextField,
  NumberField,
  DateField,
  BooleanField,
  ReferenceField,
  TextInput,
  DateTimeInput,
  DateInput,
  SelectInput,
  NumberInput,
  ReferenceInput,
  ReferenceArrayInput,
  AutocompleteInput,
  DeleteButton,
} 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 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 EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
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 CustomerTopToolBar from "../components/EditTopToolBar";
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
    '& .css-1vooibu-MuiSvgIcon-root': {
        height: '.9em'
    },
    '& .RaDatagrid-row': {
        cursor: 'auto'
    },
    '& .column-name': {
    },
    '& .opt': {
        width: 200
    },
  "& .css-1vooibu-MuiSvgIcon-root": {
    height: ".9em",
  },
  "& .RaDatagrid-row": {
    cursor: "auto",
  },
  "& .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 />,
  <SearchInput source="condition" 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" />,
    <NumberInput source="len" label="table.field.serialRuleItem.len" />,
    <NumberInput source="lenStr" label="table.field.serialRuleItem.lenStr" />,
    <NumberInput source="sort" label="table.field.serialRuleItem.sort" />,
  <NumberInput source="ruleId" label="table.field.serialRuleItem.ruleId" />,
  <TextInput source="wkType" label="table.field.serialRuleItem.wkType" />,
  <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" />,
    <TextInput label="common.field.memo" source="memo" />,
    <SelectInput
        label="common.field.status"
        source="status"
        choices={[
            { id: '1', name: 'common.enums.statusTrue' },
            { id: '0', name: 'common.enums.statusFalse' },
        ]}
        resettable
    />,
]
  <TextInput label="common.field.memo" source="memo" />,
  <SelectInput
    label="common.field.status"
    source="status"
    choices={[
      { id: "1", name: "common.enums.statusTrue" },
      { id: "0", name: "common.enums.statusFalse" },
    ]}
    resettable
  />,
];
const SerialRuleItemList = () => {
    const translate = useTranslate();
  const translate = useTranslate();
  const [createDialog, setCreateDialog] = useState(false);
  const [drawerVal, setDrawerVal] = useState(false);
    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 (
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.serialRuleItem"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='serialRuleItem' />
                        <MyExportButton />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='serialRuleItem'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    expand={() => <SerialRuleItemPanel />}
                    expandSingle={true}
                    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="len" label="table.field.serialRuleItem.len" />
                    <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}>
                        <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}>
                        <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} />
                    <WrapperField cellClassName="opt" label="common.field.opt">
                        <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
                        <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
                    </WrapperField>
                </StyledDatagrid>
            </List>
            <SerialRuleItemCreate
                open={createDialog}
                setOpen={setCreateDialog}
  return (
    <>
      {ruleId && <CustomerTopToolBar backPrevious={true} />}
      <Box display="flex">
        <List
          sx={{
            flexGrow: 1,
            transition: (theme) =>
              theme.transitions.create(["all"], {
                duration: theme.transitions.duration.enteringScreen,
              }),
            marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
          }}
          title={"menu.serialRuleItem"}
          empty={
            <EmptyData
              onClick={() => {
                setCreateDialog(true);
              }}
            />
            <PageDrawer
                title='SerialRuleItem Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
          }
          filters={filters}
          filter={ruleId ? { ruleId } : undefined}
          sort={{ field: "create_time", order: "desc" }}
          actions={
            <TopToolbar>
              <FilterButton />
              <MyCreateButton
                onClick={() => {
                  setCreateDialog(true);
                }}
              />
              <SelectColumnsButton preferenceKey="serialRuleItem" />
              <MyExportButton />
            </TopToolbar>
          }
          perPage={DEFAULT_PAGE_SIZE}
        >
          <StyledDatagrid
            preferenceKey="serialRuleItem"
            bulkActionButtons={() => (
              <BulkDeleteButton mutationMode={OPERATE_MODE} />
            )}
            rowClick={(id, resource, record) => false}
            expand={() => <SerialRuleItemPanel />}
            expandSingle={true}
            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="len" label="table.field.serialRuleItem.len" />
            <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}
            >
            </PageDrawer>
        </Box>
    )
}
              <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}
            >
              <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}
            />
            <WrapperField cellClassName="opt" label="common.field.opt">
              <EditButton sx={{ padding: "1px", fontSize: ".75rem" }} />
              <DeleteButton
                sx={{ padding: "1px", fontSize: ".75rem" }}
                mutationMode={OPERATE_MODE}
              />
            </WrapperField>
          </StyledDatagrid>
        </List>
        <SerialRuleItemCreate open={createDialog} setOpen={setCreateDialog} />
        <PageDrawer
          title="SerialRuleItem Detail"
          drawerVal={drawerVal}
          setDrawerVal={setDrawerVal}
        ></PageDrawer>
      </Box>
    </>
  );
};
export default SerialRuleItemList;
rsf-admin/src/page/system/user/UserList.jsx
@@ -1,231 +1,285 @@
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,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    EditButton,
    FilterButton,
    CreateButton,
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    useRecordContext,
    useTranslate,
    useListContext,
    useCreatePath,
    TextField,
    NumberField,
    DateField,
    BooleanField,
    ReferenceField,
    TextInput,
    DateTimeInput,
    DateInput,
    SelectInput,
    NumberInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
    FunctionField,
} from 'react-admin';
import { Box, Typography, Card, Stack, LinearProgress } from '@mui/material';
import { styled } from '@mui/material/styles';
  List,
  DatagridConfigurable,
  SearchInput,
  TopToolbar,
  SelectColumnsButton,
  EditButton,
  FilterButton,
  CreateButton,
  ExportButton,
  BulkDeleteButton,
  WrapperField,
  useRecordContext,
  useTranslate,
  useListContext,
  useCreatePath,
  TextField,
  NumberField,
  DateField,
  BooleanField,
  ReferenceField,
  TextInput,
  DateTimeInput,
  DateInput,
  SelectInput,
  NumberInput,
  ReferenceInput,
  ReferenceArrayInput,
  AutocompleteInput,
  DeleteButton,
  FunctionField,
} 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'
    },
    '& .RaDatagrid-row': {
        cursor: 'auto'
    },
    '& .column-username': {
        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',
    },
    '& .opt': {
        width: 200
    },
    '& .column-statusBool': {
        maxWidth: 60
    },
  "& .css-1vooibu-MuiSvgIcon-root": {
    height: ".9em",
  },
  "& .RaDatagrid-row": {
    cursor: "auto",
  },
  "& .column-username": {
    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",
  },
  "& .opt": {
    width: 200,
  },
  "& .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 />,
  // <SearchInput source="condition" 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"
        choices={[
            { id: 0, name: 'table.field.user.sexes.unknown' },
            { id: 1, name: "table.field.user.sexes.male" },
            { id: 2, name: 'table.field.user.sexes.female' },
        ]}
    />,
    <TextInput source="phone" label="table.field.user.phone" />,
    <TextInput source="email" label="table.field.user.email" />,
    // <ReferenceInput source="deptId" label="table.field.user.deptId" reference="dept">
    //     <AutocompleteInput label="table.field.user.deptId" optionText="name" filterToQuery={(val) => ({ name: val })} />
    // </ReferenceInput>,
    <TextInput source="realName" label="table.field.user.realName" />,
    <TextInput source="idCard" label="table.field.user.idCard" />,
  <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"
    choices={[
      { id: 0, name: "table.field.user.sexes.unknown" },
      { id: 1, name: "table.field.user.sexes.male" },
      { id: 2, name: "table.field.user.sexes.female" },
    ]}
  />,
  <TextInput source="phone" label="table.field.user.phone" />,
  <TextInput source="email" label="table.field.user.email" />,
  // <ReferenceInput source="deptId" label="table.field.user.deptId" reference="dept">
  //     <AutocompleteInput label="table.field.user.deptId" optionText="name" filterToQuery={(val) => ({ name: val })} />
  // </ReferenceInput>,
  <TextInput source="realName" label="table.field.user.realName" />,
  <TextInput source="idCard" label="table.field.user.idCard" />,
    <TextInput label="common.field.memo" source="memo" />,
    <SelectInput
        label="common.field.status"
        source="status"
        choices={[
            { id: '1', name: 'common.enums.statusTrue' },
            { id: '0', name: 'common.enums.statusFalse' },
        ]}
    />,
]
  <TextInput label="common.field.memo" source="memo" />,
  <SelectInput
    label="common.field.status"
    source="status"
    choices={[
      { id: "1", name: "common.enums.statusTrue" },
      { id: "0", name: "common.enums.statusFalse" },
    ]}
  />,
];
const UserListContent = (props) => {
    const translate = useTranslate();
    const { isLoading } = useListContext();
  const translate = useTranslate();
  const { isLoading } = useListContext();
    return (
        <Box sx={{ position: 'relative' }}>
            {isLoading && (
                <LinearProgress
                    sx={{
                        height: "2px",
                        position: 'absolute',
                        top: 0,
                        left: 0,
                        right: 0,
                    }}
                />
            )}
            <StyledDatagrid
                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']}
            >
                <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}>
                    <TextField source="name" />
                </ReferenceField>
                <TextField source="nickname" label="table.field.user.nickname" />
                <TextField source="code" label="table.field.user.code" />
                <FunctionField
                    label="table.field.user.sex"
                    sortable={false}
                    render={(record) => {
                        switch (record.sex) {
                            case 0:
                                return translate('table.field.user.sexes.unknown');
                            case 1:
                                return translate('table.field.user.sexes.male');
                            case 2:
                                return translate('table.field.user.sexes.female');
                            default:
                                return '';
                        }
                    }}
                />
                <TextField source="phone" label="table.field.user.phone" />
                <TextField source="email" label="table.field.user.email" />
                <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} />
                <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} />
                </WrapperField>
            </StyledDatagrid>
        </Box>
    )
}
  return (
    <Box sx={{ position: "relative" }}>
      {isLoading && (
        <LinearProgress
          sx={{
            height: "2px",
            position: "absolute",
            top: 0,
            left: 0,
            right: 0,
          }}
        />
      )}
      <StyledDatagrid
        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",
        ]}
      >
        <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}
        >
          <TextField source="name" />
        </ReferenceField>
        <TextField source="nickname" label="table.field.user.nickname" />
        <TextField source="code" label="table.field.user.code" />
        <FunctionField
          label="table.field.user.sex"
          sortable={false}
          render={(record) => {
            switch (record.sex) {
              case 0:
                return translate("table.field.user.sexes.unknown");
              case 1:
                return translate("table.field.user.sexes.male");
              case 2:
                return translate("table.field.user.sexes.female");
              default:
                return "";
            }
          }}
        />
        <TextField source="phone" label="table.field.user.phone" />
        <TextField source="email" label="table.field.user.email" />
        <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}
        />
        <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}
          />
        </WrapperField>
      </StyledDatagrid>
    </Box>
  );
};
const UserList = () => {
    const translate = useTranslate();
  const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
  const [createDialog, setCreateDialog] = useState(false);
  const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.user"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='user' />
                        <MyExportButton />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
                aside={<UserListAside />}
            >
                <UserListContent />
            </List>
            <UserCreate
                open={createDialog}
                setOpen={setCreateDialog}
  return (
    <Box display="flex">
      <List
        sx={{
          flexGrow: 1,
          transition: (theme) =>
            theme.transitions.create(["all"], {
              duration: theme.transitions.duration.enteringScreen,
            }),
          marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
        }}
        title={"menu.user"}
        empty={
          <EmptyData
            onClick={() => {
              setCreateDialog(true);
            }}
          />
        }
        filters={filters}
        sort={{ field: "create_time", order: "desc" }}
        actions={
          <TopToolbar>
            <FilterButton />
            <MyCreateButton
              onClick={() => {
                setCreateDialog(true);
              }}
            />
            <PageDrawer
                title='User Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
            <SelectColumnsButton preferenceKey="user" />
            <MyExportButton />
          </TopToolbar>
        }
        perPage={DEFAULT_PAGE_SIZE}
        aside={<UserListAside />}
      >
        <UserListContent />
      </List>
      <UserCreate open={createDialog} setOpen={setCreateDialog} />
      <PageDrawer
        title="User Detail"
        drawerVal={drawerVal}
        setDrawerVal={setDrawerVal}
      ></PageDrawer>
    </Box>
  );
};
export default UserList;
rsf-admin/src/page/system/user/UserPanel.jsx
@@ -1,128 +1,156 @@
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();
    if (!record) return null;
    const translate = useTranslate();
    return (
        <>
            <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}
                            </Typography>
                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
                            <Typography variant="h6" gutterBottom align="right" >
                                ID: {record.id}
                            </Typography>
                        </Grid>
                    </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>
                        </Grid>
                    </Grid>
                    <Box height={20}>&nbsp;</Box>
                    <Grid container spacing={2}>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.username"
                                property={record.username}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.nickname"
                                property={record.nickname}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.avatar"
                                property={record.avatar}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.code"
                                property={record.code}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.sex"
                                property={record.sex$}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.phone"
                                property={record.phone}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.email"
                                property={record.email}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.emailVerified"
                                property={record.emailVerified$}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.deptId"
                                property={record.deptId$}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.realName"
                                property={record.realName}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.idCard"
                                property={record.idCard}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.birthday"
                                property={record.birthday}
                            />
                        </Grid>
                        {/* <Grid item xs={6}>
  const record = useRecordContext();
  if (!record) return null;
  const translate = useTranslate();
  return (
    <>
      <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}
              </Typography>
              {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
              <Typography variant="h6" gutterBottom align="right">
                ID: {record.id}
              </Typography>
            </Grid>
          </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>
            </Grid>
          </Grid>
          <Box height={20}>&nbsp;</Box>
          <Grid container spacing={2}>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.username"
                property={record.username}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.nickname"
                property={record.nickname}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.avatar"
                property={record.avatar}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.code"
                property={record.code}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.sex"
                property={record.sex$}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.phone"
                property={record.phone}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.email"
                property={record.email}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.emailVerified"
                property={record.emailVerified$}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.deptId"
                property={record.deptId$}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.realName"
                property={record.realName}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.idCard"
                property={record.idCard}
              />
            </Grid>
            <Grid item xs={6}>
              <PanelTypography
                title="table.field.user.birthday"
                property={record.birthday}
              />
            </Grid>
            {/* <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.user.introduction" 
                                property={record.introduction}
                            />
                        </Grid> */}
                    </Grid>
                </CardContent>
            </Card >
        </>
    );
          </Grid>
        </CardContent>
      </Card>
    </>
  );
};
export default UserPanel;
rsf-admin/src/page/warehouseAreas/WarehouseAreasCreate.jsx
@@ -1,187 +1,198 @@
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';
  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';
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Stack,
  Grid,
  Box,
} from "@mui/material";
import DialogCloseButton from "../components/DialogCloseButton";
import StatusSelectInput from "../components/StatusSelectInput";
import MemoInput from "../components/MemoInput";
const WarehouseAreasCreate = (props) => {
    const { open, setOpen } = props;
  const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
  const translate = useTranslate();
  const notify = useNotify();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
  const handleClose = (event, reason) => {
    if (reason !== "backdropClick") {
      setOpen(false);
    }
  };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
  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 } });
    };
  const handleError = async (error) => {
    notify(error.message || "common.response.fail", {
      type: "error",
      messageArgs: { _: error.message },
    });
  };
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
  return (
    <>
      <CreateBase
        record={{}}
        transform={(data) => {
          return data;
        }}
        mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
      >
        <Dialog
          open={open}
          onClose={handleClose}
          aria-labelledby="form-dialog-title"
          fullWidth
          disableRestoreFocus
          maxWidth="md" // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
        >
          <Form>
            <DialogTitle
              id="form-dialog-title"
              sx={{
                position: "sticky",
                top: 0,
                backgroundColor: "background.paper",
                zIndex: 1000,
              }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <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 }}>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.warehouseAreas.uuid"
                                        source="uuid"
                                        parse={v => v}
                                        validate={[required()]}
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.warehouseAreas.name"
                                        source="name"
                                        validate={[required()]}
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.warehouseAreas.code"
                                        source="code"
                                        validate={[required()]}
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <ReferenceInput
                                        source="shipperId"
                                        reference="shipper"
                                    >
                                        <AutocompleteInput
                                            label="table.field.warehouseAreas.shipperId"
                                            optionText="name"
                                            validate={[required()]}
                                            filterToQuery={(val) => ({ name: val })}
                                        />
                                    </ReferenceInput>
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.warehouseAreas.supplierId"
                                        source="supplierId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.warehouseAreas.flagMinus"
                                        source="flagMinus"
                                        validate={[required()]}
                                        choices={[
                                            { id: 0, name: '否' },
                                            { id:  1, name: '是' },
                                        ]}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.warehouseAreas.flagLabelMange"
                                        source="flagLabelMange"
                                        validate={[required()]}
                                        choices={[
                                            { id: 0, name: ' 否' },
                                            { id:  1, name: ' 是' },
                                        ]}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.warehouseAreas.flagMix"
                                        source="flagMix"
                                        validate={[required()]}
                                        choices={[
                                            { id: 0, name: '否' },
                                            { id:  1, name: '是' },
                                        ]}
                                    />
                                </Grid>
              {translate("create.title")}
              <Box
                sx={{ position: "absolute", top: 8, right: 8, zIndex: 1001 }}
              >
                <DialogCloseButton onClose={handleClose} />
              </Box>
            </DialogTitle>
            <DialogContent sx={{ mt: 2 }}>
              <Grid container rowSpacing={2} columnSpacing={2}>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.warehouseAreas.uuid"
                    source="uuid"
                    parse={(v) => v}
                    validate={[required()]}
                    autoFocus
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.warehouseAreas.name"
                    source="name"
                    validate={[required()]}
                    parse={(v) => v}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.warehouseAreas.code"
                    source="code"
                    validate={[required()]}
                    parse={(v) => v}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <ReferenceInput source="shipperId" reference="shipper">
                    <AutocompleteInput
                      label="table.field.warehouseAreas.shipperId"
                      optionText="name"
                      validate={[required()]}
                      filterToQuery={(val) => ({ name: val })}
                    />
                  </ReferenceInput>
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <NumberInput
                    label="table.field.warehouseAreas.supplierId"
                    source="supplierId"
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <SelectInput
                    label="table.field.warehouseAreas.flagMinus"
                    source="flagMinus"
                    validate={[required()]}
                    choices={[
                      { id: 0, name: "否" },
                      { id: 1, name: "是" },
                    ]}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <SelectInput
                    label="table.field.warehouseAreas.flagLabelMange"
                    source="flagLabelMange"
                    validate={[required()]}
                    choices={[
                      { id: 0, name: " 否" },
                      { id: 1, name: " 是" },
                    ]}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <SelectInput
                    label="table.field.warehouseAreas.flagMix"
                    source="flagMix"
                    validate={[required()]}
                    choices={[
                      { id: 0, name: "否" },
                      { id: 1, name: "是" },
                    ]}
                  />
                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <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' }}  >
                                <SaveButton />
                            </Toolbar>
                        </DialogActions>
                    </Form>
                </Dialog>
            </CreateBase>
        </>
    )
}
                <Grid item xs={6} display="flex" gap={1}>
                  <StatusSelectInput />
                </Grid>
                <Grid item xs={12} display="flex" gap={1}>
                  <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" }}>
                <SaveButton />
              </Toolbar>
            </DialogActions>
          </Form>
        </Dialog>
      </CreateBase>
    </>
  );
};
export default WarehouseAreasCreate;