Kaynağa Gözat

fix: router

chenlei 1 yıl önce
ebeveyn
işleme
91b558d310

+ 5 - 3
src/components/SpinLoding/index.module.scss

@@ -1,10 +1,12 @@
 .SpinLoding {
   position: absolute;
-  z-index: 9999;
-  width: 100%;
-  height: 100%;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
   background-color: #fff;
   display: flex;
   justify-content: center;
   align-items: center;
+  z-index: 9999;
 }

+ 78 - 0
src/pages/Banner/create-or-edit/index.tsx

@@ -0,0 +1,78 @@
+import { FormPageFooter, MemoSpinLoding } from "@/components";
+import {
+  DageUpload,
+  DageUploadConsumer,
+  DageUploadProvider,
+} from "@dage/pc-components";
+import { DatePicker, Form, FormInstance } from "antd";
+import { useCallback, useRef, useState } from "react";
+import { useNavigate, useParams } from "react-router-dom";
+
+export default function BannerCreateOrEditPage() {
+  const formRef = useRef<FormInstance>(null);
+  const navigate = useNavigate();
+  const params = useParams();
+  const [loading, setLoading] = useState(false);
+
+  const handleCancel = useCallback(() => {
+    navigate(-1);
+  }, [navigate]);
+
+  const handleSubmit = useCallback(async () => {
+    if (!(await formRef.current?.validateFields())) return;
+
+    const { banner = [], ...rest } = formRef.current?.getFieldsValue();
+
+    if (params.id) {
+      rest.id = params.id;
+    }
+
+    handleCancel();
+  }, [handleCancel, params]);
+
+  return (
+    <div style={{ position: "relative" }}>
+      {loading && <MemoSpinLoding />}
+      <DageUploadProvider>
+        <DageUploadConsumer>
+          {(data) => (
+            <>
+              <Form ref={formRef} labelCol={{ span: 2 }}>
+                <Form.Item
+                  label="海报"
+                  name="banner"
+                  rules={[{ required: true, message: "请上传海报" }]}
+                >
+                  <DageUpload
+                    tips="支持png、jpg和jpeg格式;最多1张,最大20M"
+                    action={
+                      process.env.REACT_APP_API_URL + "/api/cms/history/upload"
+                    }
+                    maxSize={20}
+                    maxCount={1}
+                  />
+                </Form.Item>
+
+                <Form.Item
+                  label="发布日期"
+                  name="date"
+                  rules={[{ required: true, message: "请选择日期" }]}
+                >
+                  <DatePicker className="w220" />
+                </Form.Item>
+              </Form>
+
+              {!loading && (
+                <FormPageFooter
+                  disabled={data?.uploading}
+                  onSubmit={handleSubmit}
+                  onCancel={handleCancel}
+                />
+              )}
+            </>
+          )}
+        </DageUploadConsumer>
+      </DageUploadProvider>
+    </div>
+  );
+}

+ 72 - 0
src/pages/Banner/index.tsx

@@ -0,0 +1,72 @@
+import { DageTableActions } from "@dage/pc-components";
+import { Button, Table } from "antd";
+import { useCallback, useMemo, useState } from "react";
+import { useNavigate } from "react-router-dom";
+
+const DEFAULT_PARAMS = {
+  pageNum: 1,
+  pageSize: 20,
+};
+
+export default function BannerPage() {
+  const navigate = useNavigate();
+  const [list, setList] = useState<[]>([]);
+  const [total, setTotal] = useState(0);
+  const [loading, setLoading] = useState(false);
+  const [params, setParams] = useState({
+    ...DEFAULT_PARAMS,
+  });
+
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setParams({ ...params, pageNum, pageSize });
+    },
+    [params]
+  );
+
+  const COLUMNS = useMemo(() => {
+    return [
+      {
+        title: "海报",
+        dataIndex: "userName",
+      },
+      {
+        title: "发布日期",
+        dataIndex: "nickName",
+      },
+      {
+        title: "操作",
+        render: (item: any) => {
+          return <DageTableActions onEdit={() => {}} onDelete={() => {}} />;
+        },
+      },
+    ];
+  }, []);
+
+  return (
+    <div>
+      <div style={{ textAlign: "right" }}>
+        <Button type="primary" onClick={() => navigate("/banner/create")}>
+          新增
+        </Button>
+      </div>
+
+      <Table
+        loading={loading}
+        className="page-table"
+        dataSource={list}
+        columns={COLUMNS}
+        rowKey="id"
+        pagination={{
+          showQuickJumper: true,
+          position: ["bottomCenter"],
+          showSizeChanger: true,
+          current: params.pageNum,
+          pageSize: params.pageSize,
+          total,
+          onChange: paginationChange(),
+        }}
+      />
+    </div>
+  );
+}

