Commit 351ae987 by 肖玉娟

初始化项目

parents
# Details
Date : 2022-08-10 18:18:13
Directory e:\\workspace\\zhds-manage-web\\src
Total : 89 files, 4830 codes, 460 comments, 529 blanks, all 5819 lines
[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
## Files
| filename | language | code | comment | blank | total |
| :--- | :--- | ---: | ---: | ---: | ---: |
| [src/components/DList.tsx](/src/components/DList.tsx) | TypeScript React | 10 | 0 | 5 | 15 |
| [src/components/DTable.tsx](/src/components/DTable.tsx) | TypeScript React | 36 | 1 | 6 | 43 |
| [src/components/Icon.tsx](/src/components/Icon.tsx) | TypeScript React | 99 | 3 | 7 | 109 |
| [src/config/Menu.ts](/src/config/Menu.ts) | TypeScript | 16 | 0 | 1 | 17 |
| [src/config/config.ts](/src/config/config.ts) | TypeScript | 1 | 2 | 2 | 5 |
| [src/config/index.ts](/src/config/index.ts) | TypeScript | 1 | 0 | 1 | 2 |
| [src/hooks/useParams.ts](/src/hooks/useParams.ts) | TypeScript | 20 | 13 | 5 | 38 |
| [src/hooks/useStore.ts](/src/hooks/useStore.ts) | TypeScript | 4 | 1 | 3 | 8 |
| [src/index.html](/src/index.html) | HTML | 12 | 0 | 3 | 15 |
| [src/layouts/BaseLayout.tsx](/src/layouts/BaseLayout.tsx) | TypeScript React | 50 | 0 | 8 | 58 |
| [src/layouts/Header/components/AvatarIcon.tsx](/src/layouts/Header/components/AvatarIcon.tsx) | TypeScript React | 50 | 12 | 8 | 70 |
| [src/layouts/Header/components/BreadcrumbNav.tsx](/src/layouts/Header/components/BreadcrumbNav.tsx) | TypeScript React | 20 | 0 | 6 | 26 |
| [src/layouts/Header/components/CollapseIcon.tsx](/src/layouts/Header/components/CollapseIcon.tsx) | TypeScript React | 17 | 0 | 4 | 21 |
| [src/layouts/Header/index.tsx](/src/layouts/Header/index.tsx) | TypeScript React | 61 | 0 | 5 | 66 |
| [src/layouts/Menu/components/Logo.tsx](/src/layouts/Menu/components/Logo.tsx) | TypeScript React | 30 | 2 | 5 | 37 |
| [src/layouts/Menu/index.tsx](/src/layouts/Menu/index.tsx) | TypeScript React | 144 | 10 | 16 | 170 |
| [src/layouts/Menu/services/menu.ts](/src/layouts/Menu/services/menu.ts) | TypeScript | 11 | 10 | 4 | 25 |
| [src/less/main.less](/src/less/main.less) | Less | 3 | 0 | 1 | 4 |
| [src/main.tsx](/src/main.tsx) | TypeScript React | 27 | 5 | 8 | 40 |
| [src/pages/Account/List.tsx](/src/pages/Account/List.tsx) | TypeScript React | 131 | 2 | 8 | 141 |
| [src/pages/Account/common/router.tsx](/src/pages/Account/common/router.tsx) | TypeScript React | 14 | 0 | 3 | 17 |
| [src/pages/Account/common/services.ts](/src/pages/Account/common/services.ts) | TypeScript | 23 | 21 | 5 | 49 |
| [src/pages/Account/common/typing.ts](/src/pages/Account/common/typing.ts) | TypeScript | 21 | 0 | 4 | 25 |
| [src/pages/Account/components/add.tsx](/src/pages/Account/components/add.tsx) | TypeScript React | 87 | 1 | 12 | 100 |
| [src/pages/Game/Apply.tsx](/src/pages/Game/Apply.tsx) | TypeScript React | 23 | 0 | 5 | 28 |
| [src/pages/Game/List.tsx](/src/pages/Game/List.tsx) | TypeScript React | 162 | 2 | 8 | 172 |
| [src/pages/Game/Result.tsx](/src/pages/Game/Result.tsx) | TypeScript React | 110 | 2 | 9 | 121 |
| [src/pages/Game/Schedule.tsx](/src/pages/Game/Schedule.tsx) | TypeScript React | 235 | 11 | 11 | 257 |
| [src/pages/Game/common/router.tsx](/src/pages/Game/common/router.tsx) | TypeScript React | 47 | 0 | 3 | 50 |
| [src/pages/Game/common/typing.ts](/src/pages/Game/common/typing.ts) | TypeScript | 120 | 9 | 6 | 135 |
| [src/pages/Game/components/add-game.tsx](/src/pages/Game/components/add-game.tsx) | TypeScript React | 100 | 10 | 11 | 121 |
| [src/pages/Game/components/add-schedule.tsx](/src/pages/Game/components/add-schedule.tsx) | TypeScript React | 198 | 3 | 15 | 216 |
| [src/pages/Game/components/result-detail.tsx](/src/pages/Game/components/result-detail.tsx) | TypeScript React | 81 | 6 | 7 | 94 |
| [src/pages/Game/components/staff-setting.tsx](/src/pages/Game/components/staff-setting.tsx) | TypeScript React | 112 | 1 | 10 | 123 |
| [src/pages/Game/content/apply-audition.tsx](/src/pages/Game/content/apply-audition.tsx) | TypeScript React | 121 | 2 | 8 | 131 |
| [src/pages/Game/content/apply-challenge.tsx](/src/pages/Game/content/apply-challenge.tsx) | TypeScript React | 115 | 2 | 8 | 125 |
| [src/pages/Game/content/apply-detail.tsx](/src/pages/Game/content/apply-detail.tsx) | TypeScript React | 36 | 0 | 4 | 40 |
| [src/pages/Game/content/schedule-detail.tsx](/src/pages/Game/content/schedule-detail.tsx) | TypeScript React | 48 | 0 | 8 | 56 |
| [src/pages/Game/services/apply-audition.ts](/src/pages/Game/services/apply-audition.ts) | TypeScript | 8 | 6 | 3 | 17 |
| [src/pages/Game/services/apply-challenge.ts](/src/pages/Game/services/apply-challenge.ts) | TypeScript | 20 | 18 | 3 | 41 |
| [src/pages/Game/services/game.ts](/src/pages/Game/services/game.ts) | TypeScript | 17 | 15 | 4 | 36 |
| [src/pages/Game/services/result.ts](/src/pages/Game/services/result.ts) | TypeScript | 8 | 6 | 3 | 17 |
| [src/pages/Game/services/schedule.ts](/src/pages/Game/services/schedule.ts) | TypeScript | 20 | 18 | 4 | 42 |
| [src/pages/Game/store/game.ts](/src/pages/Game/store/game.ts) | TypeScript | 80 | 0 | 4 | 84 |
| [src/pages/Home/index.tsx](/src/pages/Home/index.tsx) | TypeScript React | 19 | 0 | 5 | 24 |
| [src/pages/Login/common/services.ts](/src/pages/Login/common/services.ts) | TypeScript | 9 | 8 | 4 | 21 |
| [src/pages/Login/index.tsx](/src/pages/Login/index.tsx) | TypeScript React | 157 | 20 | 14 | 191 |
| [src/pages/Role/Edit.tsx](/src/pages/Role/Edit.tsx) | TypeScript React | 72 | 0 | 11 | 83 |
| [src/pages/Role/List.tsx](/src/pages/Role/List.tsx) | TypeScript React | 103 | 3 | 9 | 115 |
| [src/pages/Role/common/router.tsx](/src/pages/Role/common/router.tsx) | TypeScript React | 25 | 0 | 3 | 28 |
| [src/pages/Role/common/services.ts](/src/pages/Role/common/services.ts) | TypeScript | 20 | 18 | 4 | 42 |
| [src/pages/Role/common/typing.ts](/src/pages/Role/common/typing.ts) | TypeScript | 21 | 0 | 4 | 25 |
| [src/pages/Role/components/Auth.tsx](/src/pages/Role/components/Auth.tsx) | TypeScript React | 29 | 9 | 10 | 48 |
| [src/pages/Role/components/AuthCheck.tsx](/src/pages/Role/components/AuthCheck.tsx) | TypeScript React | 37 | 0 | 7 | 44 |
| [src/pages/Room/List.tsx](/src/pages/Room/List.tsx) | TypeScript React | 129 | 1 | 9 | 139 |
| [src/pages/Room/common/router.tsx](/src/pages/Room/common/router.tsx) | TypeScript React | 14 | 0 | 3 | 17 |
| [src/pages/Room/common/services.ts](/src/pages/Room/common/services.ts) | TypeScript | 26 | 24 | 5 | 55 |
| [src/pages/Room/common/typing.ts](/src/pages/Room/common/typing.ts) | TypeScript | 17 | 5 | 4 | 26 |
| [src/pages/Room/store/room.ts](/src/pages/Room/store/room.ts) | TypeScript | 55 | 6 | 5 | 66 |
| [src/pages/System/Menus/Edit.tsx](/src/pages/System/Menus/Edit.tsx) | TypeScript React | 193 | 7 | 19 | 219 |
| [src/pages/System/Menus/List.tsx](/src/pages/System/Menus/List.tsx) | TypeScript React | 128 | 2 | 11 | 141 |
| [src/pages/System/Menus/common/router.tsx](/src/pages/System/Menus/common/router.tsx) | TypeScript React | 35 | 0 | 3 | 38 |
| [src/pages/System/Menus/common/services.ts](/src/pages/System/Menus/common/services.ts) | TypeScript | 18 | 15 | 6 | 39 |
| [src/pages/System/Menus/common/typing.ts](/src/pages/System/Menus/common/typing.ts) | TypeScript | 32 | 1 | 3 | 36 |
| [src/pages/System/Menus/store/icon.ts](/src/pages/System/Menus/store/icon.ts) | TypeScript | 40 | 1 | 3 | 44 |
| [src/pages/User/List.tsx](/src/pages/User/List.tsx) | TypeScript React | 154 | 2 | 8 | 164 |
| [src/pages/User/Record.tsx](/src/pages/User/Record.tsx) | TypeScript React | 34 | 0 | 3 | 37 |
| [src/pages/User/common/router.tsx](/src/pages/User/common/router.tsx) | TypeScript React | 25 | 0 | 3 | 28 |
| [src/pages/User/common/services.ts](/src/pages/User/common/services.ts) | TypeScript | 20 | 18 | 5 | 43 |
| [src/pages/User/common/typing.ts](/src/pages/User/common/typing.ts) | TypeScript | 25 | 0 | 4 | 29 |
| [src/pages/User/components/add.tsx](/src/pages/User/components/add.tsx) | TypeScript React | 121 | 5 | 10 | 136 |
| [src/pages/User/components/base.tsx](/src/pages/User/components/base.tsx) | TypeScript React | 69 | 0 | 7 | 76 |
| [src/pages/User/components/detail-record.tsx](/src/pages/User/components/detail-record.tsx) | TypeScript React | 117 | 1 | 8 | 126 |
| [src/router/AuthRouter.tsx](/src/router/AuthRouter.tsx) | TypeScript React | 21 | 11 | 9 | 41 |
| [src/router/index.tsx](/src/router/index.tsx) | TypeScript React | 53 | 2 | 5 | 60 |
| [src/router/interface/index.ts](/src/router/interface/index.ts) | TypeScript | 17 | 0 | 3 | 20 |
| [src/services/common.ts](/src/services/common.ts) | TypeScript | 4 | 32 | 2 | 38 |
| [src/store/breadcrumb.ts](/src/store/breadcrumb.ts) | TypeScript | 22 | 0 | 4 | 26 |
| [src/store/index.ts](/src/store/index.ts) | TypeScript | 34 | 4 | 10 | 48 |
| [src/store/menu.ts](/src/store/menu.ts) | TypeScript | 26 | 0 | 4 | 30 |
| [src/store/user.ts](/src/store/user.ts) | TypeScript | 111 | 4 | 9 | 124 |
| [src/typings/account-num.ts](/src/typings/account-num.ts) | TypeScript | 31 | 0 | 1 | 32 |
| [src/typings/account.ts](/src/typings/account.ts) | TypeScript | 23 | 0 | 1 | 24 |
| [src/typings/images.d.ts](/src/typings/images.d.ts) | TypeScript | 7 | 0 | 1 | 8 |
| [src/typings/index.d.ts](/src/typings/index.d.ts) | TypeScript | 2 | 0 | 2 | 4 |
| [src/typings/login.ts](/src/typings/login.ts) | TypeScript | 7 | 0 | 1 | 8 |
| [src/utils/lazyLoad.tsx](/src/utils/lazyLoad.tsx) | TypeScript React | 7 | 20 | 4 | 31 |
| [src/utils/request.ts](/src/utils/request.ts) | TypeScript | 101 | 15 | 15 | 131 |
| [src/utils/utils.ts](/src/utils/utils.ts) | TypeScript | 71 | 32 | 4 | 107 |
[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
\ No newline at end of file
# Diff Details
Date : 2022-08-10 18:18:13
Directory e:\\workspace\\zhds-manage-web\\src
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details
## Files
| filename | language | code | comment | blank | total |
| :--- | :--- | ---: | ---: | ---: | ---: |
[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details
\ No newline at end of file
"filename", "language", "", "comment", "blank", "total"
"Total", "-", , 0, 0, 0
\ No newline at end of file
# Diff Summary
Date : 2022-08-10 18:18:13
Directory e:\\workspace\\zhds-manage-web\\src
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)
\ No newline at end of file
Date : 2022-08-10 18:18:13
Directory : e:\workspace\zhds-manage-web\src
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
Languages
+----------+------------+------------+------------+------------+------------+
| language | files | code | comment | blank | total |
+----------+------------+------------+------------+------------+------------+
+----------+------------+------------+------------+------------+------------+
Directories
+------+------------+------------+------------+------------+------------+
| path | files | code | comment | blank | total |
+------+------------+------------+------------+------------+------------+
+------+------------+------------+------------+------------+------------+
Files
+----------+----------+------------+------------+------------+------------+
| filename | language | code | comment | blank | total |
+----------+----------+------------+------------+------------+------------+
| Total | | 0 | 0 | 0 | 0 |
+----------+----------+------------+------------+------------+------------+
\ No newline at end of file
"filename", "language", "TypeScript React", "TypeScript", "Less", "HTML", "comment", "blank", "total"
"e:\workspace\zhds-manage-web\src\components\DList.tsx", "TypeScript React", 10, 0, 0, 0, 0, 5, 15
"e:\workspace\zhds-manage-web\src\components\DTable.tsx", "TypeScript React", 36, 0, 0, 0, 1, 6, 43
"e:\workspace\zhds-manage-web\src\components\Icon.tsx", "TypeScript React", 99, 0, 0, 0, 3, 7, 109
"e:\workspace\zhds-manage-web\src\config\Menu.ts", "TypeScript", 0, 16, 0, 0, 0, 1, 17
"e:\workspace\zhds-manage-web\src\config\config.ts", "TypeScript", 0, 1, 0, 0, 2, 2, 5
"e:\workspace\zhds-manage-web\src\config\index.ts", "TypeScript", 0, 1, 0, 0, 0, 1, 2
"e:\workspace\zhds-manage-web\src\hooks\useParams.ts", "TypeScript", 0, 20, 0, 0, 13, 5, 38
"e:\workspace\zhds-manage-web\src\hooks\useStore.ts", "TypeScript", 0, 4, 0, 0, 1, 3, 8
"e:\workspace\zhds-manage-web\src\index.html", "HTML", 0, 0, 0, 12, 0, 3, 15
"e:\workspace\zhds-manage-web\src\layouts\BaseLayout.tsx", "TypeScript React", 50, 0, 0, 0, 0, 8, 58
"e:\workspace\zhds-manage-web\src\layouts\Header\components\AvatarIcon.tsx", "TypeScript React", 50, 0, 0, 0, 12, 8, 70
"e:\workspace\zhds-manage-web\src\layouts\Header\components\BreadcrumbNav.tsx", "TypeScript React", 20, 0, 0, 0, 0, 6, 26
"e:\workspace\zhds-manage-web\src\layouts\Header\components\CollapseIcon.tsx", "TypeScript React", 17, 0, 0, 0, 0, 4, 21
"e:\workspace\zhds-manage-web\src\layouts\Header\index.tsx", "TypeScript React", 61, 0, 0, 0, 0, 5, 66
"e:\workspace\zhds-manage-web\src\layouts\Menu\components\Logo.tsx", "TypeScript React", 30, 0, 0, 0, 2, 5, 37
"e:\workspace\zhds-manage-web\src\layouts\Menu\index.tsx", "TypeScript React", 144, 0, 0, 0, 10, 16, 170
"e:\workspace\zhds-manage-web\src\layouts\Menu\services\menu.ts", "TypeScript", 0, 11, 0, 0, 10, 4, 25
"e:\workspace\zhds-manage-web\src\less\main.less", "Less", 0, 0, 3, 0, 0, 1, 4
"e:\workspace\zhds-manage-web\src\main.tsx", "TypeScript React", 27, 0, 0, 0, 5, 8, 40
"e:\workspace\zhds-manage-web\src\pages\Account\List.tsx", "TypeScript React", 131, 0, 0, 0, 2, 8, 141
"e:\workspace\zhds-manage-web\src\pages\Account\common\router.tsx", "TypeScript React", 14, 0, 0, 0, 0, 3, 17
"e:\workspace\zhds-manage-web\src\pages\Account\common\services.ts", "TypeScript", 0, 23, 0, 0, 21, 5, 49
"e:\workspace\zhds-manage-web\src\pages\Account\common\typing.ts", "TypeScript", 0, 21, 0, 0, 0, 4, 25
"e:\workspace\zhds-manage-web\src\pages\Account\components\add.tsx", "TypeScript React", 87, 0, 0, 0, 1, 12, 100
"e:\workspace\zhds-manage-web\src\pages\Game\Apply.tsx", "TypeScript React", 23, 0, 0, 0, 0, 5, 28
"e:\workspace\zhds-manage-web\src\pages\Game\List.tsx", "TypeScript React", 162, 0, 0, 0, 2, 8, 172
"e:\workspace\zhds-manage-web\src\pages\Game\Result.tsx", "TypeScript React", 110, 0, 0, 0, 2, 9, 121
"e:\workspace\zhds-manage-web\src\pages\Game\Schedule.tsx", "TypeScript React", 235, 0, 0, 0, 11, 11, 257
"e:\workspace\zhds-manage-web\src\pages\Game\common\router.tsx", "TypeScript React", 47, 0, 0, 0, 0, 3, 50
"e:\workspace\zhds-manage-web\src\pages\Game\common\typing.ts", "TypeScript", 0, 120, 0, 0, 9, 6, 135
"e:\workspace\zhds-manage-web\src\pages\Game\components\add-game.tsx", "TypeScript React", 100, 0, 0, 0, 10, 11, 121
"e:\workspace\zhds-manage-web\src\pages\Game\components\add-schedule.tsx", "TypeScript React", 198, 0, 0, 0, 3, 15, 216
"e:\workspace\zhds-manage-web\src\pages\Game\components\result-detail.tsx", "TypeScript React", 81, 0, 0, 0, 6, 7, 94
"e:\workspace\zhds-manage-web\src\pages\Game\components\staff-setting.tsx", "TypeScript React", 112, 0, 0, 0, 1, 10, 123
"e:\workspace\zhds-manage-web\src\pages\Game\content\apply-audition.tsx", "TypeScript React", 121, 0, 0, 0, 2, 8, 131
"e:\workspace\zhds-manage-web\src\pages\Game\content\apply-challenge.tsx", "TypeScript React", 115, 0, 0, 0, 2, 8, 125
"e:\workspace\zhds-manage-web\src\pages\Game\content\apply-detail.tsx", "TypeScript React", 36, 0, 0, 0, 0, 4, 40
"e:\workspace\zhds-manage-web\src\pages\Game\content\schedule-detail.tsx", "TypeScript React", 48, 0, 0, 0, 0, 8, 56
"e:\workspace\zhds-manage-web\src\pages\Game\services\apply-audition.ts", "TypeScript", 0, 8, 0, 0, 6, 3, 17
"e:\workspace\zhds-manage-web\src\pages\Game\services\apply-challenge.ts", "TypeScript", 0, 20, 0, 0, 18, 3, 41
"e:\workspace\zhds-manage-web\src\pages\Game\services\game.ts", "TypeScript", 0, 17, 0, 0, 15, 4, 36
"e:\workspace\zhds-manage-web\src\pages\Game\services\result.ts", "TypeScript", 0, 8, 0, 0, 6, 3, 17
"e:\workspace\zhds-manage-web\src\pages\Game\services\schedule.ts", "TypeScript", 0, 20, 0, 0, 18, 4, 42
"e:\workspace\zhds-manage-web\src\pages\Game\store\game.ts", "TypeScript", 0, 80, 0, 0, 0, 4, 84
"e:\workspace\zhds-manage-web\src\pages\Home\index.tsx", "TypeScript React", 19, 0, 0, 0, 0, 5, 24
"e:\workspace\zhds-manage-web\src\pages\Login\common\services.ts", "TypeScript", 0, 9, 0, 0, 8, 4, 21
"e:\workspace\zhds-manage-web\src\pages\Login\index.tsx", "TypeScript React", 157, 0, 0, 0, 20, 14, 191
"e:\workspace\zhds-manage-web\src\pages\Role\Edit.tsx", "TypeScript React", 72, 0, 0, 0, 0, 11, 83
"e:\workspace\zhds-manage-web\src\pages\Role\List.tsx", "TypeScript React", 103, 0, 0, 0, 3, 9, 115
"e:\workspace\zhds-manage-web\src\pages\Role\common\router.tsx", "TypeScript React", 25, 0, 0, 0, 0, 3, 28
"e:\workspace\zhds-manage-web\src\pages\Role\common\services.ts", "TypeScript", 0, 20, 0, 0, 18, 4, 42
"e:\workspace\zhds-manage-web\src\pages\Role\common\typing.ts", "TypeScript", 0, 21, 0, 0, 0, 4, 25
"e:\workspace\zhds-manage-web\src\pages\Role\components\Auth.tsx", "TypeScript React", 29, 0, 0, 0, 9, 10, 48
"e:\workspace\zhds-manage-web\src\pages\Role\components\AuthCheck.tsx", "TypeScript React", 37, 0, 0, 0, 0, 7, 44
"e:\workspace\zhds-manage-web\src\pages\Room\List.tsx", "TypeScript React", 129, 0, 0, 0, 1, 9, 139
"e:\workspace\zhds-manage-web\src\pages\Room\common\router.tsx", "TypeScript React", 14, 0, 0, 0, 0, 3, 17
"e:\workspace\zhds-manage-web\src\pages\Room\common\services.ts", "TypeScript", 0, 26, 0, 0, 24, 5, 55
"e:\workspace\zhds-manage-web\src\pages\Room\common\typing.ts", "TypeScript", 0, 17, 0, 0, 5, 4, 26
"e:\workspace\zhds-manage-web\src\pages\Room\store\room.ts", "TypeScript", 0, 55, 0, 0, 6, 5, 66
"e:\workspace\zhds-manage-web\src\pages\System\Menus\Edit.tsx", "TypeScript React", 193, 0, 0, 0, 7, 19, 219
"e:\workspace\zhds-manage-web\src\pages\System\Menus\List.tsx", "TypeScript React", 128, 0, 0, 0, 2, 11, 141
"e:\workspace\zhds-manage-web\src\pages\System\Menus\common\router.tsx", "TypeScript React", 35, 0, 0, 0, 0, 3, 38
"e:\workspace\zhds-manage-web\src\pages\System\Menus\common\services.ts", "TypeScript", 0, 18, 0, 0, 15, 6, 39
"e:\workspace\zhds-manage-web\src\pages\System\Menus\common\typing.ts", "TypeScript", 0, 32, 0, 0, 1, 3, 36
"e:\workspace\zhds-manage-web\src\pages\System\Menus\store\icon.ts", "TypeScript", 0, 40, 0, 0, 1, 3, 44
"e:\workspace\zhds-manage-web\src\pages\User\List.tsx", "TypeScript React", 154, 0, 0, 0, 2, 8, 164
"e:\workspace\zhds-manage-web\src\pages\User\Record.tsx", "TypeScript React", 34, 0, 0, 0, 0, 3, 37
"e:\workspace\zhds-manage-web\src\pages\User\common\router.tsx", "TypeScript React", 25, 0, 0, 0, 0, 3, 28
"e:\workspace\zhds-manage-web\src\pages\User\common\services.ts", "TypeScript", 0, 20, 0, 0, 18, 5, 43
"e:\workspace\zhds-manage-web\src\pages\User\common\typing.ts", "TypeScript", 0, 25, 0, 0, 0, 4, 29
"e:\workspace\zhds-manage-web\src\pages\User\components\add.tsx", "TypeScript React", 121, 0, 0, 0, 5, 10, 136
"e:\workspace\zhds-manage-web\src\pages\User\components\base.tsx", "TypeScript React", 69, 0, 0, 0, 0, 7, 76
"e:\workspace\zhds-manage-web\src\pages\User\components\detail-record.tsx", "TypeScript React", 117, 0, 0, 0, 1, 8, 126
"e:\workspace\zhds-manage-web\src\router\AuthRouter.tsx", "TypeScript React", 21, 0, 0, 0, 11, 9, 41
"e:\workspace\zhds-manage-web\src\router\index.tsx", "TypeScript React", 53, 0, 0, 0, 2, 5, 60
"e:\workspace\zhds-manage-web\src\router\interface\index.ts", "TypeScript", 0, 17, 0, 0, 0, 3, 20
"e:\workspace\zhds-manage-web\src\services\common.ts", "TypeScript", 0, 4, 0, 0, 32, 2, 38
"e:\workspace\zhds-manage-web\src\store\breadcrumb.ts", "TypeScript", 0, 22, 0, 0, 0, 4, 26
"e:\workspace\zhds-manage-web\src\store\index.ts", "TypeScript", 0, 34, 0, 0, 4, 10, 48
"e:\workspace\zhds-manage-web\src\store\menu.ts", "TypeScript", 0, 26, 0, 0, 0, 4, 30
"e:\workspace\zhds-manage-web\src\store\user.ts", "TypeScript", 0, 111, 0, 0, 4, 9, 124
"e:\workspace\zhds-manage-web\src\typings\account-num.ts", "TypeScript", 0, 31, 0, 0, 0, 1, 32
"e:\workspace\zhds-manage-web\src\typings\account.ts", "TypeScript", 0, 23, 0, 0, 0, 1, 24
"e:\workspace\zhds-manage-web\src\typings\images.d.ts", "TypeScript", 0, 7, 0, 0, 0, 1, 8
"e:\workspace\zhds-manage-web\src\typings\index.d.ts", "TypeScript", 0, 2, 0, 0, 0, 2, 4
"e:\workspace\zhds-manage-web\src\typings\login.ts", "TypeScript", 0, 7, 0, 0, 0, 1, 8
"e:\workspace\zhds-manage-web\src\utils\lazyLoad.tsx", "TypeScript React", 7, 0, 0, 0, 20, 4, 31
"e:\workspace\zhds-manage-web\src\utils\request.ts", "TypeScript", 0, 101, 0, 0, 15, 15, 131
"e:\workspace\zhds-manage-web\src\utils\utils.ts", "TypeScript", 0, 71, 0, 0, 32, 4, 107
"Total", "-", 3706, 1109, 3, 12, 460, 529, 5819
\ No newline at end of file
# Summary
Date : 2022-08-10 18:18:13
Directory e:\\workspace\\zhds-manage-web\\src
Total : 89 files, 4830 codes, 460 comments, 529 blanks, all 5819 lines
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| TypeScript React | 48 | 3,706 | 158 | 369 | 4,233 |
| TypeScript | 39 | 1,109 | 302 | 156 | 1,567 |
| HTML | 1 | 12 | 0 | 3 | 15 |
| Less | 1 | 3 | 0 | 1 | 4 |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| . | 89 | 4,830 | 460 | 529 | 5,819 |
| components | 3 | 145 | 4 | 18 | 167 |
| config | 3 | 18 | 2 | 4 | 24 |
| hooks | 2 | 24 | 14 | 8 | 46 |
| layouts | 8 | 383 | 34 | 56 | 473 |
| layouts\\Header | 4 | 148 | 12 | 23 | 183 |
| layouts\\Header\\components | 3 | 87 | 12 | 18 | 117 |
| layouts\\Menu | 3 | 185 | 22 | 25 | 232 |
| layouts\\Menu\\components | 1 | 30 | 2 | 5 | 37 |
| layouts\\Menu\\services | 1 | 11 | 10 | 4 | 25 |
| less | 1 | 3 | 0 | 1 | 4 |
| pages | 54 | 3,681 | 281 | 356 | 4,318 |
| pages\\Account | 5 | 276 | 24 | 32 | 332 |
| pages\\Account\\common | 3 | 58 | 21 | 12 | 91 |
| pages\\Account\\components | 1 | 87 | 1 | 12 | 100 |
| pages\\Game | 20 | 1,661 | 111 | 134 | 1,906 |
| pages\\Game\\common | 2 | 167 | 9 | 9 | 185 |
| pages\\Game\\components | 4 | 491 | 20 | 43 | 554 |
| pages\\Game\\content | 4 | 320 | 4 | 28 | 352 |
| pages\\Game\\services | 5 | 73 | 63 | 17 | 153 |
| pages\\Game\\store | 1 | 80 | 0 | 4 | 84 |
| pages\\Home | 1 | 19 | 0 | 5 | 24 |
| pages\\Login | 2 | 166 | 28 | 18 | 212 |
| pages\\Login\\common | 1 | 9 | 8 | 4 | 21 |
| pages\\Role | 7 | 307 | 30 | 48 | 385 |
| pages\\Role\\common | 3 | 66 | 18 | 11 | 95 |
| pages\\Role\\components | 2 | 66 | 9 | 17 | 92 |
| pages\\Room | 5 | 241 | 36 | 26 | 303 |
| pages\\Room\\common | 3 | 57 | 29 | 12 | 98 |
| pages\\Room\\store | 1 | 55 | 6 | 5 | 66 |
| pages\\System | 6 | 446 | 26 | 45 | 517 |
| pages\\System\\Menus | 6 | 446 | 26 | 45 | 517 |
| pages\\System\\Menus\\common | 3 | 85 | 16 | 12 | 113 |
| pages\\System\\Menus\\store | 1 | 40 | 1 | 3 | 44 |
| pages\\User | 8 | 565 | 26 | 48 | 639 |
| pages\\User\\common | 3 | 70 | 18 | 12 | 100 |
| pages\\User\\components | 3 | 307 | 6 | 25 | 338 |
| router | 3 | 91 | 13 | 17 | 121 |
| router\\interface | 1 | 17 | 0 | 3 | 20 |
| services | 1 | 4 | 32 | 2 | 38 |
| store | 4 | 193 | 8 | 27 | 228 |
| typings | 5 | 70 | 0 | 6 | 76 |
| utils | 3 | 179 | 67 | 23 | 269 |
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
\ No newline at end of file
{
"presets": [
"@babel/preset-env",
["@babel/preset-react",{
"runtime": "automatic"
}],
"@babel/preset-typescript"
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"regenerator": true
}
],
[
"import",
{
"libraryName": "antd", // 指定导入包的名称
"libraryDirectory": "lib", // 指定模块的存放目录
"style": true // 导入 css 样式
}
]
]
}
# API_HOST="http://192.168.31.145:9888"
# API_HOST="http://192.168.31.15:9888"
# API_HOST="http://192.168.31.157:9888"
# API_HOST="http://zhds-admin.viz-cloud.top:9888"
API_HOST="http://81.70.97.9:8083/interact"
COS_HOST = "http://fe-assets-1301947356.cos.ap-beijing.myqcloud.com/zhds-h5/index.html"
PUBLIC_PATH = "/"
\ No newline at end of file
config/
scripts/
build/
node_modules/
\.eslintrc.js
\ No newline at end of file
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
'plugin:react/recommended',
'standard'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true
},
ecmaVersion: 'latest',
sourceType: 'module'
},
plugins: [
'react',
'@typescript-eslint',
'eslint-plugin-import-helpers'
],
rules: {
'import-helpers/order-imports': [
'error',
{ // example configuration
newlinesBetween: 'always',
groups: [
'/^react/',
'module',
'/antd/',
'/^@//',
'absolute'
],
alphabetize: { order: 'asc', ignoreCase: true }
}
],
'react/jsx-uses-react': 'off',
'react/react-in-jsx-scope': 'off',
'no-fallthrough': 'off',
'no-debugger': 'off'
}
}
.DS_Store
node_modules/
dist/
dist.zip
npm-debug.log*
yarn-debug.log*
yarn-error.log*
test/unit/coverage
test/e2e/reports
selenium-debug.log
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
engine-strict = true
**/*.md
**/*.svg
**/*.html
package.json
{
"printWidth": 120,
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"jsxBracketSameLine": true,
"arrowParens": "avoid",
"insertPragma": false,
"tabWidth": 2,
"useTabs": false,
"overrides": [
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
}
]
}
\ No newline at end of file
{
"extends": "stylelint-config-standard"
}
\ No newline at end of file
# 按钮权限
## 用户管理
```
查看 : sys:tbuser:page;sys:tbuser:info
保存 : sys:tbuser:save
修改 : sys:tbuser:update
删除 : sys:tbuser:delete
```
## 房间管理
```
查看 : sys:tbroom:page,sys:tbroom:info;sys:tbuser:list
保存 : sys:tbroom:save
修改 : sys:tbroom:update
删除 : sys:tbroom:delete
```
## 账号管理
```
查看 : sys:user:page,sys:user:info
新增 : sys:user:save
修改 : sys:user:update
删除 : sys:user:delete
```
## 角色管理
```
查看 : sys:role:page,sys:role:info,sys:role:list
新增 : sys:role:save
修改 : sys:role:update
删除 : sys:role:delete
```
## 菜单管理
```
查看 : sys:menu:list,sys:menu:info
新增 : sys:menu:save
修改 : sys:menu:update
删除 : sys:menu:delete
```
## 比赛管理
```
查看 : sys:tbschedule:page,sys:tbschedule:info
删除 : sys:tbschedule:delete
保存 : sys:tbschedule:save
修改 : sys:tbschedule:update
擂台报名管理-查看 :sys:tbapplylt:page,sys:tbapplylt:info
擂台报名管理-保存 :sys:tbapplylt:save
擂台报名管理-修改 :sys:tbapplylt:update
擂台报名管理-删除 :sys:tbapplylt:delete
海选报名管理-查看 :sys:tbapplyxb:page,sys:tbapplyxb:info
海选报名管理-保存 :sys:tbapplyxb:save
海选报名管理-修改 :sys:tbapplyxb:update
海选报名管理-删除 :sys:tbapplyxb:delete
赛程选拔-查看 :sys:tbschedulexb:page,sys:tbschedulexb:info
赛程选拔-保存 :sys:tbschedulexb:save
赛程选拔-修改 :sys:tbschedulexb:update
赛程选拔-删除 :sys:tbschedulexb:delete
```
\ No newline at end of file
// import AddAssetHtmlPlugin from 'add-asset-html-webpack-plugin'
import chalk from 'chalk'
import CopyWebpackPlugin from 'copy-webpack-plugin'
import dotenv from 'dotenv'
import ESLintPlugin from 'eslint-webpack-plugin'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import path from 'path'
import ProgressBarPlugin from 'progress-bar-webpack-plugin'
import webpack from 'webpack'
// import AntdDayjsWebpackPlugin from 'antd-dayjs-webpack-plugin'
dotenv.config()
const Resolve = (...args: string[]) => {
return path.resolve(__dirname, '../', ...args)
}
const isProd = process.env.NODE_ENV === 'production'
const exclude = /node_modules/
const include = /src/
const BaseConfig: webpack.Configuration = {
entry: ['./src/main.tsx'],
output: {
path: Resolve('dist'),
filename: 'js/[name].[contenthash].js',
chunkFilename: 'js/[name].chunk.js',
publicPath: process.env.PUBLIC_PATH
},
module: {
rules: [
{
// 同时认识ts jsx js tsx 文件
test: /\.(t|j)sx?$/,
exclude,
include,
use: [
{
loader: 'babel-loader',
options: {
// 缓存:第二次构建时,会读取之前的缓存
cacheDirectory: true
}
}
]
},
{
test: /\.less$/,
include: [/src/, /node_modules/],
use: [
isProd ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'postcss-preset-env',
'autoprefixer'
]
},
sourceMap: true
}
},
{
loader: 'less-loader',
options: {
lessOptions: { // 如果使用less-loader@5,请移除 lessOptions 这一级直接配置选项。
modifyVars: {
// 'primary-color': '#1DA57A',
// 'link-color': '#1DA57A',
// 'border-radius-base': '2px'
},
javascriptEnabled: true
}
}
}
]
},
{
test: /\.css$/,
include: [/src/, /node_modules\/@ant-design/, /node_modules\/antd/, /node_modules\/@wangeditor/],
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
exclude,
include,
use: [
{
loader: 'file-loader',
options: {
limit: 5000,
esModule: false,
// 分离图片至imgs文件夹
name () {
if (process.env.NODE_ENV === 'development') {
return '[path][name].[ext]'
}
return 'images/[contenthash].[ext]'
}
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
exclude,
include,
use: [
{
loader: 'file-loader',
options: {
name () {
if (process.env.NODE_ENV === 'development') {
return '[path][name].[ext]'
}
return 'fonts/[contenthash].[ext]'
}
}
}
]
}
]
},
plugins: [
/**
* @description 定义全局变量插件
*/
new webpack.EnvironmentPlugin(['NODE_ENV', 'API_HOST', 'COS_HOST']),
/**
* @description 复制资源插件
*/
new CopyWebpackPlugin({
patterns: [
{
from: Resolve('public'),
to: Resolve('dist')
}
]
}),
/**
* @description webpack 构建进度条
*/
new (ProgressBarPlugin as any)({
width: 50, // 默认20,进度格子数量即每个代表进度数,如果是20,那么一格就是5。
format: chalk.blue.bold('build') + chalk.yellow('[:bar] ') + chalk.green.bold(':percent') + ' (:elapsed秒)',
complete: '-', // 默认“=”,完成字符
clear: false // 默认true,完成时清除栏的选项
}),
new ESLintPlugin({
fix: true /* 自动帮助修复 */,
extensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'react'],
exclude: 'node_modules'
}),
new HtmlWebpackPlugin({
inject: true,
template: path.resolve(__dirname, '../src/index.html')
})
// new AntdDayjsWebpackPlugin()
// ==== dll 配置 ====
// new webpack.DllReferencePlugin({
// context: __dirname,
// manifest: require('../dll/vendor.manifest.json')
// }),
// // 打包后的 .dll.js 文件需要引入到 html中,可以通过 add-asset-html-webpack-plugin 插件自动引入
// new AddAssetHtmlPlugin({
// filepath: path.resolve('./dll/vendor.dll.js'),
// publicPath: ''
// })
],
resolve: {
extensions: ['.js', '.json', '.jsx', '.less', '.css', '.vue', '.json', '.ts', '.tsx'],
alias: {
'@': Resolve('src')
}
},
cache: {
type: 'filesystem'
},
watchOptions: {
ignored: /node_modules/
},
/**
* @description 设置webpack 构建时候信息输出
*/
stats: {
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}
}
export default BaseConfig
// import webpack from 'webpack'
import path from 'path'
import UnusedWebpackPlugin from 'unused-webpack-plugin'
import merge from 'webpack-merge'
import BaseConfig from './webpack.base.config'
const DevConfig = merge(BaseConfig, {
mode: 'development',
devtool: 'eval',
output: {
filename: '[name].bundle.js',
pathinfo: false
},
devServer: {
hot: true,
open: true,
// host: HOST,
// stats: 'errors-warnings',
compress: false,
historyApiFallback: true
},
// plugins: [new webpack.HotModuleReplacementPlugin()],
plugins: [
new UnusedWebpackPlugin({
directories: [path.join(__dirname, 'src')],
root: path.join(__dirname, '../')
})
]
} as any)
module.exports = DevConfig
import path from 'path'
import webpack from 'webpack'
module.exports = {
mode: 'production',
name: 'vendor',
entry: ['react', 'react-dom', 'react-redux', 'react-router-dom'], // 这个例子我们打包 lodash 作为公共类库
output: {
path: path.resolve('dll'),
filename: 'vendor.dll.js',
library: 'vendor_[hash]' // 打包后对外暴露的类库名称
},
plugins: [
new webpack.DllPlugin({
name: 'vendor_[hash]',
path: path.resolve('dll/vendor.manifest.json') // 使用 DLLPlugin 在打包的时候生成一个 manifest 文件
})
]
}
import { CleanWebpackPlugin } from 'clean-webpack-plugin'
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import TerserPlugin from 'terser-webpack-plugin'
// import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
// import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
import merge from 'webpack-merge'
import BaseConfig from './webpack.base.config'
const DevConfig = merge(BaseConfig, {
mode: 'production',
optimization: {
splitChunks: {
chunks: 'all', // initial(初始块)、async(按需加载块)、all(全部块),默认为all
minChunks: 2, // 表示被引用次数,默认为1;
// maxInitialRequests: 5, //最大的按需(异步)加载次数,默认为1;
// minSize: 2, //表示在压缩前的最小模块大小,默认为0;
// maxInitialRequests: 1, //最大的初始化加载次数,默认为1;
minSize: 1,
cacheGroups: {
commons: {
name: 'common' // 拆分出来块的名字(Chunk Names),默认由块名和hash值自动生成
}
}
},
minimizer: [
new TerserPlugin({
parallel: true
// sourceMap: true,
})
]
},
plugins: [
new CleanWebpackPlugin({
}),
new MiniCssExtractPlugin({
filename: 'css/[name].[chunkhash].css'
}),
new CssMinimizerPlugin()
// new BundleAnalyzerPlugin()
]
})
module.exports = DevConfig
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "ig-package-manage-web",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"preinstall": "only-allow npm",
"dev": "cross-env ENV=local NODE_ENV=development node --no-deprecation node_modules/webpack-dev-server/bin/webpack-dev-server.js --config build/webpack.dev.config.ts",
"build": "cross-env ENV=prod NODE_ENV=production webpack --config build/webpack.prod.config.ts",
"dll": "webpack --config build/webpack.dll.config.ts",
"fix": "eslint --fix"
},
"repository": {
"type": "git",
"url": "git@gitlab.viz-cloud.top:fe/product/ig-packag-manage-web.git"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.18.6",
"@babel/plugin-transform-runtime": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@types/lodash": "^4.14.182",
"@types/progress-bar-webpack-plugin": "^2.1.2",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@types/styled-components": "^5.1.25",
"@types/unused-webpack-plugin": "^2.4.2",
"@types/webpack-bundle-analyzer": "^4.4.1",
"@typescript-eslint/eslint-plugin": "^5.30.5",
"@typescript-eslint/parser": "^5.30.5",
"add-asset-html-webpack-plugin": "^5.0.2",
"antd-dayjs-webpack-plugin": "^1.0.6",
"autoprefixer": "^10.4.7",
"babel-loader": "^8.2.5",
"babel-plugin-import": "^1.13.5",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^4.0.0",
"dotenv": "^16.0.1",
"eslint": "^8.19.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-import-helpers": "^1.2.1",
"eslint-plugin-n": "^15.2.4",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.30.1",
"eslint-webpack-plugin": "^3.2.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0",
"less": "^4.1.3",
"less-loader": "^11.0.0",
"mini-css-extract-plugin": "^2.6.1",
"only-allow": "^1.1.1",
"postcss-loader": "^7.0.0",
"postcss-preset-env": "^7.7.2",
"progress-bar-webpack-plugin": "^2.1.0",
"style-loader": "^3.3.1",
"stylelint-config-standard": "^26.0.0",
"terser-webpack-plugin": "^5.3.3",
"ts-node": "^10.8.2",
"typescript": "^4.7.4",
"unused-webpack-plugin": "^2.4.0",
"webpack": "^5.73.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.3",
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@ant-design/icons": "^4.7.0",
"@ant-design/pro-components": "^1.1.5",
"@ant-design/pro-form": "^1.69.3",
"@ant-design/pro-layout": "^6.38.4",
"@ant-design/pro-table": "^2.75.1",
"@reduxjs/toolkit": "^1.8.3",
"@wangeditor/editor": "^5.1.15",
"@wangeditor/editor-for-react": "^1.0.4",
"ahooks": "^3.5.2",
"antd": "^4.21.5",
"axios": "^0.27.2",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"rc-tween-one": "^3.0.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.2",
"react-router-dom": "^6.3.0",
"redux-persist": "^6.0.0",
"styled-components": "^5.3.5"
},
"engines": {
"node": "18.x || 16.x"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
"eslint --fix",
"git add"
]
}
}
import React, { PropsWithChildren } from 'react'
import styled from 'styled-components'
type Props = PropsWithChildren<{}>
export function DList (props: Props) {
return <Warp>{props.children}</Warp>
}
const Warp = styled.div`
background-color: #f0f2f5;
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
`
import React, { useMemo, useRef } from 'react'
import ProTable, { ActionType, ProTableProps } from '@ant-design/pro-table'
export function DTable<T, U, ValueType = 'text'> (props: ProTableProps<T, U, ValueType>) {
return (
<>
<ProTable
rowKey="id" // 表格单行使用那个字段作为唯一标识
cardBordered={true}
editable={{
type: 'multiple'
}}
pagination={{ pageSize: 10 }}
dateFormatter="string" // 时间格式设置
options={{
// 工具栏配置 不需要时直接为false即可
fullScreen: false, // 全屏
search: false, // 搜索
setting: true, // 列设置
reload: true, // 刷新
density: true // 密度
}}
{...props}
/>
</>
)
}
export function usePTable<T = any, U = any, ValueType = 'text'> (options: ProTableProps<T, U, ValueType>) {
const actionRef = useRef<ActionType>()
const tableProps = useMemo(() => {
return {
actionRef,
...options
}
}, [options])
return {
tableProps,
actionRef
}
}
import React, { useState } from 'react'
import { ProFormText } from '@ant-design/pro-form'
import styled from 'styled-components'
import { useAppSelector } from '@/hooks/useStore'
type TProps = {
handleIconClick: (id: string) => void
}
const MyIcon = ({ handleIconClick }: TProps) => {
const iconData = useAppSelector((state) => { return state.icon.iconData })
const [isShowIcon, setIsShowIcon] = useState<boolean>(false)
const [currentIcon, setCurrentIcon] = useState<string>('')
const handleFocus = () => {
// 获取焦点事件
setIsShowIcon(true)
}
const handelBlur = () => {
// 失去焦点事件
setIsShowIcon(false)
}
// 图标点击事件
const handleClick = (data: { font_class: string, icon_id: string }) => {
handleIconClick(data.font_class)
setCurrentIcon(data.icon_id)
setIsShowIcon(false)
}
const handleMouseUp = (e: any) => {
e.returnValue = true
}
const handleMouseDown = (e: any) => {
e.preventDefault()
}
return (
<MyIconLayout>
<ProFormText
width="md"
name="icon"
label="图标"
fieldProps={{
onFocus: handleFocus,
onBlur: handelBlur
}}
placeholder="请选择图标"
/>
{isShowIcon && (
<div className="iconList">
{iconData?.glyphs.map(item => {
return (
<div key={item.icon_id} onMouseUp={(e) => { handleMouseUp(e) }} onMouseDown={(e) => { handleMouseDown(e) }} onClick={() => handleClick(item)}>
<span className={` ${item.icon_id === currentIcon ? 'icon' : ''} iconfont icon-package${item.font_class}`}></span>
</div>
)
})}
</div>
)}
</MyIconLayout>
)
}
export default MyIcon
const MyIconLayout = styled.div`
width: 100%;
height: 100%;
position: relative;
ProFormText {
width: 90%;
margin: 10px auto;
/* height: 10px; */
}
.iconList {
display: flex;
z-index: 1;
text-align: center;
overflow: hidden;
::-webkit-scrollbar {
display: none;
}
overflow-y: auto;
background: #fff;
position: absolute;
top: 80px;
width: 90%;
height: 456px;
border: 1px solid #ebeef5;
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
align-items: center;
justify-content: center;
flex-wrap: wrap;
> div {
width: 40px;
height: 40px;
line-height: 40px;
margin: 10px;
border: 1px #ccc solid;
text-align: center;
> span {
font-size: 30px;
}
.icon{
color: #3a8ee6;
}
}
}
`
export const MENUS = [
{
path: '/',
name: '系统管理',
routes: [
{
path: '/',
name: '首页'
},
{
path: 'about',
name: '关于我们'
}
]
}
]
// ? 全局不动配置项 只做导出不做修改
// * 首页地址(默认)
export const HOME_URL: string = '/home'
export const API_HOST = process.env.API_HOST
export const COS_HOST = process.env.COS_HOST
import { useMemo } from 'react'
export const useGetParams = () => {
const { params } = useMemo(() => {
const url = window.location.href
const d: Record<string, string|undefined> = {}
if (url?.indexOf('?') !== -1) {
const arr = url.slice(url?.indexOf('?') + 1).split('&')
arr.forEach(item => {
const [key, val] = item.split('=') || []
d[key] = val
})
}
return {
params: d
}
}, [])
// const [params, setParams] = useState<Record<string, any>>({})
// useEffect(() => {
// const url = window.location.href
// const d: Record<string, unknown> = {}
// if (url.indexOf('?') !== -1) {
// const arr = url.slice(url.indexOf('?') + 1).split('&')
// arr.forEach(item => {
// const [key, val] = item.split('=') || []
// d[key] = val
// })
// }
// setParams(d)
// }, [])
return {
params
}
}
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from '../store'
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>配置管理平台</title>
<link rel="stylesheet" href="https://fe-assets-1301947356.cos.ap-beijing.myqcloud.com/ig-packag-manage-web/iconFont/iconfont.css">
</head>
<body>
<link href="https://unpkg.com/@wangeditor/editor@latest/dist/css/style.css" rel="stylesheet"></link>
<div id="root"></div>
</body>
</html>
\ No newline at end of file
import React, { Suspense } from 'react'
import { Outlet } from 'react-router-dom'
import styled from 'styled-components'
import { Layout, Spin } from 'antd'
import { useAppSelector } from '@/hooks/useStore'
import LayoutHeader from './Header/index'
import LayoutMenu from './Menu/index'
const { Content, Sider } = Layout
const App = () => {
const isCollapse = useAppSelector(state => state.menu.isCollapse)
return (
<Layout>
<Sider trigger={null} collapsed={isCollapse} width={220} theme="light">
<LayoutMenu />
</Sider>
<Layout style={{ minHeight: '100vh' }}>
<LayoutHeader />
<DLayout>
<DContent>
<Suspense
fallback={
<Spin
size="large"
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100%'
}}
/>
}>
<Outlet />
</Suspense>
</DContent>
</DLayout>
</Layout>
</Layout>
)
}
export default App
const DContent = styled(Content)`
/* background: #fff; */
/* padding: 16px; */
margin: 0;
min-height: 280px;
`
const DLayout = styled(Layout)`
padding: 16px 20px 0;
/* min-height: calc(100vh - 55px); */
`
import { Put } from '@/utils/request'
/**
* @description 修改密码
*/
export const PasswordApi = (params: any): Promise<any> => {
return Put('/hzy-admin/sys/user/password', { params })
}
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { ExclamationCircleOutlined } from '@ant-design/icons'
import { Avatar, Modal, Menu, Dropdown, message } from 'antd'
import { HOME_URL } from '@/config/config'
import { useAppDispatch } from '@/hooks/useStore'
import { setLogin } from '@/store/user'
import UserPassword from './update-password'
const AvatarIcon = () => {
const dispatch = useAppDispatch()
const navigate = useNavigate()
const [passwordVisible, setPasswordVisible] = useState(false)
// 退出登录
const logout = () => {
Modal.confirm({
title: '温馨提示 🧡',
icon: <ExclamationCircleOutlined />,
content: '是否确认退出登录?',
okText: '确认',
cancelText: '取消',
onOk: () => {
dispatch(setLogin({}))
message.success('退出登录成功!')
navigate('/login')
}
})
}
// 修改密码
const handlePassword = () => {
setPasswordVisible(true)
}
const handleUpdatePassword = (value:boolean) => {
setPasswordVisible(value)
}
// Dropdown Menu
const menu = (
<Menu
items={[
{
key: '1',
label: <span className="dropdown-item">首页</span>,
onClick: () => navigate(HOME_URL)
},
// {
// key: '2',
// label: <span className="dropdown-item">个人信息</span>,
// onClick: () => infoRef.current!.showModal({ name: 11 })
// },
{
key: '3',
label: <span className="dropdown-item">修改密码</span>,
onClick: handlePassword
},
{
type: 'divider'
},
{
key: '4',
label: <span className="dropdown-item">退出登录</span>,
onClick: logout
}
]}></Menu>
)
return (
<>
<Dropdown overlay={menu} placement="bottom" arrow trigger={['click']}>
<Avatar src="https://lf1-xgcdn-tos.pstatp.com/obj/vcloud/vadmin/start.8e0e4855ee346a46ccff8ff3e24db27b.png" />
</Dropdown>
<UserPassword
handleUpdatePassword={handleUpdatePassword}
visible={passwordVisible}></UserPassword>
</>
)
}
export default AvatarIcon
import { Link, useLocation } from 'react-router-dom'
import { Breadcrumb } from 'antd'
import { HOME_URL } from '@/config/config'
import { useAppSelector } from '@/hooks/useStore'
const BreadcrumbNav = (props: any) => {
const breadcrumb = useAppSelector(state => state.breadcrumb)
const { pathname } = useLocation()
const breadcrumbList = breadcrumb.breadcrumbList[pathname] || []
return (
<Breadcrumb>
<Breadcrumb.Item>
<Link to={HOME_URL}>首页</Link>
</Breadcrumb.Item>
{breadcrumbList.map((item: string) => {
return <Breadcrumb.Item key={item}>{item !== '首页' ? item : null}</Breadcrumb.Item>
})}
</Breadcrumb>
)
}
export default BreadcrumbNav
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'
import { useAppDispatch, useAppSelector } from '@/hooks/useStore'
import { setIsCollapse } from '@/store/menu'
const CollapseIcon = () => {
const isCollapse = useAppSelector(state => state.menu.isCollapse)
const dispatch = useAppDispatch()
return (
<div
className="collapsed"
onClick={() => {
dispatch(setIsCollapse(!isCollapse))
}}>
{isCollapse ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</div>
)
}
export default CollapseIcon
import React from 'react'
import { ModalForm, ProFormText } from '@ant-design/pro-form'
import { message } from 'antd'
import { TAccountNumUpdate } from '@/typings/account-num'
import { PasswordApi } from '../common/services'
type TProps = {
visible: boolean;
handleUpdatePassword: (values: boolean) => void;
};
const UserPassword = ({ visible, handleUpdatePassword }: TProps) => {
const title = '修改密码'
const cancelClick = () => {
handleUpdatePassword(false)
}
const okClick = async (data: any) => {
const param = {
newPassword: data?.newPassword,
password: data?.password
}
try {
const { code } = await PasswordApi(param)
if (code === 0) {
message.success('修改成功')
handleUpdatePassword(false)
} else {
message.error('修改失败')
}
} catch (error) {
console.log(error)
}
}
return (
<ModalForm<TAccountNumUpdate>
title={title}
visible={visible}
autoFocusFirstInput
modalProps={{
onCancel: cancelClick,
destroyOnClose: true
}}
onFinish={async (e) => {
okClick(e)
}}
layout="horizontal"
>
<ProFormText
width="md"
name="password"
label="原密码"
placeholder="请输入账号"
rules={[{ required: true, message: '请输入原密码' }]}
/>
<ProFormText
width="md"
name="newPassword"
label="新密码"
placeholder="请输入账号"
rules={[{ required: true, message: '请输入新密码' }]}
/>
</ModalForm>
)
}
export default UserPassword
import styled from 'styled-components'
import { Layout } from 'antd'
import { useAppSelector } from '@/hooks/useStore'
import AvatarIcon from './components/AvatarIcon'
import BreadcrumbNav from './components/BreadcrumbNav'
import CollapseIcon from './components/CollapseIcon'
const { Header } = Layout
const LayoutHeader = () => {
const { username } = useAppSelector(({ user }) => (user))
return (
<DHeader>
<div className="header-lf">
<CollapseIcon />
<BreadcrumbNav />
</div>
<div className="header-ri">
<span className="username">{username}</span>
<AvatarIcon />
</div>
</DHeader>
)
}
const DHeader = styled(Header)`
display: flex;
align-items: center;
justify-content: space-between;
height: 55px;
padding: 0 40px 0 20px;
background-color: #ffffff;
border-bottom: 1px solid #e5e6eb;
.header-lf {
display: flex;
align-items: center;
.collapsed {
margin-right: 20px;
font-size: 18px;
cursor: pointer;
transition: color 0.3s;
}
}
.header-ri {
display: flex;
align-items: center;
.icon-style {
margin-right: 22px;
font-size: 19px;
line-height: 19px;
color: rgb(77 77 77);
cursor: pointer;
}
.username {
margin: 0 20px 0 0;
font-size: 15px;
color: #000000;
}
.ant-avatar {
cursor: pointer;
}
}
`
export default LayoutHeader
import styled from 'styled-components'
// import logo from '@/assets/images/logo.png'
import { useAppSelector } from '@/hooks/useStore'
const Logo = () => {
const isCollapse = useAppSelector(state => state.menu.isCollapse)
return (
<LogoWrap className="logo-box">
{/* <img src={logo} alt="logo" className="logo-img" /> */}
{!isCollapse ? <h2 className="logo-text">配置管理平台</h2> : null}
</LogoWrap>
)
}
const LogoWrap = styled.div`
display: flex;
align-items: center;
justify-content: center;
height: 55px;
border-bottom: 1px solid #e5e6eb;
.logo-img {
width: 30px;
margin: 0;
}
.logo-text {
margin: 0 0 0 10px;
font-size: 20px;
font-weight: 500;
color: #1d2129;
font-family: PingFang SC;
white-space: nowrap;
}
`
export default Logo
import React, { useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import styled from 'styled-components'
import { Menu, MenuProps, Spin } from 'antd'
import { ItemType } from 'antd/lib/menu/hooks/useItems'
import { useAppSelector, useAppDispatch } from '@/hooks/useStore'
import { setBreadcrumbList } from '@/store/breadcrumb'
import { getPermissions, setMenuListData } from '@/store/menu'
import { setAuthRouter } from '@/store/user'
import { findAllBreadcrumb, getOpenKeys, handleRouter, searchRoute } from '@/utils/utils'
import Logo from './components/Logo'
// import { MenuNavApi } from './services/menu'
type MenuItem = Required<MenuProps>['items'][number]
const LayoutMenu = () => {
const isCollapse = useAppSelector(state => state.menu.isCollapse)
const menuStore = useAppSelector(state => state.menu)
const dispatch = useAppDispatch()
const loopMenuItem = (menus: any): ItemType[] => {
return menus.map(
(item: {
label: React.ReactNode
key: React.Key
path: any
icon?: any
children?: MenuItem[]
type?: string
hidden?: boolean
}) => {
return {
key: item.path,
label: item.label,
icon: (<span className={item.icon}></span>),
children: item.children && loopMenuItem(item.children),
type: item.type || '',
hidden: item.hidden || false
}
}
)
}
const navigate = useNavigate()
const [loading, setLoading] = useState(false)
const { pathname } = useLocation()
const [selectedKeys, setSelectedKeys] = useState<string[]>([pathname])
const [openKeys, setOpenKeys] = useState<string[]>([])
const [initListData, setInitListData] = useState<any[]>([])
const [menuList, setMenuList] = useState<MenuItem[]>([]) // 获取菜单列表并处理成 antd menu 需要的格式
const defaultMenus = [
{
path: '/home',
label: '首页'
},
{
path: '/parameter',
label: '演播室管理'
}
]
// 初始化菜单列表
const initList = async () => {
// const { data } = await MenuNavApi()
// setInitListData(handleDeepList(data))
setInitListData(defaultMenus)
}
// const handleDeepList = (data: any[]): any => {
// if (!data.length) {
// return null
// }
// const newData: any[] = []
// for (let index = 0; index < data.length; index++) {
// const element = data[index]
// const tempMenuItem = {
// path: element.url,
// label: element.name,
// icon: `iconfont ${element.icon}`,
// children: handleDeepList(element.children)
// }
// if (data[index].type === 0) {
// if (Array.isArray(tempMenuItem.children)) {
// tempMenuItem.children = tempMenuItem.children.length ? tempMenuItem.children : null
// }
// newData.push(tempMenuItem)
// }
// }
// return newData
// }
// 设置当前展开的 subMenu
const onOpenChange = (openKeys: string[]) => {
if (openKeys.length === 0 || openKeys.length === 1) return setOpenKeys(openKeys)
const latestOpenKey = openKeys[openKeys.length - 1]
if (latestOpenKey.includes(openKeys[0])) return setOpenKeys(openKeys)
setOpenKeys([latestOpenKey])
}
// 刷新高亮
useEffect(() => {
setSelectedKeys([pathname])
!isCollapse && setOpenKeys(getOpenKeys(pathname))
}, [pathname, isCollapse])
// 获取动态MenuList
useEffect(() => {
initList()
dispatch(getPermissions())
setLoading(true)
// 存储处理过后的所有面包屑导航栏到 redux 中
}, [])
useEffect(() => {
if (initListData.length) {
dispatch(setBreadcrumbList(findAllBreadcrumb(initListData)))
setTimeout(() => {
setMenuList(loopMenuItem(initListData))
// 收起侧边栏 不展示subMenu
setLoading(false)
console.log('[ loading ]', loading)
}, 100)
// 把路由菜单处理成一维数组,存储到 redux 中,做菜单权限判断
const dynamicRouter = handleRouter(initListData)
dispatch(setAuthRouter(dynamicRouter))
// getMenuApi
dispatch(setMenuListData(initListData))
}
}, [initListData])
// 点击当前菜单跳转页面
const clickMenu: MenuProps['onClick'] = ({ key }: { key: string }) => {
const route = searchRoute(key, menuStore.menuListData)
console.log('[ route ]', route, menuStore.menuListData)
// if (route.isLink) window.open(route.isLink, '_blank')
navigate(key)
}
return (
<MenuWrap>
<Spin spinning={loading} tip="Loading...">
<Logo></Logo>
<Menu
theme="light"
mode="inline"
triggerSubMenuAction="click"
openKeys={openKeys}
selectedKeys={selectedKeys}
items={menuList}
onClick={clickMenu}
onOpenChange={onOpenChange}
/>
</Spin>
</MenuWrap>
)
}
const MenuWrap = styled.div`
height: 100%;
.ant-spin-nested-loading {
height: 100%;
.ant-spin-container {
height: 100%;
}
}
`
export default LayoutMenu
import { Get, TResult } from '@/utils/request'
const PATHS = {
MENU_LIST: `${process.env.API_HOST}/hzy-admin/sys/menu/list`,
MENU_NAV: `${process.env.API_HOST}/hzy-admin/sys/menu/nav`,
MENU_PER: `${process.env.API_HOST}/hzy-admin/sys/menu/permissions`
}
/**
* @description: 列表查询
* @param {TLogin} params
* @return {*}
*/
export const MenuListApi = (): TResult => {
return Get(PATHS.MENU_LIST)
}
/**
* @description: 菜单导航
* @param {*} TResult
* @return {*}
*/
export const MenuNavApi = (): TResult => {
return Get(PATHS.MENU_NAV)
}
/**
* @description: 按钮权限
* @param {*} TResult
* @return {*}
*/
export const MenuPerApi = (): TResult => {
return Get(PATHS.MENU_PER)
}
.app {
color: rgb(21, 255, 0);
}
import ReactDOM from 'react-dom/client'
/**
* React Router
* */
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import dayjs from 'dayjs'
import { PersistGate } from 'redux-persist/integration/react'
import { ConfigProvider } from 'antd'
import zhCN from 'antd/lib/locale/zh_CN'
import { store, persistor } from '@/store'
import Routes from './router'
import AuthRouter from './router/AuthRouter'
import 'dayjs/locale/zh-cn'
dayjs.locale('zh-cn')
const ROOT = document.getElementById('root')
const App = () => (
// <React.StrictMode>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<BrowserRouter>
<ConfigProvider locale={zhCN} componentSize="middle" input={{ autoComplete: 'off' }}>
<AuthRouter>
<Routes />
</AuthRouter>
</ConfigProvider>
</BrowserRouter>
</PersistGate>
</Provider>
// </React.StrictMode>
)
ROOT && ReactDOM.createRoot(ROOT).render(<App />)
import React, { useCallback, useState } from 'react'
import { Button, message, Popconfirm, Space } from 'antd'
import { DList } from '@/components/DList'
import { DTable, usePTable } from '@/components/DTable'
import { useAppSelector } from '@/hooks/useStore'
import { DelApi, GetListByPageApi } from './common/services'
import { TState } from './common/typing'
import Add from './components/add'
import UpdatePassword from './components/update-password'
const AccountList = () => {
const { permissionsData, account } = useAppSelector(({ menu }) => (menu))
const [dataSource, setDataSource] = useState<TState[]>([])
const [currentId, setCurrentId] = useState<number>(NaN)
const [isModalVisible, setIsModalVisible] = useState(false)
const [passwordVisible, setPasswordVisible] = useState(false)
const isSearch: any = permissionsData.includes(account?.info1) || permissionsData.includes(account?.info2)
const { tableProps, actionRef } = usePTable<TState>({
headerTitle: '账号列表',
search: isSearch,
request: useCallback(async (params: any) => {
const result = { data: [], total: 1, success: true }
try {
const { pageSize: limit, current: page, username } = params
const { data } = await GetListByPageApi({
limit, // 每页显示记录数
page, // 当前页码,从1开始
username,
order: '', // 排序方式,可选值(asc、desc)
orderField: '' // 排序字段
})
const { list, total } = data
setDataSource(list)
result.data = list
result.total = total
} catch (error) {
console.error('请求列表失败', error)
}
return result
}, []),
columns: [
{
title: 'ID',
dataIndex: 'id',
width: 100,
copyable: true,
hideInSearch: true
},
{
title: '账号名',
dataIndex: 'username',
copyable: true,
ellipsis: true,
hideInSearch: false
},
{
title: '昵称',
key: 'realName',
dataIndex: 'realName',
hideInSearch: true
},
{
title: '角色',
key: 'roleIdList',
dataIndex: 'roleIdList',
hideInSearch: true
},
{
title: '状态',
key: 'status',
dataIndex: 'status',
hideInSearch: true
},
{
title: '操作',
valueType: 'option',
render: (text, record) => [
<Space key="operate" size={20}>
{permissionsData.includes(account?.update) && (
<a
key="editable"
onClick={() => handleClickOperation('Edit', record)}>
编辑
</a>
)}
<a
key="password"
onClick={() => handleClickOperation('Password', record)}>
修改密码
</a>
{permissionsData.includes(account?.delete) && (
<Popconfirm
title="确定删除该条数据?"
onConfirm={() => handleClickOperation('Del', record)}
okText="Yes"
cancelText="No"
>
<a key="delete"> 删除 </a>
</Popconfirm>
)}
</Space>
]
}
],
toolBarRender: useCallback(() => [
// 工具栏设置
(permissionsData.includes(account?.save) && (
<Button key="tool" type="primary" onClick={() => handleUpdateVisible(true, '')}>
创建账号
</Button>
))
], [])
})
// 表格-操作
const handleClickOperation = useCallback(
async (type: 'Edit' | 'Del' | 'Password', record: TState) => {
switch (type) {
case 'Edit':
setIsModalVisible(true)
setCurrentId(record.id)
break
case 'Password':
setPasswordVisible(true)
setCurrentId(record.id)
break
case 'Del':
try {
await DelApi([record.id])
message.success('删除成功')
dataSource.length <= 1 ? actionRef.current?.reloadAndRest?.() : actionRef.current?.reload?.()
} catch (error) {
console.log(error)
message.error('删除失败')
}
break
}
},
[dataSource]
)
const handleUpdateVisible = (value: boolean, type: string) => {
setIsModalVisible(value)
if (!value) {
setCurrentId(NaN)
}
if (type === 'add') {
(actionRef.current as any).reload()
}
}
const handleUpdatePassword = (value: boolean) => {
setPasswordVisible(value)
}
return (
<>
<DList>
<DTable {...tableProps} />
</DList>
<Add visible={isModalVisible} handleUpdateVisible={handleUpdateVisible} id={currentId} />
<UpdatePassword
id={currentId}
handleUpdatePassword={handleUpdatePassword}
visible={passwordVisible}
/>
</>
)
}
export default AccountList
import { lazy } from 'react'
const List = lazy(() => import('../List'))
export default [
{
path: '/account',
name: '账号管理',
element: <List />,
meta: {
requiresAuth: true,
title: '账号管理',
key: 'user'
}
}
]
import { Delete, Get, Post, Put } from '@/utils/request'
import { TAccountList, TAccountUpdate } from './typing'
/**
* @description 列表查询
*/
export const GetListByPageApi = (params: TAccountList): Promise<any> => {
return Get('/hzy-admin/sys/user/page', { params })
}
/**
* @description 删除
*/
export const DelApi = (params: number[]): Promise<any> => {
return Delete('/hzy-admin/sys/user', { params })
}
/**
* @description 修改
*/
export const UpdateApi = (params: Partial<TAccountUpdate>): Promise<any> => {
return Put('/hzy-admin/sys/user', { params })
}
/**
* @description 添加
*/
export const AddApi = (params: Partial<TAccountUpdate>): Promise<any> => {
return Post('/hzy-admin/sys/user', { params })
}
/**
* @description 详情
*/
export const GetDetailApi = (id: number): Promise<any> => {
return Get(`/hzy-admin/sys/user/${id}`)
}
/**
* @description 获取角色
*/
export const GetRoleApi = (): Promise<any> => {
return Get('/hzy-admin/sys/role/list')
}
/**
* @description 修改密码
*/
export const PasswordUpdateApi = (params: any): Promise<any> => {
return Put('/hzy-admin/sys/user/update/password', { params })
}
export type TAccountList = {
limit: number // 每页显示记录数
page: number // 当前页码,从1开始
order: string // 排序方式,可选值(asc、desc)
orderField: string // 排序字段
username: string // 用户名
}
export type TAccountUpdate = {
username: string // 角色名称
password: string // 密码
realName: string // 昵称
status: number // 状态
roleIdList: any // 角色列表
id: number
}
export type TState = TAccountUpdate
import React, { useCallback, useEffect, useState } from 'react'
import { ModalForm, ProFormSelect, ProFormSwitch, ProFormText } from '@ant-design/pro-form'
import { message } from 'antd'
import { TUpdate } from '@/typings/account'
import { AddApi, GetDetailApi, GetRoleApi, UpdateApi } from '../common/services'
type TProps = {
visible: boolean;
id: number;
handleUpdateVisible: (value: boolean, type: string) => void;
};
const Add = ({ visible, id, handleUpdateVisible }: TProps) => {
const [title, setTitle] = useState<string>('')
const [roleData, setRoleData] = useState<any[]>([])
useEffect(() => {
getRole()
if (id) {
setTitle('编辑账号')
return
}
setTitle('创建账号')
}, [visible])
const getRole = useCallback(async () => {
const arr:any = []
try {
const { data } = await GetRoleApi()
console.log(data)
for (let i = 0; i < data.length; i++) {
arr.push({
label: data[i].name,
value: data[i].id
})
}
setRoleData(arr)
} catch (error) {
console.log(error)
}
}, [id])
const fetchDetail = useCallback(async () => {
let data: any = {
username: '', // 角色名称
password: '', // 密码
realName: '', // 昵称
status: null, // 状态
roleIdList: [] // 角色列表
}
if (id) {
try {
const res = await GetDetailApi(id)
if (res.code !== 0) return
const status = !!res?.data?.status
res.data.status = status
data = res?.data
} catch (error) {
console.log(error)
}
}
data.roleIdList = data.roleIdList?.[0]
return data
}, [id])
const cancelClick = () => {
handleUpdateVisible(false, '')
}
const okClick = async (values: any) => {
const params:any = {
username: values?.username, // 角色名称
password: values?.password, // 密码
realName: values?.realName, // 昵称
roleIdList: [values?.roleIdList], // 角色列表
status: Number(values?.status)
}
try {
if (id) {
params.id = id
}
const { code } = id ? await UpdateApi(params) : await AddApi(params)
code === 0 ? message.success('编辑成功') : message.success('创建成功')
handleUpdateVisible(false, 'add')
} catch (error) {
console.log(error)
}
}
return (
<ModalForm<TUpdate>
title={title}
visible={visible}
autoFocusFirstInput
modalProps={{
onCancel: cancelClick,
destroyOnClose: true
}}
onFinish={async (e) => {
okClick(e)
}}
request={fetchDetail}
// layout="horizontal"
>
<ProFormText width="md" name="username" label="账号" placeholder="请输入账号名称" rules={[{ required: true, message: '请输入账号名称' }]}
/>
{!id && (<ProFormText width="md" name="password" label="密码" placeholder="请输入账号密码" rules={[{ required: true, message: '请输入账号密码' }]} />)}
<ProFormText width="md" name="realName" label="昵称" placeholder="请输入账号昵称" />
<ProFormSwitch name="status" label="状态" />
<ProFormSelect width="md" name="roleIdList" label="角色" options={roleData} placeholder="请选择角色"
/>
</ModalForm>
)
}
export default Add
import React from 'react'
import { ModalForm, ProFormText } from '@ant-design/pro-form'
import { message } from 'antd'
import { TAccountNumUpdate } from '@/typings/account-num'
import { PasswordUpdateApi } from '../common/services'
type TProps = {
visible: boolean;
id: number;
handleUpdatePassword: (values: boolean) => void;
};
const AddAccount = ({ visible, id, handleUpdatePassword }: TProps) => {
const title = '修改密码'
const cancelClick = () => {
handleUpdatePassword(false)
}
const okClick = async (data: any) => {
const param = {
userId: id,
newPassword: data?.newPassword,
password: data?.password
}
try {
const { code } = await PasswordUpdateApi(param)
if (code === 0) {
message.success('修改成功')
handleUpdatePassword(false)
} else {
message.error('修改失败')
}
} catch (error) {
console.log(error)
}
}
return (
<ModalForm<TAccountNumUpdate>
title={title}
visible={visible}
autoFocusFirstInput
modalProps={{
onCancel: cancelClick,
destroyOnClose: true
}}
onFinish={async (e) => {
okClick(e)
}}
layout="horizontal"
>
<ProFormText
width="md"
name="password"
label="原密码"
placeholder="请输入账号"
rules={[{ required: true, message: '请输入原密码' }]}
/>
<ProFormText
width="md"
name="newPassword"
label="新密码"
placeholder="请输入账号"
rules={[{ required: true, message: '请输入新密码' }]}
/>
</ModalForm>
)
}
export default AddAccount
import styled from 'styled-components'
import welcome from '@/assets/images/welcome.png'
const Home = () => {
return (
<HomeWrap className="home">
<img src={welcome} alt="welcome" />
</HomeWrap>
)
}
const HomeWrap = styled.div`
display: flex;
align-items: center;
justify-content: center;
height: 100%;
img {
width: 78%;
}
`
export default Home
import { TLogin } from '@/typings/login'
import { Post, TResult } from '@/utils/request'
const PATHS = {
LOGIN: `${process.env.API_HOST}/hzy-admin/login`
}
/**
* 登录响应参数
*/
type LoginRes = { token: string }
/**
* @description: 列表查询
* @param {TLogin} params
* @return {*}
*/
export const LoginApi = (params: TLogin): TResult<LoginRes> => {
return Post(PATHS.LOGIN, { params })
}
import React, { useState, useCallback, useEffect, useRef } from 'react'
import { useNavigate } from 'react-router-dom'
import { UserOutlined, LockOutlined, SafetyOutlined } from '@ant-design/icons'
import styled from 'styled-components'
import { Form, Input, Button, message, Space, Image } from 'antd'
import { useAppDispatch, useAppSelector } from '@/hooks/useStore'
import { setLogin } from '@/store/user'
import { LoginApi } from './common/services'
const LoginPage: React.FC = () => {
const uuid = useRef('')
const [captchaUrl, setCaptchaUrl] = useState<string>('')
const [form] = Form.useForm()
const dispatch = useAppDispatch()
const user = useAppSelector(({ user }) => user)
const navigate = useNavigate()
const users: any = {}
const INIT_DATA = users.remember
? users
: {
username: 'admin',
password: 'admin',
remember: false
}
useEffect(() => {
getCaptcha()
}, [])
// 获取uuid 验证码
const getCaptcha = () => {
const s: any = []
const hexDigits = '0123456789abcdef'
for (let i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = '4' // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1) // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = '-'
const _uuid = s.join('')
uuid.current = _uuid
setCaptchaUrl(`${process.env.API_HOST}/hzy-admin/captcha?uuid=${_uuid}`)
}
const handleClickLogin = useCallback(async ({ remember, captcha, password, username }) => {
try {
// const data = await dispatch(
// loginAsync({
// username,
// password,
// captcha,
// uuid: uuid.current,
// grant_type: 'password'
// })
// )
// console.log('[ data ]', data)
// if (data.payload && data.payload.code === 0) {
// navigate('/home')
// } else {
// message.error(data.payload.msg)
// getCaptcha()
// }
const params = {
username,
password,
captcha,
uuid: uuid.current,
grant_type: 'password'
}
const { data, code, msg } = await LoginApi(params)
if (code === 0) {
getCaptcha()
dispatch(setLogin(data))
navigate('/home')
} else {
msg && message.error(msg)
getCaptcha()
}
} catch (e: any) {
getCaptcha()
console.error(e)
}
}, [])
return (
<LoginLayout>
<LoginContainer>
<LoginFromWarp className="login__warp">
<LoginContainerHeader>配置管理后台</LoginContainerHeader>
<Form form={form} initialValues={INIT_DATA} name="login__form" onFinish={handleClickLogin}>
<Form.Item name="username" rules={[{ required: true, message: '请输入账号' }]}>
<Input prefix={<UserOutlined className="site-form-item-icon" />} size="large" placeholder="账号" />
</Form.Item>
<Form.Item name="password" rules={[{ required: true, message: '请输入密码' }]}>
<Input.Password
prefix={<LockOutlined className="site-form-item-icon" />}
size="large"
type="password"
placeholder="密码"
/>
</Form.Item>
<Space>
<Form.Item name="captcha" rules={[{ required: true, message: '请输入验证码' }]}>
<Input prefix={<SafetyOutlined className="site-form-item-icon" />} size="large" placeholder="验证码" />
</Form.Item>
<div>
<Image width={120} height={40} margin-top={-10} src={captchaUrl} preview={false} onClick={getCaptcha} />
</div>
</Space>
{/* <Form.Item name="remember" valuePropName="checked">
<Checkbox>记住密码</Checkbox>
</Form.Item> */}
<Form.Item>
<Button
type="primary"
htmlType="submit"
size="large"
loading={user.status === 'loading'}
className="submit-btn">
登录
</Button>
</Form.Item>
</Form>
</LoginFromWarp>
</LoginContainer>
</LoginLayout>
)
}
export default LoginPage
const LoginLayout = styled.div`
position: relative;
width: 100%;
height: 100vh;
/* background: #000
url('https://fe-internal-files-1301947356.cos.ap-beijing.myqcloud.com/public/image/background.svg')
no-repeat 50%; */
`
const LoginContainer = styled.div`
width: 500px;
height: 500px;
box-sizing: border-box;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto auto;
background: transparent;
padding: 20px;
.ant-space-align-center {
align-items: flex-start;
}
`
const LoginContainerHeader = styled.h1`
height: 44px;
line-height: 44px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin-bottom: 20px;
font-size: 32px;
> img {
display: block;
height: 44px;
margin-bottom: 10px;
}
> span {
color: rgba(0, 0, 0, 0.85);
font-weight: 600;
}
`
const LoginFromWarp = styled.div`
width: 368px;
margin: 0 auto;
.icon {
color: rgba(0, 0, 0, 0.25);
}
.submit-btn {
width: 100%;
margin-top: 15px;
}
`
import React, { useCallback, useState } from 'react'
import { Button, message, Popconfirm, Space } from 'antd'
import { DList } from '@/components/DList'
import { DTable, usePTable } from '@/components/DTable'
// import { useAppSelector } from '@/hooks/useStore'
import { DelApi, GetListByPageApi } from './common/services'
import { TState } from './common/typing'
import AddUpdate from './components/addUpdate'
const Parameter = () => {
const [dataSource, setDataSource] = useState<TState[]>([])
const [currentId, setCurrentId] = useState<number>(NaN)
const [isModalVisible, setIsModalVisible] = useState<boolean>(false)
const { tableProps, actionRef } = usePTable<TState>({
headerTitle: '演播室列表',
request: useCallback(async (params: any) => {
const result = {
data: [],
total: 1,
success: true
}
try {
const { pageSize, current: pageNum, studioName } = params
const { data } = await GetListByPageApi({
pageBean: {
pageNum,
pageSize
},
studioName
})
const { list, total } = data
setDataSource(list)
result.data = list
result.total = total
} catch (error) {
console.error('请求列表失败', error)
}
return result
}, []),
columns: [
{
title: 'ID',
dataIndex: 'id',
width: 100,
copyable: true,
hideInSearch: true
},
{
title: '演播室名称',
key: 'studioName',
dataIndex: 'studioName',
hideInSearch: false
},
{
title: '演播室id',
key: 'studioId',
dataIndex: 'studioId',
hideInSearch: true
},
{
title: '主服务地址',
key: 'ip1',
dataIndex: 'ip1',
hideInSearch: true,
render: (text, record) => [
<span key='primaryService'>{record?.ip1}:{record?.port1}</span>
]
},
{
title: '备服务地址',
key: 'ip2',
dataIndex: 'ip2',
hideInSearch: true,
render: (text, record) => [
<span key='primaryService'>{record?.ip2}:{record?.port2}</span>
]
},
{
title: '主场景地址',
key: 'sceneNameS',
dataIndex: 'sceneNameS',
hideInSearch: true
},
{
title: '备场景地址',
key: 'sceneNameQ',
dataIndex: 'sceneNameQ',
hideInSearch: true
},
{
title: '弹幕类型',
key: 'type',
dataIndex: 'type',
hideInSearch: true,
render: (text, record) => [
<span key='type'>
{record?.type === '1' && '入库并存为txt'}
{record?.type === '2' && '只保存txt'}
{record?.type === '3' && '发送tcp并存为txt'}
</span>
]
},
{
title: '弹幕轮询时间(秒)',
key: 'intervalTime',
dataIndex: 'intervalTime',
hideInSearch: true
},
{
title: '发电时间间隔(秒)',
key: 'powerTime',
dataIndex: 'powerTime',
hideInSearch: true
},
{
title: '筛选弹幕限制条数',
key: 'bulletChatNums',
dataIndex: 'bulletChatNums',
hideInSearch: true
},
{
title: '创建时间',
key: 'create_time',
dataIndex: 'create_time',
valueType: 'dateTime',
hideInSearch: true
},
{
title: '修改时间',
key: 'update_time',
dataIndex: 'update_time',
valueType: 'dateTime',
hideInSearch: true
},
{
title: '操作',
valueType: 'option',
render: (text, record) => [
<Space key="option" size={20}>
<a key="editable">
编辑
</a>
<Popconfirm
title="确认删除该条数据?"
onConfirm={() => handleClickOperation('Del', record)}
okText="Yes"
cancelText='No'>
<a key="delete">
删除
</a>
</Popconfirm>
</Space>
]
}
],
toolBarRender: useCallback(() => [
<Button key='tool' type="primary" onClick={() => handleUpdateVisible(true, '')} >
创建演播室
</Button>
], [])
})
const handleClickOperation = useCallback( // 表格操作
async (type: 'Edit' | 'Del', record: TState) => {
switch (type) {
case 'Edit':
setIsModalVisible(true)
setCurrentId(record.id)
break
case 'Del':
try {
const { status } = await DelApi({ id: record.id })
if (status !== 200) {
message.error('删除失败')
return
}
message.success('删除成功')
dataSource.length <= 1 ? actionRef.current?.reloadAndRest?.() : actionRef.current?.reload?.()
} catch (error) {
console.log(error)
message.error('删除失败')
}
break
}
}, [dataSource])
const handleUpdateVisible = (value: boolean, type: string) => {
setIsModalVisible(value)
if (!value) {
setCurrentId(NaN)
}
if (type === 'add') {
(actionRef.current as any).reload()
}
}
return (
<>
<DList>
<DTable {...tableProps} />
</DList>
<AddUpdate visible={isModalVisible} handleUpdateVisible={handleUpdateVisible} id={currentId} />
</>
)
}
export default Parameter
import { lazy } from 'react'
const List = lazy(() => import('../List'))
export default [
{
path: '/parameter',
name: '演播室管理',
element: <List />,
meta: {
requiresAuth: true,
title: '演播室管理',
key: 'parameter'
}
}
]
import { Get, Post } from '@/utils/request'
import { TParameterList, TParameterUpdate } from './typing'
/**
* @description 列表查询
*/
export const GetListByPageApi = (params: TParameterList): Promise<any> => {
return Post('/studio/list', { params })
}
/**
* @description 删除
*/
export const DelApi = (params:{}): Promise<any> => {
return Post('/type/del', { params })
}
/**
* @description 修改
*/
export const UpdateApi = (params: Partial<TParameterUpdate>): Promise<any> => {
return Post('/type/update', { params })
}
/**
* @description 添加
*/
export const AddApi = (params: Partial<TParameterUpdate>): Promise<any> => {
return Post('/type/add', { params })
}
/**
* @description 详情
*/
export const GetDetailApi = (id: number): Promise<any> => {
return Get(`/hzy-admin/sys/user/${id}`)
}
export type TParameterList = {
pageBean:{
pageNum: number // 当前页码,从1开始
pageSize:number // 每页显示记录数
},
studioName: string // 演播室名称
// limit: number // 每页显示记录数
// page: number // 当前页码,从1开始
// order: string // 排序方式,可选值(asc、desc)
// orderField: string // 排序字段
// username: string // 用户名
}
export type TParameterUpdate = {
studioName: string // 演播室名称
studioId: string // 演播室id
ip1: string // 主服务ip
ip2: string // 备服务ip
port1: string // 主服务器端口
port2: string // 备服务器端口
sceneNameS: string // 主场景地址
sceneNameQ: string // 备场景地址
createTime: string // 创建时间
updateTime: string // 修改时间
type:string // 弹幕类型 1.入库并存为TXT,2.只保存txt,3.发送tcp并存为txt
intervalTime: number // 弹幕轮询时间间隔
bulletChatNums: number // 筛选弹幕限制数量
powerTime: number // 发电时刻开启服务时,默认时间间隔
id: number
}
export type TState = TParameterUpdate
import { useState, useEffect, useCallback } from 'react'
import ProForm, { ModalForm, ProFormDigit, ProFormSelect, ProFormText } from '@ant-design/pro-form'
import { message } from 'antd'
import { GetDetailApi, UpdateApi, AddApi } from '../common/services'
type TProps = {
visible: boolean,
id: number,
handleUpdateVisible: (value:boolean, type: string) => void
}
const AddUpdate = ({ id, visible, handleUpdateVisible }:TProps) => {
const [title, setTitle] = useState<string>('')
useEffect(() => {
setTitle(() => {
return id ? '编辑演播室' : '创建演播室'
})
}, [visible])
const fetchDetail = useCallback(async () => {
let data:any = {
studioName: '', // 演播室名称
studioId: '', // 演播室id
ip1: '', // 主服务ip
ip2: '', // 备服务ip
port1: '', // 主服务器端口
port2: '', // 备服务器端口
sceneNameS: '', // 主场景地址
sceneNameQ: '', // 备场景地址
createTime: '', // 创建时间
updateTime: '', // 修改时间
type: '', // 弹幕类型
intervalTime: 3, // 弹幕轮询时间间隔
bulletChatNums: 0, // 筛选弹幕限制数量
powerTime: 3 // 发电时刻开启服务时,默认时间间隔
}
if (id) {
try {
const res = await GetDetailApi(id)
if (res.status !== 200) return
data = res?.data
} catch (error) {
console.log(error)
}
}
return data
}, [id])
const cnacelClick = () => {
handleUpdateVisible(false, '')
}
const okClick = async (values:any) => {
const params:any = {
studioName: values.studioName, // 演播室名称
studioId: values.studioId, // 演播室id
ip1: values.ip1, // 主服务ip
ip2: values.ip2, // 备服务ip
port1: values.port1, // 主服务器端口
port2: values.port2, // 备服务器端口
sceneNameS: values.sceneNameS, // 主场景地址
sceneNameQ: values.sceneNameQ, // 备场景地址
type: values.type, // 弹幕类型
intervalTime: values.intervalTime, // 弹幕轮询时间间隔
bulletChatNums: values.bulletChatNums, // 筛选弹幕限制数量
powerTime: values.powerTime// 发电时刻开启服务时,默认时间间隔
}
try {
if (id) {
params.id = id
}
console.log('params===>', params)
const { status } = id ? await UpdateApi(params) : await AddApi(params)
if (status === 200) {
id ? message.success('编辑成功') : message.success('创建成功')
handleUpdateVisible(false, 'add')
}
} catch (error) {
console.log(error)
}
}
return (
<ModalForm
title={title}
visible={visible}
autoFocusFirstInput
modalProps={{
onCancel: cnacelClick,
destroyOnClose: true
}}
onFinish={async (e) => {
okClick(e)
}}
request={fetchDetail}>
<ProForm.Group>
<ProFormText width="md" name="studioName" label="演播室名称" placeholder="请输入演播室名称" />
<ProFormDigit width="md" name="studioId" label="演播室id" placeholder="请输入演播室id" />
</ProForm.Group>
<ProForm.Group>
<ProFormText width="md" name="ip1" label="主服务ip" placeholder="请输入主服务ip" />
<ProFormText width="md" name="port1" label="主服务器端口" placeholder="请输入主服务器端口" />
</ProForm.Group>
<ProForm.Group>
<ProFormText width="md" name="ip2" label="备服务ip" placeholder="请输入备服务ip" />
<ProFormText width="md" name="port2" label="备服务器端口" placeholder="请输入备服务器端口" />
</ProForm.Group>
<ProForm.Group>
<ProFormText width="md" name="sceneNameS" label="主场景地址" placeholder="请输入主场景地址" />
<ProFormText width="md" name="sceneNameQ" label="备场景地址" placeholder="请输入备场景地址" />
</ProForm.Group>
<ProForm.Group>
<ProFormSelect
width="md"
label="弹幕类型"
name="type"
placeholder='请选择弹幕类型'
valueEnum={{
1: '入库并存为txt',
2: '只保存txt',
3: '发送tcp并存为txt'
}}
/>
<ProFormDigit width="md" name="intervalTime" label="弹幕轮询时间间隔" addonAfter="秒" placeholder="请输入弹幕轮询时间间隔" />
</ProForm.Group>
<ProForm.Group>
<ProFormDigit width="md" name="bulletChatNums" label="筛选弹幕限制条数" placeholder="请输入筛选弹幕限制数量" />
<ProFormDigit width="md" name="powerTime" label="发电轮询时间间隔" addonAfter="秒" placeholder="请输入发电轮询时间间隔" />
</ProForm.Group>
</ModalForm>
)
}
export default AddUpdate
import { createSlice } from '@reduxjs/toolkit'
type TParameter = {}
const initialState:TParameter = {
data: ''
}
export const { reducer: parameterReducer, actions } = createSlice({
name: 'parameter',
initialState,
reducers: {},
extraReducers: {}
})
export default parameterReducer
import { Navigate, useLocation } from 'react-router-dom'
import { HOME_URL } from '@/config/config'
import { useAppSelector } from '@/hooks/useStore'
// import { store } from '@/store'
import { searchRoute } from '@/utils/utils'
import { rootRouter } from './index'
/**
* @description 路由守卫组件
* */
const AuthRouter = (props: any) => {
const userStore = useAppSelector(state => state.user)
const { pathname } = useLocation()
const route = searchRoute(pathname, rootRouter)
// console.log('[ route ]', rootRouter)
// * 在跳转路由之前,清除所有的请求
// axiosCanceler.removeAllPending()
// * 判断当前路由是否需要访问权限(不需要权限直接放行)
if (!route?.meta?.requiresAuth) return props.children
// * 判断是否有Token
// const token = store.getState().user.token
// if (!token) return <Navigate to="/login" replace />
// * Dynamic Router(动态路由,根据后端返回的菜单数据生成的一维数组)
const dynamicRouter = userStore.authRouter
// * Static Router(静态路由,必须配置首页地址,否则不能进首页获取菜单、按钮权限等数据),获取数据的时候会loading,所有配置首页地址也没问题
const staticRouter = [HOME_URL, '/403']
const routerList = dynamicRouter?.concat(staticRouter)
// * 如果访问的地址没有在路由表中重定向到403页面
if (routerList?.indexOf(pathname) === -1) return <Navigate to="/403" />
// * 当前账号有权限返回 Router,正常访问页面
return props.children
}
export default AuthRouter
import React, { lazy } from 'react'
import { Navigate, useRoutes } from 'react-router-dom'
import BaseLayout from '@/layouts/BaseLayout'
import AccountRouter from '@/pages/Account/common/router'
import Login from '@/pages/Login'
import ParameterRouter from '@/pages/Parameter/common/router'
import { RouteObject } from './interface'
const Home = lazy(() => import('@/pages/Home/index'))
export const rootRouter: RouteObject[] = [
{
path: '/',
element: <Navigate to="/home" />
},
{
element: <BaseLayout />,
children: [
{
path: '/home',
name: '首页',
element: <Home />,
meta: {
requiresAuth: true,
title: '首页',
key: 'home'
}
},
...AccountRouter,
...ParameterRouter
]
},
{
path: '/login',
element: <Login />,
meta: {
requiresAuth: false,
title: '登录页',
key: 'login'
}
}
// 异常页面
// { path: '*', element: <Pack /> }
]
const Routes = () => {
const routes = useRoutes(rootRouter)
return routes
}
export default Routes
import { ReactNode } from 'react'
export interface MetaProps {
keepAlive?: boolean
requiresAuth?: boolean
title: string
key?: string
}
export interface RouteObject {
caseSensitive?: boolean
children?: RouteObject[]
element?: ReactNode
index?: boolean
path?: string
meta?: MetaProps
isLink?: string
name?: string
}
import { TResult, Post } from '@/utils/request'
// const UserDb: any = new Map([
// [
// "admin",
// {
// password: "1q2w3e4r",
// res: {
// username: "管理员",
// auth: 0,
// token: "eqweqweqwsd"
// }
// }
// ]
// ]);
/**
* @description 登录
* @api http://api-center-bigdata.huan.tv/project/529/interface/api/8925
*/
export const LoginApi = (params: Record<string, any>): TResult => {
// return new Promise((resolve, reject) => {
// const { password: inputPassword, account } = params;
// if (!UserDb.has(account)) {
// reject("用户名不存在");
// }
// const { res, password } = UserDb.get(account);
// if (!inputPassword || inputPassword !== password) {
// reject("密码错误");
// }
// resolve({
// code: 200,
// msg: "登录成功",
// data: res
// });
// });
return Post('/api/admin/outer/auth/login?encrypt=false', { params })
}
import { createSlice } from '@reduxjs/toolkit'
export interface BreadcrumbState {
breadcrumbList: {
[key: string]: any
}
}
const initialState: BreadcrumbState = {
breadcrumbList: {}
}
export const { reducer: breadcrumbReducer, actions } = createSlice({
name: 'account',
initialState,
reducers: {
setBreadcrumbList: (state, { payload }: { payload: Record<string, any> }) => {
console.log('[ setBreadcrumbList ]', payload)
state.breadcrumbList = payload
}
},
extraReducers: {}
})
export const { setBreadcrumbList } = actions
export default breadcrumbReducer
import { combineReducers, configureStore } from '@reduxjs/toolkit'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/es/storage'
// import GameReducer from '@/pages/Game/store/game'
// import RoomReducer from '@/pages/Room/store/room'
// import IconReducer from '../pages/System/Menus/store/icon'
import BreadcrumbReducer from './breadcrumb'
import MenuReducer from './menu'
import UserReducer from './user'
// 创建reducer(拆分reducer)
const reducer = combineReducers({
user: UserReducer,
menu: MenuReducer,
breadcrumb: BreadcrumbReducer
// icon: IconReducer
// room: RoomReducer,
// game: GameReducer
})
// redux 持久化配置
const persistConfig = {
key: 'redux-state',
storage
}
const persistedReducer = persistReducer(persistConfig, reducer)
// 创建 store
const store = configureStore({
reducer: persistedReducer,
devTools: process.env.NODE_ENV !== 'production',
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false
})
})
// 创建持久化 store
const persistor = persistStore(store)
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export { store, persistor }
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
// import { MenuPerApi } from '@/layouts/Menu/services/menu'
export interface MenuState {
isCollapse: boolean,
menuListData: any[],
permissionsData: any[],
user: any,
room: any,
account: any,
role:any,
menu:any,
schedule: any
}
const initialState: MenuState = {
isCollapse: false,
menuListData: [],
permissionsData: [],
user: {
info1: 'sys:tbuser:page',
info2: 'sys:tbuser:info',
save: 'sys:tbuser:save',
update: 'sys:tbuser:update',
delete: 'sys:tbuser:delete'
},
room: {
info1: 'sys:tbroom:page',
info2: 'sys:tbroom:info',
info3: 'sys:tbuser:list',
save: 'sys:tbroom:save',
update: 'sys:tbroom:update',
delete: 'sys:tbroom:delete'
},
account: {
info1: 'sys:user:page',
info2: 'sys:user:info',
save: 'sys:user:save',
update: 'sys:user:update',
delete: 'sys:user:delete'
},
role: {
info1: 'sys:role:page',
info2: 'sys:role:info',
info3: 'sys:role:list',
save: 'sys:role:save',
update: 'sys:role:update',
delete: 'sys:role:delete'
},
menu: {
info1: 'sys:menu:list',
info2: 'sys:menu:info',
save: 'sys:menu:save',
update: 'sys:menu:update',
delete: 'sys:menu:delete'
},
schedule: {
info1: 'sys:tbschedule:page',
info2: 'sys:tbschedule:info',
save: 'sys:tbschedule:save',
update: 'sys:tbschedule:update',
delete: 'sys:tbschedule:delete',
LtInfo1: 'sys:tbapplylt:page',
LtInfo2: 'sys:tbapplylt:info',
LtSave: 'sys:tbapplylt:save',
LtUpdate: 'sys:tbapplylt:update',
LtDelete: 'sys:tbapplylt:delete',
HxInfo1: 'sys:tbapplyxb:page',
HxInfo2: 'sys:tbapplyxb:info',
HxSave: 'sys:tbapplyxb:save',
HxUpdate: 'sys:tbapplyxb:update',
HxDelete: 'sys:tbapplyxb:delete',
ScInfo1: 'sys:tbschedulexb:page',
ScInfo2: 'sys:tbschedulexb:info',
ScSave: 'sys:tbschedulexb:save',
ScUpdate: 'sys:tbschedulexb:update',
ScDelete: 'sys:tbschedulexb:delete'
}
}
export const getPermissions = createAsyncThunk('permissions', async () => {
// const { data } = await MenuPerApi()
// const data = []
const data:any = []
return data
})
export const { reducer, actions } = createSlice({
name: 'menu',
initialState: { ...initialState },
reducers: {
setMenuListData: (state, { payload }: { payload: any[] }) => {
console.log('[ setMenuListData ]', payload)
state.menuListData = payload
},
setIsCollapse: (state, { payload }: { payload: boolean }) => {
console.log('[ setIsCollapse ]', payload)
state.isCollapse = payload
}
},
extraReducers: {
[getPermissions.pending.type] () { },
[getPermissions.rejected.type] () { },
[getPermissions.fulfilled.type] (state, { payload }) {
state.permissionsData = payload
}
}
})
export const { setMenuListData, setIsCollapse } = actions
export default reducer
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
import { LoginApi } from '@/pages/Login/common/services'
export const NAMESPACE_KEY = 'user'
type State = {
status: 'idle' | 'loading' | 'succeeded' | 'failed',
isLogin: boolean
username: string
password: string
token: string
remember: boolean
avatar?: string
createTime: number
dept: string
deptId: number
deptList: string
email: string
enabled: boolean
licenseExpired: boolean
nickname: string
phone: string
roleIdList: string
roles: string[]
wxBinded?: false,
userLoginInfo: {
address: string
device: string
ip: string
lastLoginTime: number
},
authRouter: string[]
}
const initialState: State = {
status: 'idle',
isLogin: false,
username: '',
password: '',
token: '',
remember: false,
avatar: '',
createTime: 0,
dept: '',
deptId: 0,
deptList: '',
email: '',
enabled: false,
licenseExpired: false,
nickname: '',
phone: '',
roleIdList: '',
roles: [],
wxBinded: false,
userLoginInfo: {
address: '',
device: '',
ip: '',
lastLoginTime: 0
},
authRouter: []
}
export const loginAsync = createAsyncThunk(`${NAMESPACE_KEY}/login`, async (payload: {
username: string,
password: string,
captcha: any,
uuid: any,
grant_type: any,
}) => {
const res = await LoginApi(payload)
return res
})
const { reducer, actions } = createSlice({
name: NAMESPACE_KEY,
initialState,
reducers: {
// login(state, { payload }: PayloadAction<Partial<State>>) {
// Object.assign(state, payload)
// },
setAuthRouter (state, { payload }: PayloadAction<string[]>) {
state.authRouter = payload
},
setLogin (state, { payload }: PayloadAction<any>) {
state.token = payload?.token
state.username = payload?.username
state.isLogin = true
},
setLogout (state, { payload }: PayloadAction<string>) {
state.token = ''
state.isLogin = false
}
},
extraReducers: {
[loginAsync.pending.type]: (state: State) => {
state.status = 'loading'
},
[loginAsync.rejected.type]: (state: State) => {
state.status = 'failed'
},
[loginAsync.fulfilled.type]: (state: State, { payload }: PayloadAction<any>) => {
const { code, data } = payload
console.log('[ login请求 ]')
if (code === 0) {
const { token } = data
console.log('token', token)
state.isLogin = true
state.token = token
state.status = 'succeeded'
} else {
state.status = 'succeeded'
}
console.log('state', state)
}
// logout
}
})
export const { setAuthRouter, setLogin, setLogout } = actions
export default reducer
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
import { ProTableProps } from '@ant-design/pro-table'
export interface IMyProTableProps extends ProTableProps<any, any> { }
export type TLogin = {
captcha: string, // 验证码
password: string, // 密码
username: string, // 账户
uuid: string, // uuid
grant_type: string // 登录类型 默认值:password
}
import React from 'react'
// import { Spin } from 'antd'
/**
* @description 路由懒加载
* @param {Element} Comp 需要访问的组件
* @returns element
*/
const lazyLoad = (Comp: React.LazyExoticComponent<any>): React.ReactNode => {
return (
// <Suspense
// fallback={
// <Spin
// size="large"
// style={{
// display: 'flex',
// alignItems: 'center',
// justifyContent: 'center',
// height: '100%'
// }}
// />
// }
// >
<Comp />
// </Suspense>
)
}
export default lazyLoad
import axios, { Method, AxiosRequestConfig } from 'axios'
import { message } from 'antd'
import { store } from '@/store'
export type TResult<T = any> = Promise<{code:number, data:T, msg:string}>;
const request = axios.create({
baseURL: process.env.API_HOST,
headers: { 'Content-Type': 'application/json' },
withCredentials: false
})
request.interceptors.request.use(
(config) => {
const _config = Object.assign({}, config)
// newConfig.headers.token = TOKEN;
// console.log(store.reducer);
// if (store.state.user.userList[0]) {
// newConfig.headers.Authentication = store.state.user.userList[0].token;
// }
// if (store.getters.token) {
// newConfig.headers.content = 'application/json;charset=UTF-8';
// }
// const { data } = newConfig;
// data.platform = TOKEN;
return _config
},
(error) => {
return Promise.reject(error)
}
)
request.interceptors.response.use(
(res: any) => {
// console.log('res===>', res)
const { status, msg } = res.data || {}
switch (status) {
case 200:
return res.data
case 10020:
case 10021:
window.location.href = '/login' // react 路由组件跳转需要使用hooks方式,所以写成原生跳转
default:
message.error(msg || '服务器错误')
return Promise.reject(res)
}
},
(err) => {
switch (err?.response?.status) {
case 400:
break
case 401:
break
case 405:
case 500:
default:
break
}
return Promise.reject(err.response)
}
)
interface RequestOptions extends Partial<AxiosRequestConfig> {
isAuth?: boolean;
urlFields?: string[];
isForm?: boolean;
transformParams?: (params: any) => any;
}
const createMethod =
(method: Method = 'get') =>
(url: string, options?: RequestOptions) => {
const {
urlFields,
isAuth = true,
isForm = false,
headers = {},
transformParams,
params,
...args
} = options || {}
let data = ['get'].includes(method) ? { params } : { data: params }
// : { data: Qs.stringify(params, { arrayFormat: "brackets" }) };
// 处理formdata形式
if (isForm) {
const formParams = new FormData()
for (const key in params) {
formParams.append(key, params[key])
}
data.data = formParams;
(options as any).headers = Object.assign({}, options?.headers, {
'Content-Type': 'multipart/form-data'
})
}
if (typeof transformParams === 'function') {
data = transformParams(params)
}
// 过滤URl参数
if (urlFields) {
for (const key of urlFields) {
url = url.replace(new RegExp(`:${key}`), (params as any)[key])
}
}
// 接口增加权限token
if (isAuth) {
const data:any = store.getState()
if (data.user) {
// (headers as any)["token"] = data.user["token"];
(headers as any).token = store.getState().user.token
}
}
return request(url, {
method,
headers,
...data,
...args
} as any) as Promise<any>
}
export const Post = createMethod('post')
export const Get = createMethod('get')
export const Delete = createMethod('delete')
export const Put = createMethod('put')
export default request
import { RouteObject } from '@/router/interface'
interface MenuOptions {
path: string
label: string
icon?: string
isLink?: string
close?: boolean
children?: MenuOptions[]
}
/**
* @description 获取需要展开的 subMenu
* @param {String} path 当前访问地址
* @returns array
*/
export const getOpenKeys = (path: string) => {
let newStr: string = ''
const newArr: any[] = []
const arr = path.split('/').map(i => '/' + i)
for (let i = 1; i < arr.length - 1; i++) {
newStr += arr[i]
newArr.push(newStr)
}
return newArr
}
/**
* @description 递归查询对应的路由
* @param {String} path 当前访问地址
* @param {Array} routes 路由列表
* @returns array
*/
export const searchRoute = (path: string, routes: RouteObject[] = []): RouteObject => {
let result: RouteObject = {}
for (const item of routes) {
if (item.path === path) return item
if (item.children) {
const res = searchRoute(path, item.children)
if (Object.keys(res).length) result = res
}
}
return result
}
/**
* @description 双重递归 找出 所有面包屑生成对象存到 redux 中,就不用每次都去递归查找了
* @param {String} menuList 当前菜单列表
* @returns object
*/
export const findAllBreadcrumb = (menuList: MenuOptions[]): { [key: string]: any } => {
const handleBreadcrumbList: any = {}
const loop = (menuItem: MenuOptions) => {
// 下面判断代码解释 *** !item?.children?.length ==> (item.children && item.children.length > 0)
// console.log('[ menulist ]', menuItem.path, menuList)
if (menuItem?.children?.length) menuItem.children.forEach(item => loop(item))
else handleBreadcrumbList[menuItem.path] = getBreadcrumbList(menuItem.path, menuList)
}
menuList.forEach(item => loop(item))
return handleBreadcrumbList
}
/**
* @description 递归当前路由的 所有 关联的路由,生成面包屑导航栏
* @param {String} path 当前访问地址
* @param {Array} menuList 菜单列表
* @returns array
*/
export const getBreadcrumbList = (path: string, menuList: MenuOptions[]) => {
const tempPath: any[] = []
try {
const getNodePath = (node: MenuOptions) => {
tempPath.push(node)
// 找到符合条件的节点,通过throw终止掉递归
if (node.path === path) {
throw new Error('GOT IT!')
}
if (node.children && node.children.length > 0) {
for (let i = 0; i < node.children.length; i++) {
getNodePath(node.children[i])
}
// 当前节点的子节点遍历完依旧没找到,则删除路径中的该节点
tempPath.pop()
} else {
// 找到叶子节点时,删除路径当中的该叶子节点
tempPath.pop()
}
}
for (let i = 0; i < menuList.length; i++) {
getNodePath(menuList[i])
}
} catch (e) {
return tempPath.map(item => item.label)
}
}
/**
* @description 使用递归处理路由菜单,生成一维数组,做菜单权限判断
* @param {Array} menuList 所有菜单列表
* @param {Array} newArr 菜单的一维数组
* @return array
*/
export function handleRouter (routerList: MenuOptions[], newArr: string[] = []) {
routerList.forEach((item: MenuOptions) => {
typeof item === 'object' && item.path && newArr.push(item.path)
item.children && item.children.length && handleRouter(item.children, newArr)
})
return newArr
}
{
"compilerOptions": {
"strict": true,
"module": "commonjs",
"target": "es5",
"esModuleInterop": true,
"jsx": "react-jsx",
"allowJs": true,
"checkJs": true,
// "noImplicitAny": true,
// "importHelpers": true,
"noEmit": true,
"sourceMap": true,
"experimentalDecorators": true,
"lib": ["esnext", "dom"],
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"resolveJsonModule": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment