# modern-resort-admin **Repository Path**: yys233/modern-resort-admin ## Basic Information - **Project Name**: modern-resort-admin - **Description**: 一个基于React、TypeScript、Styled-Components的酒店后台管理系统,开发中...... - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2024-09-11 - **Last Updated**: 2025-04-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 效果图 - 登录页面 - 主页(图表暂未添加) - 房间信息 - 入住管理 - 离店管理 # 项目集成 ## 配置src别名 - 安装node的ts声明文件 ```sh npm i @types/node -D ``` - vite.config.ts ```ts import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import { resolve } from "path"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], resolve: { alias: [ { find: "@", replacement: resolve(__dirname, "./src"), }, ], }, }); ``` - tsconfig.app.json ```json { "compilerOptions": { /* src别名@ */ "baseUrl": ".", "paths": { "@/*": ["src/*"] } } } ``` ## styled-components ```sh npm i styled-components ``` ### 使用ThemeProvider管理公共样式 - 定义`theme.ts` ```ts export interface ThemeType { color: { primary: string; secondary: string; }; } export const lightMode: ThemeType = { color: { primary: "skyblue", secondary: "orange", }, }; export const darkMode: ThemeType = { color: { primary: "#eee", secondary: "pink", }, }; ``` - 将`ThemeProvider`作为项目的根组件 `App.tsx` ```tsx import { ThemeProvider } from "styled-components"; import Test from "./Test/Test"; import { lightMode } from "./theme/theme"; export default function App() { return ( ); } ``` - 扩展`defaultTheme`(文件可置于任意位置),ts类型检查 `src/theme/styled-components.d.ts` ```ts // styled-components.d.ts import "styled-components"; import { ThemeType } from "./theme"; // 确保路径正确 declare module "styled-components" { export interface DefaultTheme extends ThemeType { _placeholder?: never; // 占位符,只是为了让DefaultTheme继承ThemeType } } ``` - 组件使用示例(`theme`会被注入到`props`中) ```tsx import styled, { css } from "styled-components"; const Container = styled.div` ${({ theme }) => css` width: 100px; height: 100px; background-color: ${theme.color.primary}; `} `; function Test() { return ; } export default Test; ``` - 组件也可以正常使用`props` ```tsx import styled, { css } from "styled-components"; interface Props { height: number; } const Container = styled.div` ${({ theme, height }) => css` width: 100px; height: ${height}px; background-color: ${theme.color.primary}; `} `; function Test() { return ; } export default Test; ``` # 项目难点 ## 响应式布局 - 移动端中,左侧导航栏需要隐藏(这个都还好),顶部导航栏需要重构格式,使用普通的媒体查询会非常麻烦,因此则需要在js中来控制移动端的布局 - 整体思路为使用contextAPI管理状态,其中向外暴露一个`isMobile`,当视口宽度小于`768`时,`isMobile`为`true`,否则为`false`。 - 内部为`window`绑定`resize`事件,每当`window.innerWidth`发生改变时,更新状态,详细代码如下 ·`context/ViewPortProvider.tsx` ```tsx import { BREAK_POINT } from "@/config/config"; // 防抖函数 import { debounce } from "@/tools"; import { createContext, ReactNode, useEffect, useState } from "react"; export const ViewContext = createContext(null); /* 当改变窗口大小时,获取最新的width值,用于响应式布局 */ /** * @param width 当前视口的宽度 * @param isMobile 是否符合移动端的宽度 */ export interface ViewPortStateType { width: number; isMobile: boolean; } export default function ViewPortProvider({ children, }: { children: ReactNode; }) { const [width, setWidth] = useState(window.innerWidth); const isMobile = width < BREAK_POINT.mobile; useEffect(() => { function handleResize() { setWidth(window.innerWidth); } // 每当页面的尺寸发生改变时,更新状态 window.addEventListener("resize", debounce(handleResize, 300)); return () => { window.removeEventListener("resize", debounce(handleResize, 300)); }; }, []); return ( {children} ); } ``` `context/useViewPort.ts` ```ts import { useContext } from "react"; import { ViewContext, ViewPortStateType } from "./ViewPortProvider"; /* 封装useContext */ export default function useViewPort() { const context = useContext(ViewContext) as ViewPortStateType; if (!context) { throw new Error( "The useViewPort was used outside of the ViewPortProvider." ); } return context; } ``` # 组件库 ## Button 按钮 - 参照了`element-plus`中的按钮组件 | 属性名 | 说明 | 类型 | 默认值 | 是否可选 | | -------- | -------------- | ------------------------------------------------------------ | --------- | -------- | | children | 子元素(插槽) | `ReactNode` | | 是 | | circle | 是否为圆形按钮 | `boolean` | `false` | 是 | | onClick | 点击事件 | `function` | | 是 | | size | 按钮大小 | `large`
`middle`
`small`
| `middle` | 是 | | type | 按钮类型 | `default`
`primary`
`secondary`
`success`
`warning`
`info`
`danger`
| `default` | 是 | - 示例 ```tsx ``` ## PopConfirm 气泡确认框 博客:[React实现气泡确认框(PopConfirm)_popconfirm 取消按钮文本样式-CSDN博客](https://blog.csdn.net/qq_64157464/article/details/142349919?spm=1001.2014.3001.5501) ## Form 表单 ## Icon 图标 ## Input 输入框 ## Menu 菜单 ## Modal 弹出框 ## Table 表格 ## Select 下拉菜单 博客:[React实现带搜索框的下拉菜单_react想要一个搜索框,输入的时候弹出下拉框,下来的内容会加上输入的文字供选择-CSDN博客](https://blog.csdn.net/qq_64157464/article/details/142899486?spm=1001.2014.3001.5501) # 其他 ## 路由守卫 [如何优雅的使用react router v6, 并实现全局守卫本文主要讲述如何用pnpm+vite+typescript - 掘金 (juejin.cn)](https://juejin.cn/post/7222787944297791543) ## 需求分析 ### 主页 用户创建酒店,绑定酒店,上传酒店图片,编辑酒店信息 ### 房间信息 用户创建房间、编辑房间、删除房间 ### 管理入住 - 处理用户现场入住 - 询问用户是否预约 - 若未预约,手动为用户入住, - 若已经预约,需要查询预定信息 - 处理用户网络预约 - 消息系统