+ 3 - 0
src/pages/Collection/index.tsx

@@ -0,0 +1,3 @@
+export default function BannerPage() {
+  return <div></div>;
+}

+ 3 - 0
src/pages/Exhibition/index.tsx

@@ -0,0 +1,3 @@
+export default function BannerPage() {
+  return <div></div>;
+}

+ 119 - 0
src/pages/Information/index.tsx

@@ -0,0 +1,119 @@
+import { Button, Form, FormInstance, Input, Radio, Table } from "antd";
+import { useCallback, useMemo, useRef, useState } from "react";
+import { debounce } from "lodash";
+import { DageTableActions } from "@dage/pc-components";
+
+const TYPE_LIST = [
+  {
+    label: "展览",
+    value: 0,
+  },
+  {
+    label: "活动",
+    value: 1,
+  },
+  {
+    label: "新闻",
+    value: 2,
+  },
+  {
+    label: "通知",
+    value: 3,
+  },
+];
+
+const DEFAULT_PARAMS = {
+  pageNum: 1,
+  pageSize: 20,
+};
+
+export default function InformationPage() {
+  const formRef = useRef<FormInstance>(null);
+  const [loading, setLoading] = useState(false);
+  const [list, setList] = useState<[]>([]);
+  const [params, setParams] = useState({
+    ...DEFAULT_PARAMS,
+  });
+  const [total, setTotal] = useState(0);
+
+  const COLUMNS = useMemo(() => {
+    return [
+      {
+        title: "标题",
+        dataIndex: "title",
+      },
+      {
+        title: "正文",
+        dataIndex: "nickName",
+      },
+      {
+        title: "发布日期",
+        dataIndex: "date",
+      },
+      {
+        title: "操作",
+        render: (item: any) => {
+          return <DageTableActions onEdit={() => {}} onDelete={() => {}} />;
+        },
+      },
+    ];
+  }, []);
+
+  const handleReset = useCallback(() => {
+    formRef.current?.resetFields();
+  }, [formRef]);
+
+  const debounceSearch = useMemo(
+    () => debounce((changedVal: unknown, vals: any) => {}, 500),
+    []
+  );
+
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setParams({ ...params, pageNum, pageSize });
+    },
+    [params]
+  );
+
+  return (
+    <div className="information">
+      <Form ref={formRef} layout="inline" onValuesChange={debounceSearch}>
+        <Form.Item name="type">
+          <Radio.Group>
+            {TYPE_LIST.map((item) => (
+              <Radio.Button key={item.value} value={item.value}>
+                {item.label}
+              </Radio.Button>
+            ))}
+          </Radio.Group>
+        </Form.Item>
+        <Form.Item label="搜索项" name="stage">
+          <Input className="w220" placeholder="请输入标题或正文" allowClear />
+        </Form.Item>
+        <Form.Item>
+          <Button type="primary">新增</Button>
+        </Form.Item>
+        <Form.Item>
+          <Button onClick={handleReset}>重置</Button>
+        </Form.Item>
+      </Form>
+
+      <Table
+        loading={loading}
+        className="page-table"
+        dataSource={list}
+        columns={COLUMNS}
+        rowKey="id"
+        pagination={{
+          showQuickJumper: true,
+          position: ["bottomCenter"],
+          showSizeChanger: true,
+          current: params.pageNum,
+          pageSize: params.pageSize,
+          total,
+          onChange: paginationChange(),
+        }}
+      />
+    </div>
+  );
+}

