Skip to content

SideBar ​

(1) store/collapse.tsx ​

tsx
import { proxy } from "valtio";

interface CollapseState {
  /** 侧边栏是否收缩 */
  collapsed: boolean;
  /** 切换收缩状态 */
  toggle: () => void;
  /** 设置收缩状态 */
  setCollapsed: (collapsed: boolean) => void;
}

export const collapseStore = proxy<CollapseState>({
  collapsed: true,
  toggle() {
    this.collapsed = !this.collapsed;
  },
  setCollapsed(collapsed: boolean) {
    this.collapsed = collapsed;
  },
});

(2) components/SideBar ​

tsx
import React from "react";
import {
  AppstoreOutlined,
  ContainerOutlined,
  DesktopOutlined,
  MailOutlined,
  PieChartOutlined,
} from "@ant-design/icons";
import type { MenuProps } from "antd";
import { Menu } from "antd";

import { useSnapshot } from "valtio";

import { collapseStore } from "@/store/collapse";

type MenuItem = Required<MenuProps>["items"][number];

const items: MenuItem[] = [
  { key: "1", icon: <PieChartOutlined />, label: "Option 1" },
  { key: "2", icon: <DesktopOutlined />, label: "Option 2" },
  { key: "3", icon: <ContainerOutlined />, label: "Option 3" },
  {
    key: "sub1",
    label: "Navigation One",
    icon: <MailOutlined />,
    children: [
      { key: "5", label: "Option 5" },
      { key: "6", label: "Option 6" },
      { key: "7", label: "Option 7" },
      { key: "8", label: "Option 8" },
    ],
  },
  {
    key: "sub2",
    label: "Navigation Two",
    icon: <AppstoreOutlined />,
    children: [
      { key: "9", label: "Option 9" },
      { key: "10", label: "Option 10" },
      {
        key: "sub3",
        label: "Submenu",
        children: [
          { key: "11", label: "Option 11" },
          { key: "12", label: "Option 12" },
        ],
      },
    ],
  },
];

const SideBar: React.FC = () => {
  // 组件内订阅状态

  const { collapsed } = useSnapshot(collapseStore);

  return (
    <Menu
      defaultSelectedKeys={["1"]}
      defaultOpenKeys={["sub1"]}
      mode="inline"
      theme="dark"
      inlineCollapsed={collapsed}
      items={items}
    />
  );
};

export default SideBar;

(3) layouts ​

index.tsx ​

  • 至于图片随便找一张就行
tsx
import { Outlet } from "react-router-dom";
import { Layout, Watermark } from "antd";
import React from "react";
import { ContentWrapper, Logo } from "./layout.css";
import NavHeader from "@/components/NavHeader/index";
import NavFooter from "@/components/NavFooter/index";
const { Header, Content, Sider } = Layout;
import SideBar from "@/components/SideBar/index";
import { useSnapshot } from "valtio";

import { collapseStore } from "@/store/collapse";
import logoImg from "@/assets/images/logo.jpg";

import { css } from "@emotion/react";

const App: React.FC = () => {
  const { collapsed } = useSnapshot(collapseStore);
  const sidebarlog = () => {
    if (collapsed) {
      return (
        <img
          src={logoImg}
          alt="logo"
          css={css`
            width: 40px;
            height: 40px;
            display: block;
            align-self: center;
            justify-self: center;
            border-radius: 50%;
            margin-bottom: 12px;
            margin-top: 12px;
          `}
        />
      );
    } else {
      return <Logo>ReactCMS</Logo>;
    }
  };
  return (
    <Watermark content="翻版必究">
      <Layout style={{ minHeight: "100vh" }}>
        <Sider
          breakpoint="lg"
          width={200}
          collapsedWidth={64}
          collapsed={collapsed}
          onBreakpoint={(broken) => {
            console.log(broken);
          }}
          onCollapse={(collapsed, type) => {
            console.log(collapsed, type);
          }}
        >
          {sidebarlog()}
          <SideBar />
        </Sider>
        <Layout>
          <Header style={{ padding: 0, background: "#fff" }}>
            <NavHeader></NavHeader>
          </Header>
          <Content style={{ margin: "24px 16px 0" }}>
            <ContentWrapper style={{ padding: 24, minHeight: 360 }}>
              <Outlet />
            </ContentWrapper>
          </Content>
          <NavFooter></NavFooter>
        </Layout>
      </Layout>
    </Watermark>
  );
};

export default App;

layout.css.tsx ​

tsx
import styled from "@emotion/styled";

export const Logo = styled.div`
  height: 32px;
  margin: 16px;
  background: rgba(255, 255, 255, 0.2);
  color: #fff;
  line-height: 32px;
  text-align: center;
`;

export const SubHeader = styled.div`
  background: #fff;
`;

export const ContentWrapper = styled.div`
  background: #fff;
`;

(4) components/NavHeader ​

  • 增加了点击事件

index.tsx ​

tsx
import {
  MenuFoldOutlined,
  DownOutlined,
  MenuUnfoldOutlined,
} from "@ant-design/icons";
import { Breadcrumb, Switch, Dropdown, type MenuProps, Space } from "antd";
import { useState } from "react";
import { NavHeaderWrapper, NavLeft, NickName } from "./navheader.css";

import { useSnapshot } from "valtio";

import { collapseStore } from "@/store/collapse";

function NavHeader() {
  // 组件内订阅状态
  const { collapsed } = useSnapshot(collapseStore);

  const [breadlist] = useState([
    { title: "首页", key: "1" },
    { title: "欢迎页", key: "2" },
  ]);

  const items: MenuProps["items"] = [
    {
      key: "1",
      label: "邮箱:2993222085@qq.com",
    },

    {
      key: "2",
      danger: true,
      label: "退出登陆",
    },
  ];

  return (
    <NavHeaderWrapper>
      <NavLeft>
        {collapsed ? (
          <MenuUnfoldOutlined onClick={() => collapseStore.toggle()} />
        ) : (
          <MenuFoldOutlined onClick={() => collapseStore.toggle()} />
        )}
        <Breadcrumb items={breadlist} style={{ marginLeft: 12 }} />
      </NavLeft>
      <div>
        <Switch
          checkedChildren="暗黑"
          unCheckedChildren="默认"
          style={{ marginRight: 12 }}
        />
        <Dropdown
          menu={{
            items,
            onClick: ({ key }) => {
              console.log(`当前序号:${key},hello world`);
            },
          }}
          trigger={["click"]}
        >
          <a onClick={(e) => e.preventDefault()}>
            <NickName>
              <Space>
                七宗罪
                <DownOutlined />
              </Space>
            </NickName>
          </a>
        </Dropdown>
      </div>
    </NavHeaderWrapper>
  );
}

export default NavHeader;