ts 后台

1.创建项目

npx create-react-app my-app --template typescript

2.改造目录结构

src
	api
	components
	layout
	store
	router
	utils
	views
	App.tsx
	index.tsx
打开文件发现jsx代码处都有问题,修改 tsconfig.json
 - jsx: 'react-jsx'
 + jsx: 'react'

3.安装一些必须的模块

状态管理器

cnpm i redux react-redux redux-saga immutable redux-immutable -S
cnpm i @types/redux-immutable -D
cnpm i redux react-redux redux-thunk immutable redux-immutable -S
cnpm i @types/redux-immutable -D
cnpm i redux react-redux redux-saga -S
cnpm i redux react-redux redux-thunk -S
cnpm i mobx mobx-react -S

本项目选择第一个

路由

2021年11月4日 发布了 react-router-dom的v6.0.0版本:https://reactrouter.com/

本项目采用 V5版本:https://v5.reactrouter.com/web/guides/quick-start

cnpm i react-router-dom@5 -S
cnpm i @types/react-router-dom@5 -D

数据验证

思考,有没有必要安装 prop-types ?

cnpm i prop-types -S

数据请求

cnpm i axios -S

以前版本中 cnpm i @types/axios -S

ui库

cnpm i antd -S
// src/App.css
@import '~antd/dist/antd.css';

4.创建主布局文件

src/layout/main/Index.tsx 座位后台管理系统的主页面布局(包含左侧的菜单栏,顶部,底部等)

https://ant.design/components/layout-cn/#components-layout-demo-custom-trigger

// src/layout/main/Index.ts
import React from 'react'
import { Layout, Menu } from 'antd';
import {
  MenuUnfoldOutlined,
  MenuFoldOutlined,
  UserOutlined,
  VideoCameraOutlined,
  UploadOutlined,
} from '@ant-design/icons';

const { Header, Sider, Content } = Layout;

class Index extends React.Component {
  state = {
    collapsed: false,
  };

  toggle = () => {
    this.setState({
      collapsed: !this.state.collapsed,
    });
  };

  render() {
    return (
      <Layout id="components-layout-demo-custom-trigger">
        <Sider trigger={null} collapsible collapsed={this.state.collapsed}>
          <div className="logo" />
          <Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
            <Menu.Item key="1" icon={<UserOutlined />}>
              nav 1
            </Menu.Item>
            <Menu.Item key="2" icon={<VideoCameraOutlined />}>
              nav 2
            </Menu.Item>
            <Menu.Item key="3" icon={<UploadOutlined />}>
              nav 3
            </Menu.Item>
          </Menu>
        </Sider>
        <Layout className="site-layout">
          <Header className="site-layout-background" style={
  { padding: 0 }}>
            {React.createElement(this.state.collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
              className: 'trigger',
              onClick: this.toggle,
            })}
          </Header>
          <Content
            className="site-layout-background"
            style={
  {
              margin: '24px 16px',
              padding: 24,
              minHeight: 280,
            }}
          >
            Content
          </Content>
        </Layout>
      </Layout>
    );
  }
}

export default Index
// src/App.tsx
import React from 'react'
import Index from './layout/main/Index'

import './App.css'
const App = () => {
  return (
    <>
      <Index />
    </>
  )
}

export default App
// src/App.css
@import '~antd/dist/antd.css';

#root, #components-layout-demo-custom-trigger {
   
  height: 100%;
}

#components-layout-demo-custom-trigger .trigger {
   
  padding: 0 24px;
  font-size: 18px;
  line-height: 64px;
  cursor: pointer;
  transition: color 0.3s;
}

#components-layout-demo-custom-trigger .trigger:hover {
   
  color: #1890ff;
}

#components-layout-demo-custom-trigger .logo {
   
  height: 32px;
  margin: 16px;
  background: rgba(255, 255, 255, 0.3);
}

.site-layout .site-layout-background {
   
  background: #fff;
}

5.拆分主界面

// src/layout/main/components/SideBar.tsx
import React, { FC, useState } from 'react';
import { Layout, Menu } from 'antd';
import {
  UserOutlined,
  VideoCameraOutlined,
  UploadOutlined,
} from '@ant-design/icons';

const { Sider } = Layout;
type Props = {};

const SideBar: FC<Props> = (props) => {
  const [ collapsed, setCollapsed ] = useState(false)
  return (
    <Sider trigger={null} collapsible collapsed={collapsed}>
      <div className="logo" />
      <Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
        <Menu.Item key="1" icon={<UserOutlined />}>
          nav 1
        </Menu.Item>
        <Menu.Item key="2" icon={<VideoCameraOutlined />}>
          nav 2
        </Menu.Item>
        <Menu.Item key="3" icon={<UploadOutlined />}>
          nav 3
        </Menu.Item>
      </Menu>
    </Sider>
  )
}