+ 8 - 2
src/pages/Layout/components/Header/index.tsx

@@ -1,4 +1,4 @@
-import { FC, useCallback, useState } from "react";
+import { FC, useCallback, useMemo, useState } from "react";
 import style from "./index.module.scss";
 import { App, Avatar, Breadcrumb, Button, Popover } from "antd";
 import { Header } from "antd/es/layout/layout";
@@ -24,6 +24,8 @@ const generateBreadcrumb = (
   title: string;
   href?: string;
 }[] => {
+  if (!pathname || !child.length) return [];
+
   const breadcrumb = [];
   const paths = pathname.split("/").filter((path) => path !== "");
   let currentPath = "";
@@ -31,6 +33,7 @@ const generateBreadcrumb = (
   for (let i = 0; i < paths.length; i++) {
     currentPath += `/${paths[i]}`;
     const item = findRouteByPath(child, currentPath);
+
     if (item) {
       breadcrumb.push({
         title: item.title,
@@ -48,7 +51,10 @@ const generateBreadcrumb = (
 export const LayoutHeader: FC<LayoutHeaderProps> = ({ menuData }) => {
   const { modal } = App.useApp();
   const location = useLocation();
-  const breadcrumbItems = generateBreadcrumb(location.pathname, menuData);
+  const breadcrumbItems = useMemo(
+    () => generateBreadcrumb(location.pathname, menuData),
+    [location, menuData]
+  );
   const [resetPwdModalVisible, setResetPwdModalVisible] = useState(false);
   const { userInfo } = useSelector<RootState, RootState["base"]>(
     (state) => state.base

+ 25 - 16
src/pages/Layout/components/Menu/index.tsx

@@ -18,42 +18,51 @@ export const LayoutMenu: FC<LayoutMenuProps> = memo(
   ({ menuData, maxShowLevel = 2, ...rest }) => {
     const location = useLocation();
     const navigate = useNavigate();
+
     const defaultOpenKeys = useMemo(() => {
       let currentPath = "";
       const stack: string[] = [];
       const paths = location.pathname.split("/").filter((path) => path !== "");
-      for (let i = 0; i < paths.length - 1; i++) {
+      for (let i = 0; i < paths.length; i++) {
         currentPath += `/${paths[i]}`;
         const item = findRouteByPath(menuData, currentPath);
         if (item) {
           stack.push(item.path);
         }
       }
+
       return stack;
     }, [menuData, location]);
 
     const renderMenuItems = useCallback(
       (list: DageRouteItem[], level = 1): any[] => {
-        return list.map((item) => {
+        const stack: DageRouteItem[] = [];
+
+        list.forEach((item) => {
+          let child: DageRouteItem[] = [];
           const { title, path, icon, children, hide } = item;
+          const params = {
+            key: path,
+            icon: icon,
+            label: title,
+          };
 
           if (hide) return null;
 
           if (level <= maxShowLevel - 1 && children) {
-            return {
-              key: path,
-              icon: icon,
-              label: title,
-              children: renderMenuItems(children, level + 1),
-            };
+            child = renderMenuItems(children, level + 1);
           }
 
-          return {
-            key: path,
-            icon: icon,
-            label: title,
-          };
+          if (child.length) {
+            // @ts-ignore
+            params.children = child;
+          }
+
+          // @ts-ignore
+          stack.push(params);
         });
+
+        return stack;
       },
       [maxShowLevel]
     );
@@ -65,15 +74,15 @@ export const LayoutMenu: FC<LayoutMenuProps> = memo(
       [navigate]
     );
 
-    return (
+    return menuData.length ? (
       <Menu
         mode="inline"
         items={renderMenuItems(menuData)}
-        selectedKeys={[location.pathname]}
+        selectedKeys={location.pathname.split("/").map((i) => `/${i}`)}
         defaultOpenKeys={defaultOpenKeys}
         onClick={handleMenu}
         {...rest}
       />
-    );
+    ) : null;
   }
 );

+ 28 - 12
src/pages/Layout/index.tsx

@@ -85,7 +85,21 @@ export default function CustomLayout() {
                       <Navigate to={menuList[0].redirect || menuList[0].path} />
                     }
                   />
-                  {renderRoutes(menuList)}
+                  {renderRoutes(menuList).map((menu) =>
+                    menu.redirect ? (
+                      <Route
+                        key={menu.path}
+                        path={menu.path}
+                        element={<Navigate replace to={menu.redirect} />}
+                      />
+                    ) : (
+                      <Route
+                        key={menu.path}
+                        path={menu.path}
+                        Component={menu.Component}
+                      />
+                    )
+                  )}
                   <Route path="*" Component={NotFound} />
                 </Routes>
               )}
@@ -98,17 +112,19 @@ export default function CustomLayout() {
 }
 
 function renderRoutes(routes: DageRouteItem[]) {
-  return routes.map((route) => {
-    const { path, Component, children } = route;
+  function deep(v: DageRouteItem[]) {
+    const stack: DageRouteItem[] = [];
+    v.forEach((item) => {
+      const { children = [], ...rest } = item;
 
-    if (children && children.length > 0) {
-      return (
-        <Route key={path} path={path} Component={Component}>
-          {renderRoutes(children)}
-        </Route>
-      );
-    }
+      stack.push(rest);
+
+      if (!!children.length) {
+        stack.push(...deep(children));
+      }
+    });
+    return stack;
+  }
 
-    return <Route key={path} path={path} Component={Component} />;
-  });
+  return deep(routes);
 }

+ 3 - 0
src/pages/Message/index.tsx

@@ -0,0 +1,3 @@
+export default function BannerPage() {
+  return <div></div>;
+}

+ 3 - 0
src/pages/Questionnaire/index.tsx

@@ -0,0 +1,3 @@
+export default function BannerPage() {
+  return <div></div>;
+}

+ 54 - 1
src/router/index.tsx

@@ -1,4 +1,13 @@
-import { UserOutlined, SettingOutlined } from "@ant-design/icons";
+import {
+  UserOutlined,
+  SettingOutlined,
+  PictureOutlined,
+  CommentOutlined,
+  AppstoreOutlined,
+  BookOutlined,
+  ReadOutlined,
+  MessageOutlined,
+} from "@ant-design/icons";
 import React from "react";
 import { DageRouteItem } from "./types";
 
@@ -6,6 +15,50 @@ export const DEFAULT_MENU: DageRouteItem[] = [];
 
 export const DEFAULT_ADMIN_MENU: DageRouteItem[] = [
   {
+    path: "/banner",
+    title: "海报管理",
+    icon: <PictureOutlined />,
+    Component: React.lazy(() => import("../pages/Banner")),
+    children: [
+      {
+        path: "/banner/create",
+        title: "新增",
+        hide: true,
+        Component: React.lazy(() => import("../pages/Banner/create-or-edit")),
+      },
+    ],
+  },
+  {
+    path: "/information",
+    title: "资讯管理",
+    icon: <CommentOutlined />,
+    Component: React.lazy(() => import("../pages/Information")),
+  },
+  {
+    path: "/exhibition",
+    title: "展厅管理",
+    icon: <AppstoreOutlined />,
+    Component: React.lazy(() => import("../pages/Exhibition")),
+  },
+  {
+    path: "/collection",
+    title: "藏品管理",
+    icon: <BookOutlined />,
+    Component: React.lazy(() => import("../pages/Collection")),
+  },
+  {
+    path: "/questionnaire",
+    title: "问卷管理",
+    icon: <ReadOutlined />,
+    Component: React.lazy(() => import("../pages/Questionnaire")),
+  },
+  {
+    path: "/message",
+    title: "留言管理",
+    icon: <MessageOutlined />,
+    Component: React.lazy(() => import("../pages/Message")),
+  },
+  {
     path: "/user",
     title: "用户管理",
     icon: <UserOutlined />,