Skip to content

Commit

Permalink
feat: frontend web base on react and vite
Browse files Browse the repository at this point in the history
  • Loading branch information
BIYUEHU committed Jun 12, 2024
1 parent 3144781 commit 891bf17
Show file tree
Hide file tree
Showing 49 changed files with 2,460 additions and 363 deletions.
65 changes: 65 additions & 0 deletions http.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# HTTP状态码和请求方式

HTTP状态码提供了关于请求结果的重要信息。以下是一些常见HTTP状态码及其使用场景和请求方式的详细说明:

## 1xx - 信息性状态码

- **100 Continue**:表明到目前为止所有都是正常的,客户端应该继续其请求。通常用于发送大文件时,客户端在发送完请求头后会等待此状态码,以确认服务器准备好接收请求体。

## 2xx - 成功状态码

- **200 OK**:请求已成功,且请求的网页已返回。
- **201 Created**:请求成功,并且服务器创建了新的资源。通常用于POST请求后,资源被成功创建。
- **202 Accepted**:服务器已接受请求,但尚未处理。这表示异步操作已经开始,但尚未完成。
- **204 No Content**:服务器成功处理了请求,但没有返回任何内容。这通常用于DELETE请求。

## 3xx - 重定向状态码

- **301 Moved Permanently**:请求的网页已永久移动到新位置。服务器返回此响应时,会提供新的URL。
- **302 Found**:请求的网页临时移动到另一个URL。与301不同,302表示资源的移动是临时的。
- **303 See Other**:服务器指示请求应该使用GET方法重定向到另一个URL。
- **304 Not Modified**:自从上次请求后,请求的网页未修改过。这是缓存相关的响应,用于减少不必要的数据传输。

## 4xx - 客户端错误状态码

- **400 Bad Request**:请求有语法错误或请求无法处理。
- **401 Unauthorized**:请求需要用户的身份验证。通常需要提供认证信息,如用户名和密码。
- **403 Forbidden**:服务器理解请求客户端的请求,但是拒绝执行此请求。这表示访问被禁止。
- **404 Not Found**:服务器找不到请求的网页。
- **405 Method Not Allowed**:请求行中指定的请求方法不能被用于请求网页。
- **406 Not Acceptable**:服务器无法根据客户端请求的内容特性完成请求。
- **407 Proxy Authentication Required**:类似于401,但是请求必须通过代理服务器进行认证。
- **408 Request Timeout**:请求超时,客户端没有在服务器准备等待的时间内完成请求。
- **409 Conflict**:服务器在尝试处理请求时遇到了冲突。
- **410 Gone**:请求的资源不再可用,且没有任何重定向地址。
- **411 Length Required**:服务器拒绝在没有定义Content-Length头的情况下处理请求。
- **412 Precondition Failed**:请求头中设置的条件失败。
- **413 Payload Too Large**:请求体过大,超出了服务器愿意或能够处理的范围。
- **414 URI Too Long**:请求的URI过长。
- **415 Unsupported Media Type**:请求的媒体类型不被服务器支持。
- **416 Range Not Satisfiable**:请求的范围无法满足。
- **417 Expectation Failed**:服务器无法满足请求头Expect中指定的期望值。
- **422 Unprocessable Entity**:请求格式正确,但服务器无法理解请求体中的语义错误。
- **429 Too Many Requests**:用户发送了太多的请求,超出了服务器的处理能力。

## 5xx - 服务器错误状态码

- **500 Internal Server Error**:服务器遇到了一个未曾预料的状况,无法完成对请求的处理。
- **501 Not Implemented**:服务器不支持请求的功能,无法完成请求。
- **502 Bad Gateway**:服务器作为网关或代理,收到了一个无效的响应。
- **503 Service Unavailable**:服务器目前无法使用,可能是由于超载或停机维护。
- **504 Gateway Timeout**:服务器作为网关或代理,但是没有及时从上游服务器接收请求。
- **505 HTTP Version Not Supported**:服务器不支持请求中使用的HTTP协议版本。
- **511 Network Authentication Required**:表明客户端需要进行网络认证。

## 请求方式

- **GET**:请求指定的资源。请求可以被缓存,且只有幀定的URL参数。
- **POST**:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。
- **PUT**:请求将指定资源更新为请求体中给出的表示。
- **DELETE**:请求删除指定的资源。
- **HEAD**:与GET方法相同,但是不返回请求体,只返回头部。
- **PATCH**:对资源进行部分修改。
- **OPTIONS**:返回服务器支持的通信选项。

> 了解这些状态码和请求方式可以帮助开发者更好地设计和实现HTTP API,同时也能更准确地处理客户端和服务器之间的交互。
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"license": "GPL-3.0",
"author": "Romi <[email protected]>",
"scripts": {
"build": "tsup",
"dev": "nodemon --watch",
"dev:core": "nodemon --watch",
"dev:client": "pnpm --filter @moehub/client dev",
"lint": "eslint \"packages/*/src/*.ts\" --fix",
"format": "prettier --config .prettierrc \"packages/*/src/*.ts\" --write",
"version": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0"
Expand All @@ -30,6 +30,10 @@
},
"nodemonConfig": {
"exec": "tsx packages/core/src",
"ext": "ts"
"ext": "ts",
"ignore": [
"packages/common",
"packages/client}"
]
}
}
30 changes: 30 additions & 0 deletions packages/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:

- Configure the top-level `parserOptions` property like this:

```js
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
}
```

- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
16 changes: 16 additions & 0 deletions packages/client/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Moehub</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