export default SideBar;
// src/layout/main/components/Appheader.tsx
import React, { FC, useState } from 'react';
import { Layout } from 'antd';
import {
  MenuUnfoldOutlined,
  MenuFoldOutlined,
} from '@ant-design/icons';

const { Header } = Layout;
type Props = {};

const AppHeader: FC<Props> = (props) => {
  const [ collapsed, setCollapsed ] = useState(false)

  const toggle = () => {
    setCollapsed(!collapsed)
  }
  return (
    <Header className="site-layout-background" style={
  { padding: 0 }}>
      {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
        className: 'trigger',
        onClick: toggle,
      })}
    </Header>
  )
}

export default AppHeader;

// src/layout/main/components/AppMain

import React, { FC } from 'react';
import { Layout } from 'antd';

const { Content } = Layout;
type Props = {};

const AppMain: FC<Props> = (props) => {
  return (
    <Content
      className="site-layout-background"
      style={
  {
        margin: '24px 16px',
        padding: 24,
        minHeight: 280,
      }}
    >
      Content
    </Content>
  )
}

export default AppMain;
// src/layout/main/components/index.tsx

export { default as SideBar } from './SideBar'
export { default as AppMain } from './AppMain'
export { default as AppHeader } from './AppHeader'

// src/layout/main/Index.tsx
import React from 'react'
import {
    Layout } from 'antd';
import {
    SideBar, AppHeader, AppMain } from './components'
class Index extends React.Component {
   
  render() {
   
    return (
      <Layout id="components-layout-demo-custom-trigger">
        <SideBar/>
        <Layout className="site-layout">
          <AppHeader />
          <AppMain />
        </Layout>
      </Layout>
    );
  }
}

export default Index

6.状态管理器配置-saga

菜单的收缩封装到一个叫做 app 的模块

使用不可变的数据结构 immutable,如果初始值为对象,使用 Map,如果初始值为数组,使用 List

创建reducer的app模块

// src/store/modules/app.ts
import {
    Map } from 'immutable'
const initialState = Map({
   
  collapsed: localStorage.getItem('collapsed') === 'true'
})

export default (state = initialState, {
    type }: {
    type: string; payload?: any }) => {
   
  switch (type) {
   

    case 'CHANGE_COLLAPSED':
      // { ...state, collapsed: !state.collapsed }
      localStorage.setItem('collapsed', String(!state.get('collapsed')))
      return state.set('collapsed', !state.get('collapsed'))

    default:
      return state
  }
};

创建sagas中app模块

Redux-saga 结合es6中的 generator 函数

// src/store/sagas/app.ts
import {
    put } from 'redux-saga/effects'
// call 方法用来触发异步的请求
// put 相当于以前的dispatch,触发的是reducer中的 aciton.type

export function * changeCollapsedAction () {
   
  yield put({
   
    type: 'CHANGE_COLLAPSED'
  })
}




创建mySaga分配器

// src/store/mySaga.ts
import {
    takeLatest } from 'redux-saga/effects'

import {
    changeCollapsedAction } from './sagas/app'

function * mySaga () {
   
  yield takeLatest('REQUEST_CHANGE_COLLAPSED', changeCollapsedAction) // 如果有人触发key,立马执行value的函数
}

export default mySaga

创建状态管理器store

