From cd2e8824e06a7ebd527830a3cdf2c553a82dcd1c Mon Sep 17 00:00:00 2001 From: haishan <710328466@qq.com> Date: Wed, 25 May 2022 12:28:35 +0800 Subject: [PATCH] feat: react-pc --- .editorconfig | 16 +++ .env | 2 + .eslintrc.js | 4 + .gitignore | 23 +++++ .prettierignore | 8 ++ .umirc.pre.ts | 7 ++ .umirc.production.ts | 10 ++ .umirc.release.ts | 7 ++ .umirc.test.ts | 7 ++ .umirc.ts | 107 +++++++++++++++++++++ .vscode/settings.json | 40 ++++++++ CHANGELOG.md | 0 README.md | 35 +------ mock/.gitkeep | 0 package.json | 55 +++++++++++ public/favicon.ico | Bin 0 -> 67646 bytes src/app.ts | 5 + src/assets/data/code.ts | 2 + src/assets/data/constants.ts | 5 + src/assets/images/logo.png | Bin 0 -> 11270 bytes src/components/PageLoading/index.tsx | 3 + src/components/User/index.tsx | 12 +++ src/interfaces/base.ts | 26 +++++ src/layouts/BasicLayout/Breadcrumb.tsx | 44 +++++++++ src/layouts/BasicLayout/BreadcrumbItem.tsx | 48 +++++++++ src/layouts/BasicLayout/index.less | 84 ++++++++++++++++ src/layouts/BasicLayout/index.tsx | 77 +++++++++++++++ src/layouts/BlankLayout/index.tsx | 5 + src/layouts/WindowLayout/Header.tsx | 33 +++++++ src/layouts/WindowLayout/index.less | 65 +++++++++++++ src/layouts/WindowLayout/index.tsx | 18 ++++ src/locales/zh-CN.ts | 1 + src/models/connect.ts | 5 + src/models/user.ts | 56 +++++++++++ src/pages/index.less | 3 + src/pages/index.tsx | 9 ++ src/services/user.ts | 7 ++ src/styles/index.less | 1 + src/styles/reset.less | 15 +++ src/styles/var.less | 90 +++++++++++++++++ src/utils/index.ts | 0 src/utils/request.ts | 66 +++++++++++++ src/wrappers/SecurityWrapper.tsx | 29 ++++++ tsconfig.json | 34 +++++++ typings.d.ts | 5 + 45 files changed, 1035 insertions(+), 34 deletions(-) create mode 100755 .editorconfig create mode 100644 .env create mode 100755 .eslintrc.js create mode 100755 .gitignore create mode 100644 .prettierignore create mode 100644 .umirc.pre.ts create mode 100644 .umirc.production.ts create mode 100644 .umirc.release.ts create mode 100644 .umirc.test.ts create mode 100644 .umirc.ts create mode 100644 .vscode/settings.json create mode 100644 CHANGELOG.md create mode 100644 mock/.gitkeep create mode 100644 package.json create mode 100644 public/favicon.ico create mode 100644 src/app.ts create mode 100644 src/assets/data/code.ts create mode 100644 src/assets/data/constants.ts create mode 100644 src/assets/images/logo.png create mode 100644 src/components/PageLoading/index.tsx create mode 100644 src/components/User/index.tsx create mode 100644 src/interfaces/base.ts create mode 100644 src/layouts/BasicLayout/Breadcrumb.tsx create mode 100644 src/layouts/BasicLayout/BreadcrumbItem.tsx create mode 100644 src/layouts/BasicLayout/index.less create mode 100644 src/layouts/BasicLayout/index.tsx create mode 100644 src/layouts/BlankLayout/index.tsx create mode 100644 src/layouts/WindowLayout/Header.tsx create mode 100644 src/layouts/WindowLayout/index.less create mode 100644 src/layouts/WindowLayout/index.tsx create mode 100644 src/locales/zh-CN.ts create mode 100644 src/models/connect.ts create mode 100644 src/models/user.ts create mode 100644 src/pages/index.less create mode 100644 src/pages/index.tsx create mode 100644 src/services/user.ts create mode 100644 src/styles/index.less create mode 100644 src/styles/reset.less create mode 100644 src/styles/var.less create mode 100644 src/utils/index.ts create mode 100644 src/utils/request.ts create mode 100644 src/wrappers/SecurityWrapper.tsx create mode 100644 tsconfig.json create mode 100644 typings.d.ts diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..7e3649a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.env b/.env new file mode 100644 index 0000000..0cc6b94 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +BROWSER=none +ESLINT=1 \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100755 index 0000000..15992f1 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + extends: [require.resolve('uniubi-lint/typescript/react')], + rules: {}, +}; diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..988398f --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/npm-debug.log* +/yarn-error.log +/yarn.lock +/package-lock.json + +# production +/dist + +# misc +.DS_Store + +# umi +/src/.umi +/src/.umi-production +/src/.umi-test +/.env.local + +# editor directories and files +.idea diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..0d4222f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +**/*.md +**/*.svg +**/*.ejs +**/*.html +package.json +.umi +.umi-production +.umi-test diff --git a/.umirc.pre.ts b/.umirc.pre.ts new file mode 100644 index 0000000..5c3fd32 --- /dev/null +++ b/.umirc.pre.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'umi'; + +export default defineConfig({ + define: { + 'process.env.BASE_API': '/api', + }, +}); diff --git a/.umirc.production.ts b/.umirc.production.ts new file mode 100644 index 0000000..bae8c94 --- /dev/null +++ b/.umirc.production.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'umi'; + +export default defineConfig({ + define: { + 'process.env.BASE_API': '/api', + }, + extraBabelPlugins: [ + ['transform-remove-console', { "exclude": [ "error", "warn"] }] + ], +}); diff --git a/.umirc.release.ts b/.umirc.release.ts new file mode 100644 index 0000000..5c3fd32 --- /dev/null +++ b/.umirc.release.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'umi'; + +export default defineConfig({ + define: { + 'process.env.BASE_API': '/api', + }, +}); diff --git a/.umirc.test.ts b/.umirc.test.ts new file mode 100644 index 0000000..5c3fd32 --- /dev/null +++ b/.umirc.test.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'umi'; + +export default defineConfig({ + define: { + 'process.env.BASE_API': '/api', + }, +}); diff --git a/.umirc.ts b/.umirc.ts new file mode 100644 index 0000000..1cc3917 --- /dev/null +++ b/.umirc.ts @@ -0,0 +1,107 @@ +import { defineConfig } from 'umi'; +import Icon from './public/favicon.ico' + +export default defineConfig({ + favicon: Icon, + hash: true, + dva: { + immer: true, + hmr: false, + }, + webpack5: {}, + dynamicImport: { + loading: '@/components/PageLoading/index', + }, + routes: [ + { + path: '/window', + component: '@/layouts/WindowLayout', + routes: [ + { + path: 'demo', + component: '@/pages/index', + name: '一级菜单', + title: '一级菜单', + icon: 'EntranceOutlined', + } + ] + }, + { + path: '/', + component: '@/layouts/BasicLayout', + // wrappers: ['@/wrappers/SecurityLayout'], + routes: [ + { exact: true, path: '/', redirect: '/a' }, + { + path: 'a', + component: '@/pages/index', + name: '一级菜单', + title: '一级菜单', + icon: 'EntranceOutlined', + }, + { + path: 'b', + name: '一级菜单', + title: '一级菜单', + icon: 'EntranceOutlined', + routes: [ + { exact: true, path: '/b', redirect: '/b/c' }, + { + path: 'c', + component: '@/pages/index', + name: '二级菜单', + title: '二级菜单', + }, + ], + }, + ], + }, + ], + define: { + 'process.env.BASE_API': '/api', + }, + proxy: { + '/api': { + target: 'http://mock.com', + changeOrigin: true, + pathRewrite: { '^/api': '' }, + }, + }, + theme: { + // 'primary-color': '#2228e0', + // 'info-color': '#2228e0', + // 'processing-color': '#2228e0', + // 'link-color': '#2228e0', + // 'success-color': '#46cf84', + // 'warning-color': '#ffa42e', + // 'error-color': '#fa4646', + // 'highlight-color': '#fa4646', + // 'normal-color': '#e0e0e0', + // 'heading-color': '#000000', + // 'text-color': '#4a4a4a', + // 'text-color-secondary': '#7c7c7c', + // 'disabled-color': '#c7c7c7', + // 'border-radius-base': '3px', + // 'border-color-base': '#e0e0e0', + // 'font-family': + // '"Alibaba PuHuiTi", "SourceHanSans TW", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"', + // 'padding-lg': '24px', + // 'padding-md': '16px', + // 'padding-sm': '12px', + // 'padding-xs': '8px', + // 'padding-xxs': '4px', + // 'margin-lg': '24px', + // 'margin-md': '16px', + // 'margin-sm': '12px', + // 'margin-xs': '8px', + // 'margin-xxs': '4px', + }, + locale: { + default: 'zh-CN', + antd: true, + }, + ignoreMomentLocale: true, + targets: { + ie: 10, + } +}); diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..37ad7a2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,40 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "eslint.validate": [ + "javascript", + "react", + "typescript", + "typescriptreact", + "vue" + ], + "editor.formatOnSave": true, + "[typescriptreact]": { + "editor.defaultFormatter": null + }, + "[javascript]": { + "editor.defaultFormatter": null + }, + "[typescript]": { + "editor.defaultFormatter": null + }, + "[javascriptreact]": { + "editor.defaultFormatter": null + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[less]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[scss]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index 161ddb4..d4fc42b 100644 --- a/README.md +++ b/README.md @@ -1,34 +1 @@ -## 目前已有脚手架列表 - -| 名称 | 说明 | 技术栈 | -| ------- | --------------------------- | --------------------- | -| nextJs | 基于 React 的服务端渲染方案 | nextJs + axios + antd | -| ReactJs | 基于 React 的业务型脚手架 | umiJs + axios + antd | -| TaroJs | 基于 React 的多端适配方案 | TaroJs 全家桶 | -| Vue | 基于 Vue 的业务型脚手架 | vueJs 全家桶 | -| Gulp | 基于 Gulp 的清凉型脚手架 | -- | - -## 快速上手 - -### 1. 安装相关依赖 - -```js -// 推荐 -yarn global add @nicecode/cli - -// or -// npm install @nicecode/cli -g -``` - -### 2. 运行命令 - -```js -// 查看脚手架版本号,是否安装成功 -nice - V -``` - -### 3. 创建项目 - -```js -nice create ${项目名称} -``` +# react-template \ No newline at end of file diff --git a/mock/.gitkeep b/mock/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json new file mode 100644 index 0000000..a70d435 --- /dev/null +++ b/package.json @@ -0,0 +1,55 @@ +{ + "private": true, + "scripts": { + "start": "umi dev", + "build": "umi build", + "build:test": "cross-env UMI_ENV=test umi build", + "build:release": "cross-env UMI_ENV=release umi build", + "build:pre": "cross-env UMI_ENV=pre umi build", + "build:production": "cross-env UMI_ENV=production umi build", + "lint": "eslint 'src/**/*.{js,jsx,tsx,ts}'", + "lint:fix": "eslint 'src/**/*.{js,jsx,tsx,ts}' --fix", + "prettier": "prettier --write '**/*.{less,css,md,json}'", + "test": "umi-test", + "test:coverage": "umi-test --coverage" + }, + "gitHooks": { + "pre-commit": "tsc --noEmit && lint-staged" + }, + "lint-staged": { + "*.{js,jsx,ts,tsx}": [ + "npm run lint:fix", + "git add ." + ], + "*.{less,css,md,json}": [ + "npm run prettier", + "git add ." + ] + }, + "dependencies": { + "@ant-design/pro-layout": "^6.15.4", + "@materials/user-avatar": "^1.0.1", + "@ant-design/icons-react": "^2.0.10", + "antd": "^100.0.1", + "axios": "^0.19.2", + "classnames": "^2.2.6", + "js-cookie": "^2.2.1", + "poseidon-web-monitoring": "^1.1.3", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "umi": "^3.5.15" + }, + "devDependencies": { + "@types/classnames": "^2.2.10", + "@umijs/preset-react": "1.x", + "@umijs/test": "^3.0.16", + "babel-plugin-transform-remove-console": "^6.9.4", + "cross-env": "^7.0.2", + "end-type-to-front-type": "^1.2.1", + "eslint": "^7.16.0", + "lint-staged": "^10.0.7", + "prettier": "^1.19.1", + "typescript": "^4.1.3", + "yorkie": "^2.0.0" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..fb0271bd934f046de8f7ee055c31faf6bcd81026 GIT binary patch literal 67646 zcmeHQ32;=$nI02LW(kp1EXyi{vI%AhcAOx|G6)coIE;iqAcSs>?px@VkOdY(;xu<~ z4y=_iaV4zFPziQil-KbR8DFd`_PSCrOHr;3Hew`=M%Uak7UJLUf0`#h4I?nvPV|@_ z>ZiN^?$__{@9)?B_uu`VG@ASHuc=9c|7U6DO}bArU8B*=1A)a%iO*3fF&q*93j_oL z0s(=5KtLcM5D*9m1Ox&C0fB%(Kp-Fx5C{ka1Ofs9fq+0jARrJB2nYlO0s;YnfIvVX zAP^7;2m}NI0s(=5KtLcM5D*9m1Ox&C0fB%(Kp-FxP!$3%yzs&VRS7JF{YDXZ=9y>S z`9>=gmHpii*tc(=-{HfDeUvwBYirXSIB>vE$-7mY^-n|^)bo|ExmA54@0&#c`Z@1j zBlJEQ{MSGyn2=t~Wey%Z$ozLe$H8kw_?r8*?rD^{2zm*026R-JkF+o7raHje3ObK_ z3^@B7U9=N06Orb{};jkIq=@h)*|5KD>TY;bbGh7e<5RV0~#Zw*NiRXV9rf>cKLx&Fe@$(7H=RhYTYjyMi zAMoZu_mh-Dpu7+I!Cd4`1>IKvDC>uMxvse_EiEJUgEs}d>71{upX1;Q{kwxQ&eG5k zj|C^bj#%hh+=qUrtONaGFL>`Ej8O8?Pqg5jMP9v-NU$f#*tKsRNOVh7i6C{>b_oc!z-Z7gxSqt{>?});Ir3N=hbh z8e~+IYezjfQUUx2{!4Nm>XiKXNMDe7(5J^f#n+>rFqHcM#PZ29m=QmYx+GZk^RMZ!YsSLjR1w z@DG^90v~=pUPU~Bxrp<#EASC8`NrH$U|6*a5nnn1eOiykukuPR_b4tM{bL>7wNj^$e4iZ+{gdV zxr{fA|J+XZA29t0Gzv4MPsluk+&@l(wxVCmnhXfDl=4GUOwXIcbALPC>H@@S>$m=zNZl~Oafo2f2E!9p0YlayOsW((EW7u3uRq5 z?R6{byO-j?MyY>!ZkM0YkC8tU#CG1Rpcd9Ww;%DdAl3)t&N||JSwFvYl~L-S=L@Bc zpt}{0DBGf_Pp+3kt|P)#zfxzff&ZrK8kQ+ft{?GR>7VOl+tvV*?{iYV_FfOs=9~17 zXMxuUN<3s-M)q-BV}OjeoX_ElAZbmBP}V==`x}t5oMfAM!k5Qrx3Vnb0njQut0QX$ zq?P(V1DX85ljk$;1JcjOT|H7wg4&i9rd+U!yP|H$@32h$*z zZ0lJOZv(OJqPn{{d%~v<<4uI0S0Y|#jWa@)|>yy|A!w#4d1e z1IkR0%gO#r_c9_a5D*9m1Ox&C0fB%(Kp-Fx5C{ka1Ofs9fq+0jAaHjG;FAuTKmO4< zhfA_C{ZIAft$28{I!@IkixN1BhaOo0F*}L1cH$S__kg&dk zVpr>EeUy&k)^<{ALibQr*}(t(;%Am>tCdFi^~O^hge-T5KxZdS+|^`hO^!3ry2vhC z9o|VR7wBkZa0f*$?xeLVx+rdSH)W>w(~*7lBYM5)19yk?_`K@CURyy*eD6?9q@I=s z=_qL0RSNj-=Mn|_w$q{?>L@m<2V+4GWu^@b9Xn=kAZi+)jS*7a5d#1D2j{f>EVDj# zO%JVovYQsnXs7w#y-M@{$wiafDSB}aWuzJ@C83`ZV*6=RvDN(UJA+=e_kKs9s;Kbc z!!~VFypiH!dMPcrpE5FxwD1QVG=K6n^n+oFi|VJ+avSC6StuE0(~>RJ+UhJ(Q8R_8 z9uQ!AzizYR=l$ta^xmhNvsI*Z`cn<~m>L}oQItujbpyZ84s&8;mO^uy$vd{;R zZro;no#@oK+k^JdOu-rh0ix{_sw-@7#6$m?X(lS&WTpD29ni6vo($=cbiZ;z52dG? zs2tD#wq^$v71)T!i?R}%;lmHEPaC6Z!BznRM2}9WskEPtUq1lb-b|ZHY*bTcr`&8a z?0y5D{|;LGLp`M?ny9$YN)1g8^bs4z3KL~%%|j=DGq_v@%@iWu0)c(IoOOwDW=cym zQelCWDynUin`x%#rFj0QU@h=~j^bkms36xuPi?VNE&4}>7Uj_gcJFdF+@flcr2+(6 zpEEB`OEM27#_{}bp^7Ry73P^~v_@L?sGb5R>*xvp z4rzWUDzLx?a7wly+XPu!%h1mbJ4W8!8~?2p5_&>lM}xCDDGvUf_yO4Wm;8478kM&FhC^*_TF=^lk_cF zuLV%$4JW!Xp{BzA#>RF1l(Yf9UdaZqz~&pE@JG6&Ibgx}JFpKtJU?vN=761GhacET z+GH~w|M$UIl|$h5;_qp8HY8yUxFNES3JT0rhA|)qdjV0QumLds^E|+7Lbd^FHp5TE zHbA-=HiMCNHan$v$h{s!6$5ah#S)g5Y#NGLfjwU6y`ly-K(1M``+^>T4G24MHSED` z*Z^f^HmcudN4|x)&s3G!E<5PTWED5x>qYDcJzUuno2~!w&!(AO(IP_>JtZyfPT<^&qMk0HX8>wPm(*@iG0hc3BT? z!d_5WnUylq2PooE_<`{JKQUb=*#PVx+=BhU@^U-oe-mkw%(Q=xtyIO$7or1!-Hnb$ zex9Qi87L2XK$UC*WE*J>Y=HS#0|a0mh=UEl`$M&`0XEm!C>u6Fa=eKew>pl~hXn#h z6(jI`t0PRCWE|wRKF{mr@b?!Km|z3+(&K)79^jg^FPNPUKOx4325Bztxs{5s-cQ9|z!U!p{bLQV^hfXmVBJ>& z8-V=)CB;@sgZ}ffEQ4oGTRnbvMpYnqv9LrRYBpC|Ur&G!U|FD^3Jb9gL?6h6Pbh32 zY=G~zQ!x5L^2UB?kC4v zEKpQ00v9imZ%KjeQe1Q|h0X1x0?hO2Dg6|)%z%Ah>;+A^iZfvyloZ>CIUnnQdOKB1 z`+?Xmz&dcx4tuM5TQ4NI&y2qW=-^h^e?M)l}MA6E_S{B>Vs=I2Tx22^$}4fMxS> z9t3_MUI)Z1?~(Qgc&~`hg%;s_DDMRn=31?1UlTTfiq)@aZnSGt6HK%+sGGvD-Ydkp zuhfJ-3Yj^)55(^VEXP>D`-AKYetNr8Iyc0=z>HKYwfxGqPR;H2cG+M0Ej`9I;M%Yr zTJ&Hi-uuIz0OtG9xg8YzFMJ-T9en}&!59+?^UbswHb7~qh4%rZv*J6N?5$!MsNP;g zbk(OM-*Sn4y{v!U6Uv1jI1TUrE&D0v0QdodrVgJ8%fmUbO3V)x@COzbS*3SHsw?c5 z|MoW;^*3hKi-vbfzNN<6k`&i3jsLtKl$~jUPoM+-zYYpw{bLMR9oh~5KkNh80DLa2 z48A~K3*zkH(CJfRKTyp*=g0wP4*P#r1a?t4_I)=-^XY*}!YC`Pmn2lFkKJSv2?iimL*|FV*_k-2idLcI>uw}FD z81DmzVf+t+?XQLXr%mdo24rFTVm+)qYdSr-AH0nUk*mskz|{kBc| z`!fPfbs}(JuOm7m#WckJ{)IUEvk+&&*$4bMZ2tuaLuTnHBA|Fgh9 z8s7EO$HE5~8El|cf!!23yHk2E>0#YOi~X_3`%;eu1x ze=6@Sc(+sYbiLzf{JH^J0=+MJ7I;-5Wh^xsryZr_OSKdxE~eJ}2!$WT4r4dW2~fWsx7Qak}=s>_YKx6eCg z%h!ug{Rq79{1+>W@=g64*BbDBSm=BS?7+o3dJ+_gcyw4N&WUwXd8yHG>eO}VZ%C@Y z)uIJT1pecTJ_GPUKT8wL~BVP7~}M;q4Yus7U6g*gUiQ={q3E2k~e-zryLC*t=G z0{{D&-sg8O4~87vXRP1R-239r=HBB+4w`Cz|9eM}fehkXQ1_0iMN0$%0s(=5KtLcM z5D*9m1Ox&C0fB%(Kp-Fx5C{ka1Ofs9fq+0jARrJB2nYlO0s;YnfIvVXAP^7;2m}NI z0s(=5KtLcM5D*C56A0k@#^N^)5omLhX;k`8A=~%<$@yi8w!!VMw+()o{xAFJ^f?-x zK8)+b73J}V(}~>DZ`i-A@7s+H)Q8Q?z3v<6e_{AOp9T6)$#wWVET`WNf9~Dff%iw! z9U=bj=Ffg=1;qzrW@VSZKaroZvfB$fOH$rAkx!)1?u>S+DbT~rC`~1K3 zyAh7S;S+vBN)buv>%QR+xzgh?a6u)DcH=%dHXL5pY#+sNPdhZ>qw?L;1Dc0M<=-yt za4YZ7xUhEBa{IJPSFZBcHFuW2q4~}zdG9n$8aDuMo7RlFA2r=Jy?xXLO>PQuPrv1s z|CXD=ZkPVdt^8+h3c6kTb_#dTzqtcEG^)Ms`Pbd#yW!Sea>J84M^z^h-xLD>50$gC AY5)KL literal 0 HcmV?d00001 diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..8d8011c --- /dev/null +++ b/src/app.ts @@ -0,0 +1,5 @@ +import { enableES5 } from 'immer'; +import '@/styles/index.less'; +import '@/styles/reset.less'; + +enableES5(); \ No newline at end of file diff --git a/src/assets/data/code.ts b/src/assets/data/code.ts new file mode 100644 index 0000000..343f4ab --- /dev/null +++ b/src/assets/data/code.ts @@ -0,0 +1,2 @@ +// 业务错误码 +export default {}; diff --git a/src/assets/data/constants.ts b/src/assets/data/constants.ts new file mode 100644 index 0000000..6e96b1a --- /dev/null +++ b/src/assets/data/constants.ts @@ -0,0 +1,5 @@ +export const DATE_FORMAT = 'yyyy-MM-dd'; + +export const TIME_FORMAT = 'HH:mm:ss'; + +export const DATE_TIME_FORMAT = `${DATE_FORMAT} ${TIME_FORMAT}`; diff --git a/src/assets/images/logo.png b/src/assets/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..79804dfcf6e7e039fc5d14d4d73033aaa1c22924 GIT binary patch literal 11270 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 { + return ( + +
个人中心
+
退出登录
+
+ ); +}; + +export default User; diff --git a/src/interfaces/base.ts b/src/interfaces/base.ts new file mode 100644 index 0000000..7526fc2 --- /dev/null +++ b/src/interfaces/base.ts @@ -0,0 +1,26 @@ +export interface BaseProps { + className?: string; + style?: React.CSSProperties; + children?: React.ReactNode; +} + +export interface Pagination { + total: number; + size: number; + index: number; + length: number; + beginIndex: number; + endIndex: number; +} + +export interface BaseResponse { + code: string; + msg: string; + memo: string; + data: any; + page?: Pagination; + result: number; + success: boolean; + requestId: string; + linkTime: number; +} diff --git a/src/layouts/BasicLayout/Breadcrumb.tsx b/src/layouts/BasicLayout/Breadcrumb.tsx new file mode 100644 index 0000000..c1b94a3 --- /dev/null +++ b/src/layouts/BasicLayout/Breadcrumb.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import classnames from 'classnames'; +import BreadcrumbItem from './BreadcrumbItem'; +import './index.less'; + +interface BreadcrumbProps { + routes: any[]; +} + +const Breadcrumb: React.FC = ({ routes }) => { + const validRoutes = routes.filter(item => !!item); + + return ( +
+ + {validRoutes.map( + (item: any, index) => + item && ( + + + / + + + {item.name} + + + ), + )} +
+ ); +}; + +export default Breadcrumb; diff --git a/src/layouts/BasicLayout/BreadcrumbItem.tsx b/src/layouts/BasicLayout/BreadcrumbItem.tsx new file mode 100644 index 0000000..5b61d7b --- /dev/null +++ b/src/layouts/BasicLayout/BreadcrumbItem.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { history } from 'umi'; +import { Button } from 'antd'; +import { ButtonProps } from 'antd/lib/button'; +import { HomeFilled } from '@ant-design/icons-react'; +import classnames from 'classnames'; +import './index.less'; + +interface BreadcrumbItemProps extends ButtonProps { + path: string; +} + +const BreadcrumbItem = ({ + path, + children, + className, + ...rest +}: BreadcrumbItemProps): JSX.Element => { + return ( + + ); +}; + +const RootItem: React.FC = ({ className, ...rest }) => ( + +); + +BreadcrumbItem.Root = RootItem; + +export default BreadcrumbItem; diff --git a/src/layouts/BasicLayout/index.less b/src/layouts/BasicLayout/index.less new file mode 100644 index 0000000..87bd379 --- /dev/null +++ b/src/layouts/BasicLayout/index.less @@ -0,0 +1,84 @@ +@import "../../styles/var.less"; + +.g-basic-layout { + &-header { + display: flex; + + &-breadcrumb { + .g-basic-layout-header-breadcrumb-item { + color: @M4; + padding: 0; + + &:active { + color: @M4; + } + + &:hover { + color: @S3; + background-color: @M7; + } + + &:focus { + background-color: @M7; + } + + &-active { + color: @M2; + + &[disabled], + &[disabled]:hover, + &[disabled]:focus, + &[disabled]:active { + color: @M2; + cursor: default; + } + } + } + + .g-basic-layout-header-breadcrumb-divider { + color: @M4; + margin: 0 @Sp-3; + } + } + } + + .ant-pro-sider-logo img { + width: 32px; + height: 32px; + } + + .ant-pro-global-header { + background-color: @M7; + box-shadow: none; + } + + .ant-pro-basicLayout-content { + margin: @Sp-5 @Sp-8; + } + + .ant-page-header-heading { + &-left { + margin: 0; + } + + &-title { + font-size: @Fs-4; + line-height: @Lh-4; + } + } + + .ant-layout { + background-color: @M7; + } + + .ant-layout-sider, + .ant-menu.ant-menu-dark, + .ant-menu-dark .ant-menu-sub, + .ant-menu.ant-menu-dark .ant-menu-sub { + background-color: @S1; + } + + .ant-layout-content { + min-height: calc(100vh - 72px); + } +} diff --git a/src/layouts/BasicLayout/index.tsx b/src/layouts/BasicLayout/index.tsx new file mode 100644 index 0000000..d5a2bde --- /dev/null +++ b/src/layouts/BasicLayout/index.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { history, Link } from 'umi'; +import { EntranceOutlined } from '@ant-design/icons-react'; +import ProLayout, { MenuDataItem } from '@ant-design/pro-layout'; +import { HeaderViewProps } from '@ant-design/pro-layout/lib/Header'; +import User from '@/components/User'; +import Breadcrumb from './Breadcrumb'; +import './index.less'; + +const BasicLayout: React.FC = ({ children, ...rest }) => { + const iconMap = { + EntranceOutlined: , + }; + + // 带子菜单的一级导航 + const renderSubMenuItem = (itemProps: MenuDataItem): React.ReactNode => { + return ( + <> + {itemProps.icon && iconMap[itemProps.icon as string]} + {itemProps.name} + + ); + }; + + // 不带子菜单的导航 + const renderMenuItem = (itemProps: MenuDataItem): React.ReactNode => { + return itemProps.isUrl || !itemProps.path ? ( + <> + {itemProps.icon && iconMap[itemProps.icon as string]} + {itemProps.name} + + ) : ( + + {itemProps.icon && iconMap[itemProps.icon as string]} + {itemProps.name} + + ); + }; + + // 面包屑 + const renderHeaderContent: ( + props: HeaderViewProps, + ) => React.ReactNode = props => { + // 匹配到到路由和面包屑信息 + const { matchMenuKeys, breadcrumb } = props as any; + const matchRoutes = matchMenuKeys.map((item: any) => breadcrumb[item]); + return ( +
+ +
+ ); + }; + + // 用户信息 + const renderUserAvatar = () => ; + + return ( + history.push('/')} + subMenuItemRender={renderSubMenuItem} + menuItemRender={renderMenuItem} + headerContentRender={renderHeaderContent} + rightContentRender={renderUserAvatar} + {...rest} + > + {children} + + ); +}; + +export default BasicLayout; diff --git a/src/layouts/BlankLayout/index.tsx b/src/layouts/BlankLayout/index.tsx new file mode 100644 index 0000000..76aeaed --- /dev/null +++ b/src/layouts/BlankLayout/index.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +const BlankLayout: React.FC = ({ children }) => <>{children}; + +export default BlankLayout; diff --git a/src/layouts/WindowLayout/Header.tsx b/src/layouts/WindowLayout/Header.tsx new file mode 100644 index 0000000..852dc62 --- /dev/null +++ b/src/layouts/WindowLayout/Header.tsx @@ -0,0 +1,33 @@ +import { history } from 'umi'; +import { Image, Divider } from 'antd'; +import User from '@/components/User'; +import './index.less'; + +const Header = () => { + return ( +
+
history.push('/')} + > + logo + + react-template +
+
+ +
+
+ ); +}; + +export default Header; diff --git a/src/layouts/WindowLayout/index.less b/src/layouts/WindowLayout/index.less new file mode 100644 index 0000000..21b53ed --- /dev/null +++ b/src/layouts/WindowLayout/index.less @@ -0,0 +1,65 @@ +@import "../../styles/var.less"; + +@headerHeader: 64px; + +.g-window-layout { + display: flex; + flex-direction: column; + min-width: 1200px; + min-height: 100vh; + background-color: @M7; + + &-header { + display: flex; + justify-content: space-between; + align-items: center; + height: @headerHeader; + width: 1200px; + margin: 0 auto; + + &-wrapper { + background-color: #000000; + } + + &-logo { + display: flex; + justify-content: center; + align-items: center; + color: #ffffff; + cursor: pointer; + + &-divider.ant-divider { + border-color: #ffffff; + height: 12px; + margin: 0 @Sp-5; + } + + &-text { + font-size: @Fs-3; + } + } + + &-actions { + color: #fff; + } + } + + &-content { + flex: 1; + width: 1200px; + min-height: 600px; + margin: 0 auto; + padding: @Sp-8; + background-color: #ffffff; + box-shadow: @Sh-2; + border-radius: @Ra-2; + + &-wrapper { + display: flex; + flex-direction: column; + flex: 1; + min-height: 500px; + padding: @Sp-8 0; + } + } +} diff --git a/src/layouts/WindowLayout/index.tsx b/src/layouts/WindowLayout/index.tsx new file mode 100644 index 0000000..182599a --- /dev/null +++ b/src/layouts/WindowLayout/index.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import Header from './Header'; +import './index.less'; + +const WindowLayout: React.FC = ({ children }) => { + return ( +
+
+
+
+
+
{children}
+
+
+ ); +}; + +export default WindowLayout; diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts new file mode 100644 index 0000000..ff8b4c5 --- /dev/null +++ b/src/locales/zh-CN.ts @@ -0,0 +1 @@ +export default {}; diff --git a/src/models/connect.ts b/src/models/connect.ts new file mode 100644 index 0000000..099a624 --- /dev/null +++ b/src/models/connect.ts @@ -0,0 +1,5 @@ +import { UserModelState } from './user'; + +export interface ConnectState { + user: UserModelState; +} diff --git a/src/models/user.ts b/src/models/user.ts new file mode 100644 index 0000000..9121eee --- /dev/null +++ b/src/models/user.ts @@ -0,0 +1,56 @@ +import { ImmerReducer, Effect } from 'umi'; +import { SubscriptionsMapObject } from 'dva'; +import { getUserInfo } from '@/services/user'; + +export interface UserInfo { + name?: string; +} + +export interface UserModelState { + info: UserInfo; + token: string; +} + +export interface UserModelType { + namespace: 'user'; + state: UserModelState; + effects: { + fetchUser: Effect; + }; + reducers: { + setUserInfo: ImmerReducer; + setToken: ImmerReducer; + }; + subscriptions: SubscriptionsMapObject; +} + +const UserModel: UserModelType = { + namespace: 'user', + state: { + info: {}, + token: '', + }, + effects: { + // 获取用户信息 + *fetchUser(_, { call, put }) { + const response = yield call(getUserInfo); + yield put({ + type: 'setUserInfo', + payload: response, + }); + }, + }, + reducers: { + // 修改用户信息 + setUserInfo(state, { payload }) { + state.info = payload; + }, + // 修改 token + setToken(state, { payload }) { + state.token = payload || ''; + }, + }, + subscriptions: {}, +}; + +export default UserModel; diff --git a/src/pages/index.less b/src/pages/index.less new file mode 100644 index 0000000..586302b --- /dev/null +++ b/src/pages/index.less @@ -0,0 +1,3 @@ +.title { + background: rgb(121, 242, 157); +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx new file mode 100644 index 0000000..cc86886 --- /dev/null +++ b/src/pages/index.tsx @@ -0,0 +1,9 @@ +import styles from './index.less'; + +export default () => { + return ( +
+

index

+
+ ); +}; diff --git a/src/services/user.ts b/src/services/user.ts new file mode 100644 index 0000000..a927650 --- /dev/null +++ b/src/services/user.ts @@ -0,0 +1,7 @@ +import request from '@/utils/request'; + +export async function getUserInfo(): Promise { + return request('/developer/info', { + method: 'get', + }); +} diff --git a/src/styles/index.less b/src/styles/index.less new file mode 100644 index 0000000..4193fae --- /dev/null +++ b/src/styles/index.less @@ -0,0 +1 @@ +@import "./var.less"; diff --git a/src/styles/reset.less b/src/styles/reset.less new file mode 100644 index 0000000..014fb2f --- /dev/null +++ b/src/styles/reset.less @@ -0,0 +1,15 @@ +@import "var.less"; + +.ant-picker { + width: 100%; +} + +.ant-pagination { + display: flex; + justify-content: flex-end; + width: 100%; + + &-total-text { + flex: 1; + } +} diff --git a/src/styles/var.less b/src/styles/var.less new file mode 100644 index 0000000..69f69b7 --- /dev/null +++ b/src/styles/var.less @@ -0,0 +1,90 @@ +// 颜色 +@S1: #25282a; +@S2: #1c21b8; +@S3: #2228e0; +@S4: #4046ff; +@S5: #9ca8ff; +@S6: #c9d2ff; +@M1: #000000; +@M2: #4a4a4a; +@M3: #7c7c7c; +@M4: #959595; +@M5: #c7c7c7; +@M6: #e0e0e0; +@M7: #f2f2f2; +@M8: #f9f9f9; +@M9: #ffffff; +@D1: #fac105; +@D2: #ffab24; +@D3: #ff6952; +@D4: #ff3c73; +@D5: #ee62d5; +@D6: #8475ff; +@D7: #4f73ff; +@D8: #148aff; +@D9: #0cc0c9; +@D10: #0cc991; +@D11: #83d615; +@D1-1: #fef8e5; +@D2-1: #fff6e9; +@D3-1: #fff0ed; +@D4-1: #ffebf1; +@D5-1: #fdeffa; +@D6-1: #f2f1ff; +@D7-1: #edf1ff; +@D8-1: #e7f3ff; +@D9-1: #e6f8f9; +@D10-1: #e6f9f3; +@D11-1: #f2fae7; +@F-info: #4766ff; +@F-success: #46cf84; +@F-warning: #ffa42e; +@F-red: #fa4646; + +/* 字体 */ +@Fs-1: 12px; +@Fs-2: 14px; +@Fs-3: 16px; +@Fs-4: 18px; +@Fs-5: 20px; +@Fs-6: 22px; +@Fs-7: 24px; +@Fs-8: 28px; +@Fs-9: 32px; + +/* 行高 */ +@Lh-1: 20px; +@Lh-2: 22px; +@Lh-3: 24px; +@Lh-4: 26px; +@Lh-5: 28px; +@Lh-6: 30px; +@Lh-7: 32px; +@Lh-8: 36px; +@Lh-9: 42px; + +// 间距 +@Sp-1: 2px; +@Sp-2: 4px; +@Sp-3: 8px; +@Sp-4: 10px; +@Sp-5: 12px; +@Sp-6: 16px; +@Sp-7: 20px; +@Sp-8: 24px; +@Sp-9: 30px; +@Sp-10: 32px; +@Sp-11: 40px; + +// 阴影 +@Sh-1: 0 0 8px 0 rgba(0, 0, 0, 0.05); +@Sh-2: 0 2px 8px 0 rgba(0, 0, 0, 0.1); +@Sh-3: 0 0 12px 0 rgba(0, 0, 0, 0.1); +@Sh-4: -4px 0 20px 0 rgba(0, 0, 0, 0.1); +@Sh-5: 0 2px 20px 0 rgba(0, 0, 0, 0.3); + +// 圆角 +@Ra-1: 2px; +@Ra-2: 3px; +@Ra-3: 6px; +@Ra-4: 10px; diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/request.ts b/src/utils/request.ts new file mode 100644 index 0000000..b33d2de --- /dev/null +++ b/src/utils/request.ts @@ -0,0 +1,66 @@ +import axios from 'axios'; +import { message } from 'antd'; +import CodeMsg from '@/assets/data/code'; +import { BaseResponse } from '@/interfaces/base'; + +export const DEFAULT_TIP_MESSAGE = '请求失败,请刷新重试'; + +/** + * 错误处理 + * @param data {Object} 请求返回的信息 + */ +export function handleError(data: BaseResponse): void { + const msg = CodeMsg[data.code] || data.msg || DEFAULT_TIP_MESSAGE; + message.error(msg); +} + +// create an axios instance +const service = axios.create({ + baseURL: process.env.BASE_API, // api的base_url + // timeout: 5000, // request timeout +}); + +// request interceptor +service.interceptors.request.use( + config => { + // 防止 GET 请求缓存GET + if (config.method === 'get') { + const t = new Date().getTime(); + config.params = config.params ? { ...config.params, t } : { t }; + } + return config; + }, + error => { + // Do something with request error + if (error.status === '504') { + message.error('网关超时,请重试!'); + } else { + message.error(`网络异常[-${error.status}]`); + console.log(error); // for debug + } + Promise.reject(error); + }, +); + +// response interceptor +service.interceptors.response.use( + response => { + const res = response.data; + if (!res.success) { + if (res.code === '1007') { + // 登录失效 + window.location.href = '/'; + return; + } + handleError(res); + } + return res; + }, + error => { + handleError(error); + console.log(`err${error}`); // for debug + return Promise.reject(error); + }, +); + +export default service; diff --git a/src/wrappers/SecurityWrapper.tsx b/src/wrappers/SecurityWrapper.tsx new file mode 100644 index 0000000..1a88773 --- /dev/null +++ b/src/wrappers/SecurityWrapper.tsx @@ -0,0 +1,29 @@ +import React, { useEffect } from 'react'; +import { Redirect, connect, Dispatch, useLocation } from 'umi'; +import { ConnectState } from '@/models/connect'; + +export interface SecurityWrapperProps { + token: string; + dispatch: Dispatch; +} + +const SecurityWrapper: React.FC = ({ + token, + children, +}) => { + useEffect(() => { + // dispatch && dispatch({ type: 'user/fetchUser' }); + }, []); + + const isLogin = !!token; + const { pathname } = useLocation(); + + if (!isLogin && pathname !== '/') { + return ; + } + return <>{children}; +}; + +export default connect(({ user }: ConnectState) => ({ + token: user.token, +}))(SecurityWrapper); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4c9e695 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "esnext", + "lib": ["esnext", "dom"], + "sourceMap": true, + "baseUrl": ".", + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "moduleResolution": "node", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "suppressImplicitAnyIndexErrors": true, + "noUnusedLocals": true, + "allowJs": true, + "skipLibCheck": true, + "experimentalDecorators": true, + "strict": true, + "paths": { + "@/*": ["./src/*"], + "@@/*": ["./src/.umi/*"] + } + }, + "exclude": [ + "node_modules", + "build", + "dist", + "scripts", + "src/.umi/*", + "webpack", + "jest" + ] +} diff --git a/typings.d.ts b/typings.d.ts new file mode 100644 index 0000000..26fbdfa --- /dev/null +++ b/typings.d.ts @@ -0,0 +1,5 @@ +declare module '*.css'; +declare module '*.less'; +declare module '*.png'; +declare module '*.jpeg'; +declare module '*.jpg';