</html>
34 changes: 34 additions & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@moehub/client",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --host",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@ant-design/icons": "^5.3.7",
"@moehub/common": "workspace:^",
"antd": "^5.18.1",
"axios": "^1.7.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.1",
"tailwindcss": "^3.4.4"
},
"devDependencies": {
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"typescript": "^5.2.2",
"vite": "^5.2.0"
}
}
1 change: 1 addition & 0 deletions packages/client/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions packages/client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useRoutes } from 'react-router-dom';
import layout from './components/layout';
import router from './router';

function App() {
const outlet = useRoutes(router);

return <div>{layout(outlet!)}</div>;
}

export default App;
48 changes: 48 additions & 0 deletions packages/client/src/components/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Flex, Layout } from 'antd';
import React from 'react';

const { Header, Footer, Content } = Layout;

const headerStyle: React.CSSProperties = {
textAlign: 'center',
color: '#fff',
height: 64,
paddingInline: 48,
lineHeight: '64px'
};

const contentStyle: React.CSSProperties = {
textAlign: 'center',
minHeight: 120,
lineHeight: '120px'
};

const footerStyle: React.CSSProperties = {
textAlign: 'center',
color: '#ffff',
backgroundColor: '#333'
};

const layoutStyle = {
borderRadius: 8,
overflow: 'hidden',
height: '100vh'
};

const layoutSecondStyle = {
margin: '0 auto',
width: 'min(85ch, 100% - 4rem)',
marginLine: 'auto'
};

export default (outlet: React.ReactElement) => (
<Flex gap="middle" wrap>
<Layout style={layoutStyle}>
<Header style={headerStyle}>Header</Header>
<Layout style={layoutSecondStyle}>
<Content style={contentStyle}>{outlet}</Content>
<Footer style={footerStyle}>Footer</Footer>
</Layout>
</Layout>
</Flex>
);
10 changes: 10 additions & 0 deletions packages/client/src/http/character.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { MoehubApiCharacter, MoehubApiCharacters } from '@moehub/common';
import http from './http';

export function getCharacter(id: number): Promise<MoehubApiCharacter> {
return http.get(`/character/${id}`);
}

export function getCharacters(): Promise<MoehubApiCharacters> {
return http.get('/character');
}
3 changes: 3 additions & 0 deletions packages/client/src/http/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
url: 'http://localhost:5000/api'
};
31 changes: 31 additions & 0 deletions packages/client/src/http/http.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import axios from 'axios';
import { message } from 'antd';
import config from './config.js';

const http = axios.create({
baseURL: config.url,
timeout: 20000
});

http.interceptors.request.use(
(config) => config,
(err) => Promise.reject(err)
);

http.interceptors.response.use(
(response) =>
/* if (response.status === 200 && typeof response.data === 'object' && typeof response.data.data === 'object') {
return response.data.data;
} */
response.data,
(err) => {
console.log(err);

if (err instanceof Error) {
message.useMessage()[0].error(`请求错误:${err.message}`);
}
return Promise.reject(err);
}
);

export default http;
2 changes: 2 additions & 0 deletions packages/client/src/http/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './http';
export * from './character';
16 changes: 16 additions & 0 deletions packages/client/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
// import 'antd/dist/reset.css';
import 'tailwindcss/tailwind.css';
import 'antd/dist/antd.min.js.LICENSE.txt';
import './styles/index.css';
import App from './App.tsx';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
22 changes: 22 additions & 0 deletions packages/client/src/router/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { lazy } from 'react';

/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
function lazyLoader(callback: () => Promise<any>) {
const Component = lazy(callback);
return (
<React.Suspense fallback={<div>Loading...</div>}>
<Component />
</React.Suspense>
);
}

export default [
{
path: '/',
element: lazyLoader(() => import('../views/Home'))
},
{
path: '/about',
element: lazyLoader(() => import('../views/About'))
}
];
Empty file.
6 changes: 6 additions & 0 deletions packages/client/src/views/About.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default () => (
<div>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Iste quod culpa dolores esse quis beatae cupiditate
commodi, id vitae tenetur, facilis dicta iusto atque, animi soluta consequatur! Officia, ab rerum!
</div>
);
54 changes: 54 additions & 0 deletions packages/client/src/views/Home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Flex, Image, Card } from 'antd';
import { useEffect, useState } from 'react';
import { MoehubDataCharacter } from '@moehub/common';
import { Link } from 'react-router-dom';
import { getCharacters } from '../http';

export default () => {
const [data, setData] = useState<null | MoehubDataCharacter[]>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<null | string>(null);

useEffect(() => {
getCharacters()
.then((data) => setData(data.data))
.catch((error) => setError(error instanceof Error ? error.message : error))
.finally(() => setIsLoading(false));
}, []);

if (isLoading) return <div>Loading...</div>;

if (error || data === null) return <div>Error: {error}</div>;

const { Meta } = Card;

return (
<div>
<h1>角色列表</h1>
<Flex justify="center" align="center" wrap="wrap">
{data
.filter((item) => item.images && item.images.length > 0)
.map((item) => (
<Card
key={item.id}
hoverable
style={{ width: 220, float: 'none', margin: '0.5vw' }}
cover={
<Image
src={item.images![0]}
style={{ width: 200, height: 300, objectFit: 'cover' }}
alt={item.romaji}
/>
}
>
<br />
<span>{}</span>
<Link to={`/character/${item.id}`}>
<Meta title={item.name} description={item.description} />
</Link>
</Card>
))}
</Flex>
</div>
);
};
1 change: 1 addition & 0 deletions packages/client/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
Loading

0 comments on commit 891bf17

Please sign in to comment.