// src/store/index.ts
import {
    createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
// import thunk from 'redux-thunk'

import {
    combineReducers } from 'redux-immutable' // 组合reducer 一定要使用这个

import mySaga from './mySaga' // 必须存在

import app from './modules/app'

const reducer = combineReducers({
   
  app
})

const sagaMiddleware = createSagaMiddleware() // 生成saga中间件

const store = createStore(reducer, applyMiddleware(sagaMiddleware))
// const store = createStore(reducer, applyMiddleware(thunk))

// 一定要在生成store之后运行
sagaMiddleware.run(mySaga)

export default store

入口文件引入状态状态管理器

// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './store'

ReactDOM.render(
  <React.StrictMode>
    <Provider store = { store }>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

左侧菜单栏调用状态

高阶组件

// src/layout/main/components/SideBar.tsx
import React, { FC } from 'react';
import { Layout, Menu } from 'antd';
import {
  UserOutlined,
  VideoCameraOutlined,
  UploadOutlined,
} from '@ant-design/icons';

import { connect } from 'react-redux'

const { Sider } = Layout;

type Props = {
  collapsed: boolean
};

const SideBar: FC<Props> = ({ collapsed }) => {
  // const [ collapsed, setCollapsed ] = useState(false)
  return (
    <Sider trigger={null} collapsible collapsed={collapsed}>
      <div className="logo" />
      <Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
        <Menu.Item key="1" icon={<UserOutlined />}>
          nav 1
        </Menu.Item>
        <Menu.Item key="2" icon={<VideoCameraOutlined />}>
          nav 2
        </Menu.Item>
        <Menu.Item key="3" icon={<UploadOutlined />}>
          nav 3
        </Menu.Item>
      </Menu>
    </Sider>
  )
}

export default connect(
  (state: any) => {
    console.log(state)
    return {
      collapsed: state.getIn(['app', 'collapsed'])
    }
  }
)(SideBar);

头部组件调用以及更改状态

// src/layout/main/components/AppHeader.tsx
import React, { FC } from 'react';
import { Layout } from 'antd';
import {
  MenuUnfoldOutlined,
  MenuFoldOutlined,
} from '@ant-design/icons';
import { connect } from 'react-redux'
const { Header } = Layout;
type Props = {
  collapsed: boolean;
  toggleCollapsed: Function
};

const AppHeader: FC<Props> = ({ collapsed, toggleCollapsed }) => {

  const toggle = () => {
    toggleCollapsed()
  }
  return (
    <Header className="site-layout-background" style={
  { padding: 0 }}>
      {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
        className: 'trigger',
        onClick: toggle,
      })}
    </Header>
  )
}
// connect(mapStateToProps, mapDispatchToProps)()
export default connect(
  (state: any) => {
    return {
      collapsed: state.getIn(['app', 'collapsed'])
    }
  },
  (dispatch) => {
    return {
      toggleCollapsed () {
        dispatch({
          type: 'REQUEST_CHANGE_COLLAPSED'
        })
      }
    }
  }
)(AppHeader);

7.状态管理配置-thunk

创建reducer的app模块

// src/store/modules/app.ts
import {
    Map } from 'immutable'
const initialState = Map({
   
  collapsed: localStorage.getItem('collapsed') === 'true'
})

export default (state = initialState, {
    type }: {
    type: string; payload?: any }) => {
   
  switch (type) {
   

    case 'CHANGE_COLLAPSED':
      // { ...state, collapsed: !state.collapsed }
      localStorage.setItem('collapsed', String(!state.get('collapsed')))
      return state.set('collapsed', !state.get('collapsed'))

    default:
      return state
  }
};

创建状态管理器

// src/store/index.ts
import {
    createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

import {
    combineReducers } from 'redux-immutable'

import app from './modules/app'

const reducer = combineReducers({
   
  app
})

const store = createStore(reducer, applyMiddleware(thunk))

export default store

入口文件引入状态状态管理器

// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import store from './store'

ReactDOM.render(
  <React.StrictMode>
    <Provider store = { store }>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

左侧菜单栏调用状态

高阶组件

// src/layout/main/components/SideBar.tsx
import React, { FC } from 'react';
import { Layout, Menu } from 'antd';
import {
  UserOutlined,
  VideoCameraOutlined,
  UploadOutlined,
} from '@ant-design/icons';

import { connect } from 'react-redux'

const { Sider } = Layout;

type Props = {
  collapsed: boolean
};

const SideBar: FC<Props> = ({ collapsed }) => {
  // const [ collapsed, setCollapsed ] = useState(false)
  return (
    <Sider trigger={null} collapsible collapsed={collapsed}>
      <div className="logo" />
      <Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
        <Menu.Item key="1" icon={<UserOutlined />}>
          nav 1
        </Menu.Item>
        <Menu.Item key="2" icon={<VideoCameraOutlined />}>
          nav 2
        </Menu.Item>
        <Menu.Item key="3" icon={<UploadOutlined />}>
          nav 3
        </Menu.Item>
      </Menu>
    </Sider>
  )
}

export default connect(
  (state: any) => {
    console.log(state)
    return {
      collapsed: state.getIn(['app', 'collapsed'])
    }
  }
)(SideBar);

创建app模块的异步行为

// src/store/actions/app.ts
const actions = {
   
  changeCollapsedAction () {
    
    return (dispatch: any) =>{
   
      // 异步操作
      dispatch({
    // 触发reducer
        type: 'CHANGE_COLLAPSED'
      })
    }
  }
}
export default actions

头部组件调用以及更改状态

// src/layout/main/components/AppHeader.tsx
import React, { FC } from 'react';
import { Layout } from 'antd';
import {
  MenuUnfoldOutlined,
  MenuFoldOutlined,
} from '@ant-design/icons';
import { connect } from 'react-redux'
import action from './../../../store/actions/app'

const { Header } = Layout;
type Props = {
  collapsed: boolean;
  toggleCollapsed: Function
};

const AppHeader: FC<Props> = ({ collapsed, toggleCollapsed }) => {

  const toggle = () => {
    toggleCollapsed()
  }
  return (
    <Header className="site-layout-background" style={
  { padding: 0 }}>
      {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
        className: 'trigger',
        onClick: toggle,
      })}
    </Header>
  )
}
// connect(mapStateToProps, mapDispatchToProps)()
export default connect(
  (state: any) => {
    return {
      collapsed: state.getIn(['app', 'collapsed'])
    }
  },
  (dispatch: any) => {
    return {
      toggleCollapsed () {
        // 加不加() 取决于你在定义acitons时的写法
        // 如果你是函数返回一个函数(且返回的函数有默认参数dispatch),需要加
        // 如果函数直接写业务逻辑,且默认参数为dispatch,不要加
        dispatch(action.changeCollapsedAction())
      }
    }
  }
)(AppHeader);

8.左侧菜单栏

1.设计左侧菜单栏的数据

// src/router/menus.ts
import {
   
  HomeOutlined
} from '@ant-design/icons';
const menus = [
  {
   
    key: '0-0', // 树形控件时需要
    title: '系统首页',
    path: '/',
    icon: HomeOutlined
  },
  {
   
    key: '0-1',
    title: '轮播图管理',
    path: '/banner',
    icon: HomeOutlined,
    children: [
      {
   
        key: '0-1-0',
        title: '轮播图列表',
        path: '/banner/list',
        icon: HomeOutlined
      },
      {
   
        key: '0-1-1',
        title: '添加轮播图',
        path: '/banner/add',
        icon: HomeOutlined,
        hidden: true
      }
    ]
  },
  {
   
    key: '0-2',
    title: '产品管理',
    path: '/pro',
    icon: HomeOutlined,
    children: [
      {
   
        key: '0-2-0',
        title: '产品列表',
        path: '/pro/list',
        icon: HomeOutlined
      },
      {
   
        key: '0-2-1',
        title: '秒杀列表',
        path: '/pro/seckill',
        icon: HomeOutlined
      },
      {
   
        key: '0-2-2',
        title: '推荐列表',
        path: '/pro/recommend',
        icon: HomeOutlined
      },
      {
   
        key: '0-2-3',
        title: '筛选列表',
        path: '/pro/search',
        icon: HomeOutlined
      },
    ]
  },
  {
   
    key: '0-3',
    title: '账户管理',
    path: '/user',
    icon: HomeOutlined,
    children: [
      {
   
        key: '0-3-0',
        title: '用户管理',
        path: '/user/list',
        icon: HomeOutlined
      },
      {
   
        key: '0-3-1',
        title: '管理员管理',
        path: '/user/admin',
        icon: HomeOutlined
      }
    ]
  },
  {
   
    key: '0-4',
    title: '设置',
    path: '/setting',
    icon: HomeOutlined,
    hidden: true
  }
]

export default menus

2.渲染左侧菜单栏

带有hidden属性不渲染在左侧菜单栏

// src/layout/main/components/SideBar.tsx
import React, { FC } from 'react';
import { Layout, Menu } from 'antd';
import { connect } from 'react-redux'

import menus, { IRoute } from './../../../router/menus'

const { Sider } = Layout;
const { SubMenu } = Menu
type Props = {
  collapsed: boolean
};

const SideBar: FC<Props> = ({ collapsed }) => {
  // const [ collapsed, setCollapsed ] = useState(false)

  const renderMenu = (menus: IRoute[]) => {
    return menus.map(item => {
      if (item.children) {
        return (
          <SubMenu key={item.path} icon={<item.icon />} title={ item.title }>
            {
              renderMenu(item.children)
            }
          </SubMenu>
        )
      } else {
        return (
          item.hidden ? null : <Menu.Item key={item.path} icon={<item.icon />}>
            { item.title }
          </Menu.Item>
        )
      }
    })
  }
  return (
    <Sider trigger={null} collap
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Me-admin template是一个基于Vue 3和TypeScript的后台管理系统模板。它使用了一些流行的技术和工具,如Vite 4、Pinia、Element Plus和vue-request@next。该模板具有许多特性,包括可配置的主题、国际化方案、自定义keepAlive缓存、动态路由权限生成方案等。它还内置了mock数据方案,方便测试组件的开发。你可以在官方网站上找到更多关于Me-admin template的信息和文档。 如果你想开始使用这个模板,你可以在官网上找到快速开始的指南,并按照指南进行安装和配置。 另外,如果你需要使用Element Plus这个Vue 3的UI框架,你可以通过运行 `pnpm install element-plus @element-plus/icons-vue` 进行安装。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [推荐一个好用的vue3+vite4+ts+element-plus的 admin 管理后台模板。](https://blog.csdn.net/u010454239/article/details/127064416)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [搭建后台管理系统模板(v3+ts+vite)](https://blog.csdn.net/qq_63358859/article/details/130944564)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值