generated from maochaoying/react-vite-template
commit
0a031ffb40
27 changed files with 3546 additions and 0 deletions
-
4.env
-
4.env.dev
-
4.env.prod
-
20.eslintrc.cjs
-
24.gitignore
-
11.prettierrc
-
17README.md
-
13index.html
-
1jsconfig.json
-
34package.json
-
1public/vite.svg
-
10src/App.jsx
-
110src/assets/css/reset.css
-
BINsrc/assets/img/login/bg.gif
-
5src/components/Add.jsx
-
0src/index.css
-
115src/layouts/Base.jsx
-
20src/main.jsx
-
9src/request/clientError.js
-
115src/request/index.js
-
13src/router/index.jsx
-
18src/store/CountStore.js
-
24src/store/index.jsx
-
13src/views/Login/index.jsx
-
22src/views/Login/index.module.less
-
13vite.config.js
-
2926yarn.lock
@ -0,0 +1,4 @@ |
|||
|
|||
VITE_APP_TITLE=Hello |
|||
|
|||
VITE_APP_PROXY_URL=https://next.bspapp.com/server |
@ -0,0 +1,4 @@ |
|||
|
|||
VITE_APP_ENV = development |
|||
|
|||
VITE_APP_PROXY_URL=https://next.development.com/server |
@ -0,0 +1,4 @@ |
|||
|
|||
VITE_APP_ENV = production |
|||
|
|||
VITE_APP_PROXY_URL=https://next.production.com/server |
@ -0,0 +1,20 @@ |
|||
module.exports = { |
|||
root: true, |
|||
env: { browser: true, es2020: true }, |
|||
extends: [ |
|||
'eslint:recommended', |
|||
'plugin:react/recommended', |
|||
'plugin:react/jsx-runtime', |
|||
'plugin:react-hooks/recommended', |
|||
], |
|||
ignorePatterns: ['dist', '.eslintrc.cjs'], |
|||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, |
|||
settings: { react: { version: '18.2' } }, |
|||
plugins: ['react-refresh'], |
|||
rules: { |
|||
'react-refresh/only-export-components': [ |
|||
'warn', |
|||
{ allowConstantExport: true }, |
|||
], |
|||
}, |
|||
} |
@ -0,0 +1,24 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
pnpm-debug.log* |
|||
lerna-debug.log* |
|||
|
|||
node_modules |
|||
dist |
|||
dist-ssr |
|||
*.local |
|||
|
|||
# Editor directories and files |
|||
.vscode/* |
|||
!.vscode/extensions.json |
|||
.idea |
|||
.DS_Store |
|||
*.suo |
|||
*.ntvs* |
|||
*.njsproj |
|||
*.sln |
|||
*.sw? |
@ -0,0 +1,11 @@ |
|||
{ |
|||
"semi": false, |
|||
"singleQuote": true, |
|||
"printWidth": 80, |
|||
"trailingComma": "all", |
|||
"arrowParens": "avoid", |
|||
"tabWidth": 2, |
|||
"endOfLine": "lf", |
|||
"jsxSingleQuote": true, |
|||
"jsxBracketSameLine": false |
|||
} |
@ -0,0 +1,17 @@ |
|||
# React + Vite 前端项目模版 |
|||
|
|||
## 使用全新的技术栈开发 |
|||
React18 |
|||
AntDesign 5.0 |
|||
react-router 6 |
|||
Vite4 |
|||
|
|||
## 包管理 |
|||
本项目采用yarn进行包管理 |
|||
yarn.lock:锁定安装时的版本号,并且需要上传到git,以保证其他人再yarn install 时大家的依赖能保证一致 |
|||
|
|||
## UI库 AntDesign |
|||
```javascript |
|||
yarn add antd |
|||
``` |
|||
|
@ -0,0 +1,13 @@ |
|||
<!doctype html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|||
<title>Vite + React</title> |
|||
</head> |
|||
<body> |
|||
<div id="root"></div> |
|||
<script type="module" src="/src/main.jsx"></script> |
|||
</body> |
|||
</html> |
@ -0,0 +1 @@ |
|||
{} |
@ -0,0 +1,34 @@ |
|||
{ |
|||
"name": "react-vite-template", |
|||
"private": true, |
|||
"version": "0.0.0", |
|||
"type": "module", |
|||
"scripts": { |
|||
"dev": "vite --mode dev", |
|||
"prod": "vite --mode prod", |
|||
"build": "vite build", |
|||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", |
|||
"preview": "vite preview" |
|||
}, |
|||
"dependencies": { |
|||
"antd": "^5.8.4", |
|||
"axios": "^1.4.0", |
|||
"less": "^4.2.0", |
|||
"mobx": "^6.10.0", |
|||
"mobx-react": "^9.0.0", |
|||
"react": "^18.2.0", |
|||
"react-dom": "^18.2.0", |
|||
"react-router-dom": "^6.15.0" |
|||
}, |
|||
"devDependencies": { |
|||
"@types/react": "^18.2.15", |
|||
"@types/react-dom": "^18.2.7", |
|||
"@vitejs/plugin-react": "^4.0.3", |
|||
"eslint": "^8.45.0", |
|||
"eslint-plugin-react": "^7.32.2", |
|||
"eslint-plugin-react-hooks": "^4.6.0", |
|||
"eslint-plugin-react-refresh": "^0.4.3", |
|||
"prettier": "^3.0.2", |
|||
"vite": "^4.4.5" |
|||
} |
|||
} |
@ -0,0 +1 @@ |
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> |
@ -0,0 +1,10 @@ |
|||
import React, { useState } from 'react' |
|||
import BaseLayout from '@/layouts/Base' |
|||
import Login from '@/views/Login' |
|||
|
|||
const App = () => { |
|||
const [isLogin, setIsLogin] = useState(false) |
|||
|
|||
return !isLogin ? <Login /> : <BaseLayout /> |
|||
} |
|||
export default App |
@ -0,0 +1,110 @@ |
|||
/* http://meyerweb.com/eric/tools/css/reset/ */ |
|||
/* v1.0 | 20080212 */ |
|||
|
|||
html, |
|||
body, |
|||
div, |
|||
span, |
|||
applet, |
|||
object, |
|||
iframe, |
|||
h1, |
|||
h2, |
|||
h3, |
|||
h4, |
|||
h5, |
|||
h6, |
|||
p, |
|||
blockquote, |
|||
pre, |
|||
a, |
|||
abbr, |
|||
acronym, |
|||
address, |
|||
big, |
|||
cite, |
|||
code, |
|||
del, |
|||
dfn, |
|||
em, |
|||
font, |
|||
img, |
|||
ins, |
|||
kbd, |
|||
q, |
|||
s, |
|||
samp, |
|||
small, |
|||
strike, |
|||
strong, |
|||
sub, |
|||
sup, |
|||
tt, |
|||
var, |
|||
b, |
|||
u, |
|||
i, |
|||
center, |
|||
dl, |
|||
dt, |
|||
dd, |
|||
ol, |
|||
ul, |
|||
li, |
|||
fieldset, |
|||
form, |
|||
label, |
|||
legend, |
|||
table, |
|||
caption, |
|||
tbody, |
|||
tfoot, |
|||
thead, |
|||
tr, |
|||
th, |
|||
td { |
|||
margin: 0; |
|||
padding: 0; |
|||
border: 0; |
|||
outline: 0; |
|||
font-size: 100%; |
|||
vertical-align: baseline; |
|||
background: transparent; |
|||
} |
|||
body { |
|||
line-height: 1; |
|||
} |
|||
ol, |
|||
ul { |
|||
list-style: none; |
|||
} |
|||
blockquote, |
|||
q { |
|||
quotes: none; |
|||
} |
|||
blockquote:before, |
|||
blockquote:after, |
|||
q:before, |
|||
q:after { |
|||
content: ''; |
|||
content: none; |
|||
} |
|||
|
|||
/* remember to define focus styles! */ |
|||
:focus { |
|||
outline: 0; |
|||
} |
|||
|
|||
/* remember to highlight inserts somehow! */ |
|||
ins { |
|||
text-decoration: none; |
|||
} |
|||
del { |
|||
text-decoration: line-through; |
|||
} |
|||
|
|||
/* tables still need 'cellspacing="0"' in the markup */ |
|||
table { |
|||
border-collapse: collapse; |
|||
border-spacing: 0; |
|||
} |
After Width: 700 | Height: 701 | Size: 2.8 MiB |
@ -0,0 +1,5 @@ |
|||
function Add() { |
|||
return <div>123</div> |
|||
} |
|||
|
|||
export default Add |
@ -0,0 +1,115 @@ |
|||
import React, { useState } from 'react' |
|||
import { |
|||
DesktopOutlined, |
|||
FileOutlined, |
|||
PieChartOutlined, |
|||
TeamOutlined, |
|||
UserOutlined, |
|||
} from '@ant-design/icons' |
|||
import { Breadcrumb, Layout, Menu, theme } from 'antd' |
|||
import { useRoutes } from 'react-router-dom' |
|||
import routes from '@/router/index' |
|||
import { useRootStore } from '@/store' |
|||
|
|||
const { Header, Content, Footer, Sider } = Layout |
|||
|
|||
function getItem(label, key, icon, children) { |
|||
return { |
|||
key, |
|||
icon, |
|||
children, |
|||
label, |
|||
} |
|||
} |
|||
const items = [ |
|||
getItem('Option 1', '1', <PieChartOutlined />), |
|||
getItem('Option 2', '2', <DesktopOutlined />), |
|||
getItem('User', 'sub1', <UserOutlined />, [ |
|||
getItem('Tom', '3'), |
|||
getItem('Bill', '4'), |
|||
getItem('Alex', '5'), |
|||
]), |
|||
getItem('Team', 'sub2', <TeamOutlined />, [ |
|||
getItem('Team 1', '6'), |
|||
getItem('Team 2', '8'), |
|||
]), |
|||
getItem('Files', '9', <FileOutlined />), |
|||
] |
|||
function BaseLayout() { |
|||
const { countStore } = useRootStore() |
|||
const { count } = countStore |
|||
const { |
|||
token: { colorBgContainer }, |
|||
} = theme.useToken() |
|||
const [collapsed, setCollapsed] = useState(false) |
|||
return ( |
|||
<Layout |
|||
style={{ |
|||
minHeight: '100vh', |
|||
}} |
|||
> |
|||
<Sider |
|||
collapsible |
|||
collapsed={collapsed} |
|||
onCollapse={value => setCollapsed(value)} |
|||
> |
|||
<div |
|||
style={{ |
|||
textAlign: 'center', |
|||
padding: '20px 0', |
|||
color: '#fff', |
|||
fontSize: '18px', |
|||
}} |
|||
> |
|||
{count} |
|||
</div> |
|||
<Menu |
|||
theme='dark' |
|||
defaultSelectedKeys={['1']} |
|||
mode='inline' |
|||
items={items} |
|||
/> |
|||
</Sider> |
|||
<Layout> |
|||
<Header |
|||
style={{ |
|||
padding: 0, |
|||
background: colorBgContainer, |
|||
}} |
|||
/> |
|||
<Content |
|||
style={{ |
|||
margin: '0 16px', |
|||
}} |
|||
> |
|||
<Breadcrumb |
|||
style={{ |
|||
margin: '16px 0', |
|||
}} |
|||
> |
|||
<Breadcrumb.Item>User</Breadcrumb.Item> |
|||
<Breadcrumb.Item>Bill</Breadcrumb.Item> |
|||
</Breadcrumb> |
|||
<div |
|||
style={{ |
|||
padding: 24, |
|||
minHeight: 360, |
|||
background: colorBgContainer, |
|||
}} |
|||
> |
|||
{useRoutes(routes)} |
|||
</div> |
|||
</Content> |
|||
<Footer |
|||
style={{ |
|||
textAlign: 'center', |
|||
}} |
|||
> |
|||
Ant Design ©2023 Created by Ant UED |
|||
</Footer> |
|||
</Layout> |
|||
</Layout> |
|||
) |
|||
} |
|||
|
|||
export default BaseLayout |
@ -0,0 +1,20 @@ |
|||
import React from 'react' |
|||
import ReactDOM from 'react-dom/client' |
|||
import { BrowserRouter } from 'react-router-dom' |
|||
import App from './App.jsx' |
|||
import { RootStoreProvider } from '@/store' |
|||
import '@/assets/css/reset.css' |
|||
import './index.css' |
|||
import { Suspense } from 'react' |
|||
|
|||
ReactDOM.createRoot(document.getElementById('root')).render( |
|||
<RootStoreProvider> |
|||
<BrowserRouter> |
|||
<React.StrictMode> |
|||
<Suspense fallback={<div>加载中</div>}> |
|||
<App /> |
|||
</Suspense> |
|||
</React.StrictMode> |
|||
</BrowserRouter> |
|||
</RootStoreProvider>, |
|||
) |
@ -0,0 +1,9 @@ |
|||
class ClientError extends Error { |
|||
constructor(code, message) { |
|||
super(message); |
|||
this.message = message; |
|||
this.code = code; |
|||
} |
|||
} |
|||
|
|||
export default ClientError; |
@ -0,0 +1,115 @@ |
|||
/* eslint-disable no-unused-expressions */ |
|||
import axios from 'axios' |
|||
import React from 'react' |
|||
import { message } from 'antd' |
|||
import ClientError from './clientError' |
|||
|
|||
/** |
|||
* 0:成功 |
|||
* 1:系统内部错误 |
|||
* 2:session过期,跳转登陆界面 |
|||
*/ |
|||
axios.defaults.headers['Cache-Control'] = |
|||
'no-store,no-cache,no-transform,must-revalidate,max-age=0' |
|||
class BaseHttpClient { |
|||
constructor() {} |
|||
|
|||
baseUrl = '' |
|||
|
|||
loged = 1 |
|||
|
|||
instance = axios.create({ |
|||
baseURL: this.baseUrl, |
|||
timeout: 20000, |
|||
withCredentials: true, |
|||
}) |
|||
|
|||
getCommon(method, url, params = {}, headers = {}) { |
|||
this.url = url |
|||
return this.instance[method](url, { params, ...headers }) |
|||
.then(this.handleSuccessResponse.bind(this)) |
|||
.catch(this.handleErrorResponse.bind(this)) |
|||
.finally(() => {}) |
|||
} |
|||
|
|||
postCommon(method, url, data = {}, header) { |
|||
return this.instance[method](url, data, header) |
|||
.then(this.handleSuccessResponse.bind(this)) |
|||
.catch(this.handleErrorResponse.bind(this)) |
|||
.finally(() => {}) |
|||
} |
|||
|
|||
get(url, params = {}, headers = {}) { |
|||
return this.getCommon('get', url, params, headers) |
|||
} |
|||
|
|||
delete(url, params = {}, header) { |
|||
return this.getCommon('delete', url, params, header) |
|||
} |
|||
|
|||
post(url, data = {}, header) { |
|||
return this.postCommon('post', url, data, header) |
|||
} |
|||
|
|||
put(url, data = {}, header) { |
|||
return this.postCommon('put', url, data, header) |
|||
} |
|||
|
|||
handleSuccessResponse(res) { |
|||
if (res) { |
|||
if (res && res.data.result_code === 0) { |
|||
return res.data |
|||
} |
|||
} |
|||
return null |
|||
} |
|||
|
|||
handleErrorResponse(err) { |
|||
// console.log(err.response, 92)
|
|||
if (err.toString().indexOf('Network Error') >= 0) { |
|||
message.destroy() |
|||
message.error('网络连接错误') |
|||
return |
|||
} |
|||
if ([101002, 101003].includes(err?.response?.data?.result_code)) { |
|||
message.destroy() |
|||
message.error('用户登录已过期') |
|||
// window.location.href = '/login';
|
|||
return |
|||
} |
|||
if (err?.response?.data?.result_code && err?.response?.data?.message) { |
|||
message.destroy() |
|||
message.error(err.response.data.message) |
|||
} else if (err) { |
|||
throw err |
|||
} else { |
|||
throw new ClientError(999, '未知错误') |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 带有拦截器的HttpClient
|
|||
class HttpClientWithInterceptors extends BaseHttpClient { |
|||
constructor() { |
|||
super() |
|||
this.instance.interceptors.response.use( |
|||
response => |
|||
// 在这里添加响应的拦截
|
|||
response, |
|||
error => { |
|||
const originalRequest = error.config |
|||
if ( |
|||
error.code === 'ECONNABORTED' && |
|||
error.message.indexOf('timeout') !== -1 && |
|||
!originalRequest._retry |
|||
) { |
|||
message.error('查询超时,请稍后重试') |
|||
} |
|||
}, |
|||
) |
|||
} |
|||
} |
|||
|
|||
class HttpClientClass extends HttpClientWithInterceptors {} |
|||
|
|||
export const HttpClient = new HttpClientClass() |
@ -0,0 +1,13 @@ |
|||
import { lazy } from 'react' |
|||
|
|||
const Add = lazy(() => import('../components/Add')) |
|||
|
|||
// 物料管理 |
|||
const routes = [ |
|||
{ |
|||
path: '/', |
|||
element: <Add />, |
|||
}, |
|||
] |
|||
|
|||
export default routes |
@ -0,0 +1,18 @@ |
|||
import { action, makeObservable, observable } from 'mobx' |
|||
/** |
|||
* 状态被标记为observable |
|||
* 更改状态的方法标记为action.bound |
|||
* 组件使用observer方法包裹 |
|||
*/ |
|||
export default class CountStore { |
|||
constructor() { |
|||
this.count = 0 |
|||
makeObservable(this, { |
|||
count: observable, |
|||
increment: action.bound, |
|||
}) |
|||
} |
|||
increment() { |
|||
this.count++ |
|||
} |
|||
} |
@ -0,0 +1,24 @@ |
|||
import CountStore from './CountStore' |
|||
import { createContext, useContext } from 'react' |
|||
|
|||
class RootStore { |
|||
constructor() { |
|||
this.countStore = new CountStore() |
|||
} |
|||
} |
|||
|
|||
const rootStore = new RootStore() |
|||
|
|||
const RootStoreContext = createContext() |
|||
|
|||
export const RootStoreProvider = ({ children }) => { |
|||
return ( |
|||
<RootStoreContext.Provider value={rootStore}> |
|||
{children} |
|||
</RootStoreContext.Provider> |
|||
) |
|||
} |
|||
|
|||
export const useRootStore = () => { |
|||
return useContext(RootStoreContext) |
|||
} |
@ -0,0 +1,13 @@ |
|||
import styles from './index.module.less' |
|||
import Background from '@/assets/img/login/bg.gif' |
|||
|
|||
const Login = () => { |
|||
return ( |
|||
<div className={styles.login_container}> |
|||
<img src={Background} className={styles.back} /> |
|||
<div className={styles.form_wrap}></div> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default Login |
@ -0,0 +1,22 @@ |
|||
.login_container { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
background: #f6f5f8; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 0 8vw; |
|||
box-sizing: border-box; |
|||
overflow: hidden; |
|||
.back{ |
|||
width: 40vw; |
|||
height: 40vw; |
|||
margin-top: 8vw; |
|||
} |
|||
.form_wrap{ |
|||
width: 40vw; |
|||
height: 40vw; |
|||
border-radius: 38px; |
|||
background: #FFFFFF; |
|||
} |
|||
} |
@ -0,0 +1,13 @@ |
|||
import { defineConfig } from "vite"; |
|||
import react from "@vitejs/plugin-react"; |
|||
import path from "path"; |
|||
|
|||
// https://vitejs.dev/config/
|
|||
export default defineConfig({ |
|||
plugins: [react()], |
|||
resolve: { |
|||
alias: { |
|||
"@": path.resolve(__dirname, "src"), |
|||
}, |
|||
}, |
|||
}); |
2926
yarn.lock
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue