# 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)
## 需求分析
### 主页
用户创建酒店,绑定酒店,上传酒店图片,编辑酒店信息
### 房间信息
用户创建房间、编辑房间、删除房间
### 管理入住
- 处理用户现场入住
- 询问用户是否预约
- 若未预约,手动为用户入住,
- 若已经预约,需要查询预定信息
- 处理用户网络预约
- 消息系统