From 2a0fdeae313bf1f56f7e64a8a1372fc526155e0e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?=
<41315874+fumiama@users.noreply.github.com>
Date: Fri, 10 Mar 2023 17:18:32 +0800
Subject: [PATCH] add frontend/vben from vben-admin-thin
---
.gitignore | 3 +
frontend/vben/.editorconfig | 19 +
frontend/vben/.env | 8 +
frontend/vben/.env.development | 22 +
frontend/vben/.env.production | 35 +
frontend/vben/.eslintignore | 15 +
frontend/vben/.eslintrc.js | 76 +
frontend/vben/.gitignore | 33 +
frontend/vben/.gitpod.yml | 6 +
frontend/vben/.prettierignore | 9 +
frontend/vben/.stylelintignore | 3 +
frontend/vben/LICENSE | 22 +
frontend/vben/README.md | 101 +
frontend/vben/build/config/themeConfig.ts | 79 +
frontend/vben/build/constant.ts | 6 +
.../vben/build/generate/generateModifyVars.ts | 37 +
frontend/vben/build/generate/icon/index.ts | 72 +
frontend/vben/build/getConfigFileName.ts | 9 +
frontend/vben/build/script/buildConf.ts | 47 +
frontend/vben/build/script/postBuild.ts | 23 +
frontend/vben/build/utils.ts | 92 +
frontend/vben/build/vite/plugin/compress.ts | 35 +
frontend/vben/build/vite/plugin/html.ts | 40 +
frontend/vben/build/vite/plugin/imagemin.ts | 34 +
frontend/vben/build/vite/plugin/index.ts | 82 +
frontend/vben/build/vite/plugin/mock.ts | 19 +
frontend/vben/build/vite/plugin/pwa.ts | 33 +
.../vben/build/vite/plugin/styleImport.ts | 81 +
frontend/vben/build/vite/plugin/svgSprite.ts | 17 +
frontend/vben/build/vite/plugin/theme.ts | 89 +
frontend/vben/build/vite/plugin/visualizer.ts | 17 +
frontend/vben/build/vite/proxy.ts | 34 +
frontend/vben/commitlint.config.js | 107 +
frontend/vben/index.html | 159 +
frontend/vben/mock/_createProductionServer.ts | 18 +
frontend/vben/mock/_util.ts | 62 +
frontend/vben/mock/demo/account.ts | 71 +
frontend/vben/mock/demo/api-cascader.ts | 325 +
frontend/vben/mock/demo/select-demo.ts | 28 +
frontend/vben/mock/demo/system.ts | 202 +
frontend/vben/mock/demo/table-demo.ts | 52 +
frontend/vben/mock/demo/tree-demo.ts | 38 +
frontend/vben/mock/sys/menu.ts | 260 +
frontend/vben/mock/sys/user.ts | 122 +
frontend/vben/package.json | 191 +
frontend/vben/postcss.config.js | 5 +
frontend/vben/prettier.config.js | 10 +
frontend/vben/public/favicon.ico | Bin 0 -> 894 bytes
frontend/vben/public/resource/img/logo.png | Bin 0 -> 4042 bytes
.../vben/public/resource/img/pwa-192x192.png | Bin 0 -> 12205 bytes
.../vben/public/resource/img/pwa-512x512.png | Bin 0 -> 52656 bytes
.../vben/public/resource/tinymce/langs/en.js | 419 +
.../public/resource/tinymce/langs/zh_CN.js | 389 +
.../ui/oxide-dark/content.inline.min.css | 7 +
.../skins/ui/oxide-dark/content.min.css | 7 +
.../ui/oxide-dark/content.mobile.min.css | 7 +
.../tinymce/skins/ui/oxide-dark/skin.min.css | 7 +
.../skins/ui/oxide-dark/skin.mobile.min.css | 7 +
.../ui/oxide-dark/skin.shadowdom.min.css | 7 +
.../skins/ui/oxide/content.inline.min.css | 7 +
.../tinymce/skins/ui/oxide/content.min.css | 7 +
.../skins/ui/oxide/content.mobile.min.css | 7 +
.../skins/ui/oxide/fonts/tinymce-mobile.woff | Bin 0 -> 4624 bytes
.../tinymce/skins/ui/oxide/skin.min.css | 7 +
.../skins/ui/oxide/skin.mobile.min.css | 7 +
.../skins/ui/oxide/skin.shadowdom.min.css | 7 +
frontend/vben/src/App.vue | 21 +
frontend/vben/src/api/demo/account.ts | 16 +
frontend/vben/src/api/demo/cascader.ts | 9 +
frontend/vben/src/api/demo/error.ts | 12 +
.../vben/src/api/demo/model/accountModel.ts | 7 +
frontend/vben/src/api/demo/model/areaModel.ts | 12 +
.../vben/src/api/demo/model/optionsModel.ts | 15 +
.../vben/src/api/demo/model/systemModel.ts | 74 +
.../vben/src/api/demo/model/tableModel.ts | 20 +
frontend/vben/src/api/demo/select.ts | 11 +
frontend/vben/src/api/demo/system.ts | 44 +
frontend/vben/src/api/demo/table.ts | 20 +
frontend/vben/src/api/demo/tree.ts | 11 +
frontend/vben/src/api/model/baseModel.ts | 9 +
frontend/vben/src/api/sys/menu.ts | 14 +
frontend/vben/src/api/sys/model/menuModel.ts | 16 +
.../vben/src/api/sys/model/uploadModel.ts | 5 +
frontend/vben/src/api/sys/model/userModel.ts | 38 +
frontend/vben/src/api/sys/user.ts | 55 +
.../vben/src/assets/icons/download-count.svg | 1 +
.../src/assets/icons/dynamic-avatar-1.svg | 1 +
.../src/assets/icons/dynamic-avatar-2.svg | 1 +
.../src/assets/icons/dynamic-avatar-3.svg | 1 +
.../src/assets/icons/dynamic-avatar-4.svg | 1 +
.../src/assets/icons/dynamic-avatar-5.svg | 1 +
.../src/assets/icons/dynamic-avatar-6.svg | 1 +
frontend/vben/src/assets/icons/moon.svg | 16 +
frontend/vben/src/assets/icons/sun.svg | 42 +
frontend/vben/src/assets/icons/test.svg | 21 +
.../vben/src/assets/icons/total-sales.svg | 1 +
.../vben/src/assets/icons/transaction.svg | 1 +
.../vben/src/assets/icons/visit-count.svg | 1 +
frontend/vben/src/assets/images/demo.png | Bin 0 -> 33342 bytes
frontend/vben/src/assets/images/header.jpg | Bin 0 -> 16880 bytes
frontend/vben/src/assets/images/logo.png | Bin 0 -> 28304 bytes
frontend/vben/src/assets/svg/illustration.svg | 1 +
.../vben/src/assets/svg/login-bg-dark.svg | 19 +
frontend/vben/src/assets/svg/login-bg.svg | 17 +
frontend/vben/src/assets/svg/login-box-bg.svg | 1 +
frontend/vben/src/assets/svg/net-error.svg | 1 +
frontend/vben/src/assets/svg/no-data.svg | 1 +
.../vben/src/assets/svg/preview/p-rotate.svg | 1 +
.../vben/src/assets/svg/preview/resume.svg | 1 +
.../vben/src/assets/svg/preview/scale.svg | 1 +
.../vben/src/assets/svg/preview/unrotate.svg | 1 +
.../vben/src/assets/svg/preview/unscale.svg | 1 +
.../vben/src/components/Application/index.ts | 15 +
.../Application/src/AppDarkModeToggle.vue | 76 +
.../Application/src/AppLocalePicker.vue | 76 +
.../components/Application/src/AppLogo.vue | 93 +
.../Application/src/AppProvider.vue | 82 +
.../Application/src/search/AppSearch.vue | 33 +
.../src/search/AppSearchFooter.vue | 56 +
.../src/search/AppSearchKeyItem.vue | 11 +
.../Application/src/search/AppSearchModal.vue | 267 +
.../Application/src/search/useMenuSearch.ts | 166 +
.../Application/src/useAppContext.ts | 17 +
frontend/vben/src/components/Basic/index.ts | 8 +
.../src/components/Basic/src/BasicArrow.vue | 84 +
.../src/components/Basic/src/BasicHelp.vue | 114 +
.../src/components/Basic/src/BasicTitle.vue | 76 +
frontend/vben/src/components/Button/index.ts | 9 +
.../src/components/Button/src/BasicButton.vue | 40 +
.../Button/src/PopConfirmButton.vue | 54 +
.../vben/src/components/Button/src/props.ts | 19 +
.../vben/src/components/Container/index.ts | 10 +
.../Container/src/LazyContainer.vue | 145 +
.../Container/src/ScrollContainer.vue | 93 +
.../src/collapse/CollapseContainer.vue | 110 +
.../Container/src/collapse/CollapseHeader.vue | 38 +
.../src/components/Container/src/typing.ts | 17 +
.../vben/src/components/CountDown/index.ts | 6 +
.../components/CountDown/src/CountButton.vue | 62 +
.../CountDown/src/CountdownInput.vue | 54 +
.../components/CountDown/src/useCountdown.ts | 51 +
.../vben/src/components/Description/index.ts | 6 +
.../Description/src/Description.vue | 184 +
.../src/components/Description/src/typing.ts | 50 +
.../Description/src/useDescription.ts | 28 +
frontend/vben/src/components/Drawer/index.ts | 6 +
.../src/components/Drawer/src/BasicDrawer.vue | 256 +
.../Drawer/src/components/DrawerFooter.vue | 82 +
.../Drawer/src/components/DrawerHeader.vue | 74 +
.../vben/src/components/Drawer/src/props.ts | 44 +
.../vben/src/components/Drawer/src/typing.ts | 193 +
.../src/components/Drawer/src/useDrawer.ts | 161 +
.../vben/src/components/Dropdown/index.ts | 5 +
.../src/components/Dropdown/src/Dropdown.vue | 96 +
.../src/components/Dropdown/src/typing.ts | 9 +
.../src/components/Icon/data/icons.data.ts | 786 ++
frontend/vben/src/components/Icon/index.ts | 7 +
.../vben/src/components/Icon/src/Icon.vue | 121 +
.../src/components/Icon/src/IconPicker.vue | 188 +
.../vben/src/components/Icon/src/SvgIcon.vue | 65 +
frontend/vben/src/components/Loading/index.ts | 5 +
.../src/components/Loading/src/Loading.vue | 79 +
.../components/Loading/src/createLoading.ts | 65 +
.../vben/src/components/Loading/src/typing.ts | 10 +
.../src/components/Loading/src/useLoading.ts | 49 +
frontend/vben/src/components/Menu/index.ts | 3 +
.../src/components/Menu/src/BasicMenu.vue | 164 +
.../Menu/src/components/BasicMenuItem.vue | 20 +
.../Menu/src/components/BasicSubMenuItem.vue | 55 +
.../Menu/src/components/MenuItemContent.vue | 34 +
.../vben/src/components/Menu/src/index.less | 74 +
.../vben/src/components/Menu/src/props.ts | 60 +
.../vben/src/components/Menu/src/types.ts | 25 +
.../src/components/Menu/src/useOpenKeys.ts | 83 +
frontend/vben/src/components/Modal/index.ts | 8 +
.../src/components/Modal/src/BasicModal.vue | 242 +
.../components/Modal/src/components/Modal.tsx | 31 +
.../Modal/src/components/ModalClose.vue | 106 +
.../Modal/src/components/ModalFooter.vue | 40 +
.../Modal/src/components/ModalHeader.vue | 22 +
.../Modal/src/components/ModalWrapper.vue | 169 +
.../components/Modal/src/hooks/useModal.ts | 163 +
.../Modal/src/hooks/useModalContext.ts | 16 +
.../Modal/src/hooks/useModalDrag.ts | 107 +
.../Modal/src/hooks/useModalFullScreen.ts | 43 +
.../vben/src/components/Modal/src/index.less | 127 +
.../vben/src/components/Modal/src/props.ts | 83 +
.../vben/src/components/Modal/src/typing.ts | 209 +
frontend/vben/src/components/Page/index.ts | 9 +
.../src/components/Page/src/PageFooter.vue | 50 +
.../src/components/Page/src/PageWrapper.vue | 191 +
.../vben/src/components/Scrollbar/index.ts | 8 +
.../components/Scrollbar/src/Scrollbar.vue | 206 +
.../vben/src/components/Scrollbar/src/bar.ts | 110 +
.../src/components/Scrollbar/src/types.d.ts | 18 +
.../vben/src/components/Scrollbar/src/util.ts | 50 +
.../vben/src/components/SimpleMenu/index.ts | 2 +
.../components/SimpleMenu/src/SimpleMenu.vue | 160 +
.../SimpleMenu/src/SimpleMenuTag.vue | 68 +
.../SimpleMenu/src/SimpleSubMenu.vue | 116 +
.../SimpleMenu/src/components/Menu.vue | 158 +
.../src/components/MenuCollapseTransition.vue | 78 +
.../SimpleMenu/src/components/MenuItem.vue | 107 +
.../SimpleMenu/src/components/SubMenuItem.vue | 333 +
.../SimpleMenu/src/components/menu.less | 309 +
.../SimpleMenu/src/components/types.ts | 25 +
.../SimpleMenu/src/components/useMenu.ts | 84 +
.../src/components/useSimpleMenuContext.ts | 18 +
.../src/components/SimpleMenu/src/index.less | 77 +
.../src/components/SimpleMenu/src/types.ts | 5 +
.../components/SimpleMenu/src/useOpenKeys.ts | 50 +
.../src/components/StrengthMeter/index.ts | 4 +
.../StrengthMeter/src/StrengthMeter.vue | 142 +
.../vben/src/components/Transition/index.ts | 27 +
.../Transition/src/CollapseTransition.vue | 78 +
.../Transition/src/CreateTransition.tsx | 73 +
.../Transition/src/ExpandTransition.ts | 89 +
.../vben/src/components/registerGlobComp.ts | 7 +
frontend/vben/src/design/ant/btn.less | 285 +
frontend/vben/src/design/ant/index.less | 67 +
frontend/vben/src/design/ant/input.less | 31 +
frontend/vben/src/design/ant/pagination.less | 96 +
frontend/vben/src/design/ant/table.less | 76 +
frontend/vben/src/design/color.less | 138 +
frontend/vben/src/design/config.less | 2 +
frontend/vben/src/design/index.less | 44 +
frontend/vben/src/design/public.less | 51 +
frontend/vben/src/design/theme.less | 52 +
frontend/vben/src/design/transition/base.less | 18 +
frontend/vben/src/design/transition/fade.less | 93 +
.../vben/src/design/transition/index.less | 10 +
.../vben/src/design/transition/scale.less | 21 +
.../vben/src/design/transition/scroll.less | 67 +
.../vben/src/design/transition/slide.less | 39 +
frontend/vben/src/design/transition/zoom.less | 27 +
frontend/vben/src/design/var/breakpoint.less | 33 +
frontend/vben/src/design/var/easing.less | 18 +
frontend/vben/src/design/var/index.less | 39 +
frontend/vben/src/directives/clickOutside.ts | 86 +
frontend/vben/src/directives/index.ts | 11 +
frontend/vben/src/directives/loading.ts | 39 +
frontend/vben/src/directives/permission.ts | 32 +
frontend/vben/src/directives/repeatClick.ts | 31 +
.../vben/src/directives/ripple/index.less | 21 +
frontend/vben/src/directives/ripple/index.ts | 191 +
frontend/vben/src/enums/appEnum.ts | 52 +
frontend/vben/src/enums/breakpointEnum.ts | 28 +
frontend/vben/src/enums/cacheEnum.ts | 28 +
frontend/vben/src/enums/exceptionEnum.ts | 27 +
frontend/vben/src/enums/httpEnum.ts | 31 +
frontend/vben/src/enums/menuEnum.ts | 50 +
frontend/vben/src/enums/pageEnum.ts | 10 +
frontend/vben/src/enums/roleEnum.ts | 7 +
frontend/vben/src/enums/sizeEnum.ts | 19 +
.../vben/src/hooks/component/useFormItem.ts | 60 +
.../src/hooks/component/usePageContext.ts | 18 +
.../src/hooks/core/onMountedOrActivated.ts | 18 +
frontend/vben/src/hooks/core/useAttrs.ts | 40 +
frontend/vben/src/hooks/core/useContext.ts | 44 +
frontend/vben/src/hooks/core/useRefs.ts | 16 +
frontend/vben/src/hooks/core/useTimeout.ts | 45 +
.../vben/src/hooks/event/useBreakpoint.ts | 89 +
.../vben/src/hooks/event/useEventListener.ts | 58 +
.../hooks/event/useIntersectionObserver.ts | 48 +
frontend/vben/src/hooks/event/useScroll.ts | 65 +
frontend/vben/src/hooks/event/useScrollTo.ts | 59 +
.../vben/src/hooks/event/useWindowSizeFn.ts | 35 +
frontend/vben/src/hooks/setting/index.ts | 30 +
.../src/hooks/setting/useHeaderSetting.ts | 102 +
.../vben/src/hooks/setting/useMenuSetting.ts | 164 +
.../hooks/setting/useMultipleTabSetting.ts | 28 +
.../vben/src/hooks/setting/useRootSetting.ts | 92 +
.../src/hooks/setting/useTransitionSetting.ts | 31 +
frontend/vben/src/hooks/web/useAppInject.ts | 10 +
.../vben/src/hooks/web/useContentHeight.ts | 191 +
.../vben/src/hooks/web/useCopyToClipboard.ts | 68 +
frontend/vben/src/hooks/web/useDesign.ts | 22 +
frontend/vben/src/hooks/web/useECharts.ts | 121 +
frontend/vben/src/hooks/web/useFullContent.ts | 28 +
frontend/vben/src/hooks/web/useI18n.ts | 55 +
frontend/vben/src/hooks/web/useMessage.tsx | 123 +
frontend/vben/src/hooks/web/usePage.ts | 53 +
frontend/vben/src/hooks/web/usePagination.ts | 34 +
frontend/vben/src/hooks/web/usePermission.ts | 111 +
frontend/vben/src/hooks/web/useScript.ts | 46 +
frontend/vben/src/hooks/web/useSortable.ts | 21 +
frontend/vben/src/hooks/web/useTabs.ts | 103 +
frontend/vben/src/hooks/web/useTitle.ts | 35 +
frontend/vben/src/hooks/web/useWatermark.ts | 100 +
.../src/layouts/default/content/index.vue | 51 +
.../default/content/useContentContext.ts | 17 +
.../default/content/useContentViewHeight.ts | 42 +
.../src/layouts/default/feature/index.vue | 81 +
.../vben/src/layouts/default/footer/index.vue | 95 +
.../layouts/default/header/MultipleHeader.vue | 126 +
.../default/header/components/Breadcrumb.vue | 204 +
.../default/header/components/FullScreen.vue | 37 +
.../default/header/components/index.ts | 12 +
.../header/components/notify/NoticeList.vue | 189 +
.../default/header/components/notify/data.ts | 193 +
.../header/components/notify/index.vue | 91 +
.../components/user-dropdown/DropMenuItem.vue | 32 +
.../header/components/user-dropdown/index.vue | 161 +
.../src/layouts/default/header/index.less | 196 +
.../vben/src/layouts/default/header/index.vue | 197 +
frontend/vben/src/layouts/default/index.vue | 87 +
.../vben/src/layouts/default/menu/index.vue | 197 +
.../src/layouts/default/menu/useLayoutMenu.ts | 109 +
.../layouts/default/setting/SettingDrawer.tsx | 415 +
.../setting/components/InputNumberItem.vue | 56 +
.../default/setting/components/SelectItem.vue | 75 +
.../setting/components/SettingFooter.vue | 99 +
.../default/setting/components/SwitchItem.vue | 66 +
.../setting/components/ThemeColorPicker.vue | 88 +
.../default/setting/components/TypePicker.vue | 178 +
.../default/setting/components/index.ts | 8 +
.../vben/src/layouts/default/setting/enum.ts | 155 +
.../src/layouts/default/setting/handler.ts | 171 +
.../src/layouts/default/setting/index.vue | 26 +
.../src/layouts/default/sider/DragBar.vue | 66 +
.../src/layouts/default/sider/LayoutSider.vue | 185 +
.../src/layouts/default/sider/MixSider.vue | 590 +
.../vben/src/layouts/default/sider/index.vue | 57 +
.../layouts/default/sider/useLayoutSider.ts | 143 +
.../default/tabs/components/FoldButton.vue | 42 +
.../default/tabs/components/TabContent.vue | 76 +
.../default/tabs/components/TabRedo.vue | 33 +
.../vben/src/layouts/default/tabs/index.less | 207 +
.../vben/src/layouts/default/tabs/index.vue | 142 +
.../vben/src/layouts/default/tabs/types.ts | 25 +
.../layouts/default/tabs/useMultipleTabs.ts | 80 +
.../layouts/default/tabs/useTabDropdown.ts | 140 +
.../layouts/default/trigger/HeaderTrigger.vue | 25 +
.../layouts/default/trigger/SiderTrigger.vue | 21 +
.../src/layouts/default/trigger/index.vue | 22 +
frontend/vben/src/layouts/page/index.vue | 67 +
frontend/vben/src/layouts/page/transition.ts | 33 +
frontend/vben/src/locales/helper.ts | 37 +
frontend/vben/src/locales/lang/en.ts | 12 +
frontend/vben/src/locales/lang/en/common.ts | 20 +
.../vben/src/locales/lang/en/component.ts | 129 +
frontend/vben/src/locales/lang/en/layout.ts | 106 +
.../vben/src/locales/lang/en/routes/basic.ts | 4 +
.../src/locales/lang/en/routes/dashboard.ts | 6 +
.../vben/src/locales/lang/en/routes/demo.ts | 189 +
frontend/vben/src/locales/lang/en/sys.ts | 96 +
.../lang/zh-CN/antdLocale/DatePicker.ts | 19 +
.../vben/src/locales/lang/zh-CN/common.ts | 20 +
.../vben/src/locales/lang/zh-CN/component.ts | 134 +
.../vben/src/locales/lang/zh-CN/layout.ts | 106 +
.../src/locales/lang/zh-CN/routes/basic.ts | 4 +
.../locales/lang/zh-CN/routes/dashboard.ts | 6 +
.../src/locales/lang/zh-CN/routes/demo.ts | 181 +
frontend/vben/src/locales/lang/zh-CN/sys.ts | 94 +
frontend/vben/src/locales/lang/zh_CN.ts | 10 +
frontend/vben/src/locales/setupI18n.ts | 44 +
frontend/vben/src/locales/useLocale.ts | 69 +
frontend/vben/src/logics/initAppConfig.ts | 84 +
frontend/vben/src/logics/mitt/routeChange.ts | 31 +
frontend/vben/src/logics/theme/dark.ts | 24 +
frontend/vben/src/logics/theme/index.ts | 17 +
.../vben/src/logics/theme/updateBackground.ts | 75 +
.../vben/src/logics/theme/updateColorWeak.ts | 9 +
.../vben/src/logics/theme/updateGrayMode.ts | 9 +
frontend/vben/src/logics/theme/util.ts | 11 +
frontend/vben/src/main.ts | 64 +
frontend/vben/src/router/constant.ts | 24 +
frontend/vben/src/router/guard/index.ts | 147 +
.../vben/src/router/guard/paramMenuGuard.ts | 47 +
.../vben/src/router/guard/permissionGuard.ts | 118 +
frontend/vben/src/router/guard/stateGuard.ts | 24 +
frontend/vben/src/router/helper/menuHelper.ts | 106 +
.../vben/src/router/helper/routeHelper.ts | 172 +
frontend/vben/src/router/index.ts | 42 +
frontend/vben/src/router/menus/index.ts | 126 +
frontend/vben/src/router/routes/basic.ts | 48 +
frontend/vben/src/router/routes/index.ts | 42 +
.../vben/src/router/routes/modules/about.ts | 31 +
.../src/router/routes/modules/dashboard.ts | 37 +
.../vben/src/router/routes/modules/setup.ts | 31 +
frontend/vben/src/router/types.ts | 58 +
.../vben/src/settings/componentSetting.ts | 51 +
frontend/vben/src/settings/designSetting.ts | 48 +
.../vben/src/settings/encryptionSetting.ts | 13 +
frontend/vben/src/settings/localeSetting.ts | 29 +
frontend/vben/src/settings/projectSetting.ts | 177 +
frontend/vben/src/settings/siteSetting.ts | 8 +
frontend/vben/src/store/index.ts | 10 +
frontend/vben/src/store/modules/app.ts | 108 +
frontend/vben/src/store/modules/locale.ts | 55 +
.../vben/src/store/modules/multipleTab.ts | 359 +
frontend/vben/src/store/modules/permission.ts | 259 +
frontend/vben/src/store/modules/user.ts | 177 +
frontend/vben/src/utils/auth/index.ts | 26 +
frontend/vben/src/utils/bem.ts | 52 +
frontend/vben/src/utils/cache/index.ts | 32 +
frontend/vben/src/utils/cache/memory.ts | 107 +
frontend/vben/src/utils/cache/persistent.ts | 125 +
frontend/vben/src/utils/cache/storageCache.ts | 109 +
frontend/vben/src/utils/cipher.ts | 55 +
frontend/vben/src/utils/color.ts | 151 +
frontend/vben/src/utils/dateUtil.ts | 23 +
frontend/vben/src/utils/domUtils.ts | 180 +
frontend/vben/src/utils/env.ts | 83 +
frontend/vben/src/utils/event/index.ts | 42 +
.../utils/factory/createAsyncComponent.tsx | 63 +
frontend/vben/src/utils/file/base64Conver.ts | 41 +
frontend/vben/src/utils/file/download.ts | 96 +
frontend/vben/src/utils/helper/treeHelper.ts | 216 +
frontend/vben/src/utils/helper/tsxHelper.tsx | 35 +
frontend/vben/src/utils/http/axios/Axios.ts | 237 +
.../vben/src/utils/http/axios/axiosCancel.ts | 60 +
.../vben/src/utils/http/axios/axiosRetry.ts | 28 +
.../src/utils/http/axios/axiosTransform.ts | 52 +
.../vben/src/utils/http/axios/checkStatus.ts | 80 +
frontend/vben/src/utils/http/axios/helper.ts | 48 +
frontend/vben/src/utils/http/axios/index.ts | 267 +
frontend/vben/src/utils/index.ts | 92 +
frontend/vben/src/utils/is.ts | 99 +
frontend/vben/src/utils/lib/echarts.ts | 57 +
frontend/vben/src/utils/log.ts | 9 +
frontend/vben/src/utils/mitt.ts | 101 +
frontend/vben/src/utils/propTypes.ts | 34 +
frontend/vben/src/utils/props.ts | 185 +
frontend/vben/src/utils/types.ts | 42 +
frontend/vben/src/utils/uuid.ts | 28 +
.../analysis/components/GrowCard.vue | 14 +
.../analysis/components/SalesProductPie.vue | 64 +
.../analysis/components/SiteAnalysis.vue | 38 +
.../analysis/components/VisitAnalysis.vue | 89 +
.../analysis/components/VisitAnalysisBar.vue | 47 +
.../analysis/components/VisitRadar.vue | 94 +
.../analysis/components/VisitSource.vue | 81 +
.../dashboard/analysis/components/props.ts | 16 +
.../vben/src/views/dashboard/analysis/data.ts | 8 +
.../src/views/dashboard/analysis/index.vue | 25 +
.../workbench/components/DynamicInfo.vue | 31 +
.../workbench/components/ProjectCard.vue | 32 +
.../workbench/components/QuickNav.vue | 17 +
.../workbench/components/SaleRadar.vue | 94 +
.../workbench/components/WorkbenchHeader.vue | 33 +
.../dashboard/workbench/components/data.ts | 156 +
.../src/views/dashboard/workbench/index.vue | 36 +
frontend/vben/src/views/setup/index.vue | 43 +
frontend/vben/src/views/sys/about/index.vue | 98 +
.../src/views/sys/exception/Exception.vue | 148 +
.../vben/src/views/sys/exception/index.ts | 1 +
.../views/sys/login/ForgetPasswordForm.vue | 64 +
frontend/vben/src/views/sys/login/Login.vue | 215 +
.../vben/src/views/sys/login/LoginForm.vue | 137 +
.../src/views/sys/login/LoginFormTitle.vue | 24 +
.../vben/src/views/sys/login/MobileForm.vue | 63 +
.../vben/src/views/sys/login/RegisterForm.vue | 104 +
.../views/sys/login/SessionTimeoutLogin.vue | 53 +
frontend/vben/src/views/sys/login/useLogin.ts | 118 +
.../vben/src/views/sys/redirect/index.vue | 30 +
frontend/vben/stylelint.config.js | 100 +
frontend/vben/tsconfig.json | 44 +
frontend/vben/types/axios.d.ts | 53 +
frontend/vben/types/config.d.ts | 158 +
frontend/vben/types/global.d.ts | 101 +
frontend/vben/types/index.d.ts | 27 +
frontend/vben/types/module.d.ts | 16 +
frontend/vben/types/store.d.ts | 40 +
frontend/vben/types/utils.d.ts | 5 +
frontend/vben/types/vue-router.d.ts | 45 +
frontend/vben/vite.config.ts | 115 +
frontend/vben/windi.config.ts | 74 +
frontend/vben/yarn.lock | 9754 +++++++++++++++++
469 files changed, 42028 insertions(+)
create mode 100644 frontend/vben/.editorconfig
create mode 100644 frontend/vben/.env
create mode 100644 frontend/vben/.env.development
create mode 100644 frontend/vben/.env.production
create mode 100644 frontend/vben/.eslintignore
create mode 100644 frontend/vben/.eslintrc.js
create mode 100644 frontend/vben/.gitignore
create mode 100644 frontend/vben/.gitpod.yml
create mode 100644 frontend/vben/.prettierignore
create mode 100644 frontend/vben/.stylelintignore
create mode 100644 frontend/vben/LICENSE
create mode 100644 frontend/vben/README.md
create mode 100644 frontend/vben/build/config/themeConfig.ts
create mode 100644 frontend/vben/build/constant.ts
create mode 100644 frontend/vben/build/generate/generateModifyVars.ts
create mode 100644 frontend/vben/build/generate/icon/index.ts
create mode 100644 frontend/vben/build/getConfigFileName.ts
create mode 100644 frontend/vben/build/script/buildConf.ts
create mode 100644 frontend/vben/build/script/postBuild.ts
create mode 100644 frontend/vben/build/utils.ts
create mode 100644 frontend/vben/build/vite/plugin/compress.ts
create mode 100644 frontend/vben/build/vite/plugin/html.ts
create mode 100644 frontend/vben/build/vite/plugin/imagemin.ts
create mode 100644 frontend/vben/build/vite/plugin/index.ts
create mode 100644 frontend/vben/build/vite/plugin/mock.ts
create mode 100644 frontend/vben/build/vite/plugin/pwa.ts
create mode 100644 frontend/vben/build/vite/plugin/styleImport.ts
create mode 100644 frontend/vben/build/vite/plugin/svgSprite.ts
create mode 100644 frontend/vben/build/vite/plugin/theme.ts
create mode 100644 frontend/vben/build/vite/plugin/visualizer.ts
create mode 100644 frontend/vben/build/vite/proxy.ts
create mode 100644 frontend/vben/commitlint.config.js
create mode 100644 frontend/vben/index.html
create mode 100644 frontend/vben/mock/_createProductionServer.ts
create mode 100644 frontend/vben/mock/_util.ts
create mode 100644 frontend/vben/mock/demo/account.ts
create mode 100644 frontend/vben/mock/demo/api-cascader.ts
create mode 100644 frontend/vben/mock/demo/select-demo.ts
create mode 100644 frontend/vben/mock/demo/system.ts
create mode 100644 frontend/vben/mock/demo/table-demo.ts
create mode 100644 frontend/vben/mock/demo/tree-demo.ts
create mode 100644 frontend/vben/mock/sys/menu.ts
create mode 100644 frontend/vben/mock/sys/user.ts
create mode 100644 frontend/vben/package.json
create mode 100755 frontend/vben/postcss.config.js
create mode 100644 frontend/vben/prettier.config.js
create mode 100644 frontend/vben/public/favicon.ico
create mode 100644 frontend/vben/public/resource/img/logo.png
create mode 100644 frontend/vben/public/resource/img/pwa-192x192.png
create mode 100644 frontend/vben/public/resource/img/pwa-512x512.png
create mode 100644 frontend/vben/public/resource/tinymce/langs/en.js
create mode 100755 frontend/vben/public/resource/tinymce/langs/zh_CN.js
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide-dark/content.inline.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide-dark/content.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide-dark/skin.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide/content.inline.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide/content.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide/content.mobile.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide/skin.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css
create mode 100644 frontend/vben/public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css
create mode 100644 frontend/vben/src/App.vue
create mode 100644 frontend/vben/src/api/demo/account.ts
create mode 100644 frontend/vben/src/api/demo/cascader.ts
create mode 100644 frontend/vben/src/api/demo/error.ts
create mode 100644 frontend/vben/src/api/demo/model/accountModel.ts
create mode 100644 frontend/vben/src/api/demo/model/areaModel.ts
create mode 100644 frontend/vben/src/api/demo/model/optionsModel.ts
create mode 100644 frontend/vben/src/api/demo/model/systemModel.ts
create mode 100644 frontend/vben/src/api/demo/model/tableModel.ts
create mode 100644 frontend/vben/src/api/demo/select.ts
create mode 100644 frontend/vben/src/api/demo/system.ts
create mode 100644 frontend/vben/src/api/demo/table.ts
create mode 100644 frontend/vben/src/api/demo/tree.ts
create mode 100644 frontend/vben/src/api/model/baseModel.ts
create mode 100644 frontend/vben/src/api/sys/menu.ts
create mode 100644 frontend/vben/src/api/sys/model/menuModel.ts
create mode 100644 frontend/vben/src/api/sys/model/uploadModel.ts
create mode 100644 frontend/vben/src/api/sys/model/userModel.ts
create mode 100644 frontend/vben/src/api/sys/user.ts
create mode 100644 frontend/vben/src/assets/icons/download-count.svg
create mode 100644 frontend/vben/src/assets/icons/dynamic-avatar-1.svg
create mode 100644 frontend/vben/src/assets/icons/dynamic-avatar-2.svg
create mode 100644 frontend/vben/src/assets/icons/dynamic-avatar-3.svg
create mode 100644 frontend/vben/src/assets/icons/dynamic-avatar-4.svg
create mode 100644 frontend/vben/src/assets/icons/dynamic-avatar-5.svg
create mode 100644 frontend/vben/src/assets/icons/dynamic-avatar-6.svg
create mode 100644 frontend/vben/src/assets/icons/moon.svg
create mode 100644 frontend/vben/src/assets/icons/sun.svg
create mode 100644 frontend/vben/src/assets/icons/test.svg
create mode 100644 frontend/vben/src/assets/icons/total-sales.svg
create mode 100644 frontend/vben/src/assets/icons/transaction.svg
create mode 100644 frontend/vben/src/assets/icons/visit-count.svg
create mode 100644 frontend/vben/src/assets/images/demo.png
create mode 100644 frontend/vben/src/assets/images/header.jpg
create mode 100644 frontend/vben/src/assets/images/logo.png
create mode 100644 frontend/vben/src/assets/svg/illustration.svg
create mode 100644 frontend/vben/src/assets/svg/login-bg-dark.svg
create mode 100644 frontend/vben/src/assets/svg/login-bg.svg
create mode 100644 frontend/vben/src/assets/svg/login-box-bg.svg
create mode 100644 frontend/vben/src/assets/svg/net-error.svg
create mode 100644 frontend/vben/src/assets/svg/no-data.svg
create mode 100644 frontend/vben/src/assets/svg/preview/p-rotate.svg
create mode 100644 frontend/vben/src/assets/svg/preview/resume.svg
create mode 100644 frontend/vben/src/assets/svg/preview/scale.svg
create mode 100644 frontend/vben/src/assets/svg/preview/unrotate.svg
create mode 100644 frontend/vben/src/assets/svg/preview/unscale.svg
create mode 100644 frontend/vben/src/components/Application/index.ts
create mode 100644 frontend/vben/src/components/Application/src/AppDarkModeToggle.vue
create mode 100644 frontend/vben/src/components/Application/src/AppLocalePicker.vue
create mode 100644 frontend/vben/src/components/Application/src/AppLogo.vue
create mode 100644 frontend/vben/src/components/Application/src/AppProvider.vue
create mode 100644 frontend/vben/src/components/Application/src/search/AppSearch.vue
create mode 100644 frontend/vben/src/components/Application/src/search/AppSearchFooter.vue
create mode 100644 frontend/vben/src/components/Application/src/search/AppSearchKeyItem.vue
create mode 100644 frontend/vben/src/components/Application/src/search/AppSearchModal.vue
create mode 100644 frontend/vben/src/components/Application/src/search/useMenuSearch.ts
create mode 100644 frontend/vben/src/components/Application/src/useAppContext.ts
create mode 100644 frontend/vben/src/components/Basic/index.ts
create mode 100644 frontend/vben/src/components/Basic/src/BasicArrow.vue
create mode 100644 frontend/vben/src/components/Basic/src/BasicHelp.vue
create mode 100644 frontend/vben/src/components/Basic/src/BasicTitle.vue
create mode 100644 frontend/vben/src/components/Button/index.ts
create mode 100644 frontend/vben/src/components/Button/src/BasicButton.vue
create mode 100644 frontend/vben/src/components/Button/src/PopConfirmButton.vue
create mode 100644 frontend/vben/src/components/Button/src/props.ts
create mode 100644 frontend/vben/src/components/Container/index.ts
create mode 100644 frontend/vben/src/components/Container/src/LazyContainer.vue
create mode 100644 frontend/vben/src/components/Container/src/ScrollContainer.vue
create mode 100644 frontend/vben/src/components/Container/src/collapse/CollapseContainer.vue
create mode 100644 frontend/vben/src/components/Container/src/collapse/CollapseHeader.vue
create mode 100644 frontend/vben/src/components/Container/src/typing.ts
create mode 100644 frontend/vben/src/components/CountDown/index.ts
create mode 100644 frontend/vben/src/components/CountDown/src/CountButton.vue
create mode 100644 frontend/vben/src/components/CountDown/src/CountdownInput.vue
create mode 100644 frontend/vben/src/components/CountDown/src/useCountdown.ts
create mode 100644 frontend/vben/src/components/Description/index.ts
create mode 100644 frontend/vben/src/components/Description/src/Description.vue
create mode 100644 frontend/vben/src/components/Description/src/typing.ts
create mode 100644 frontend/vben/src/components/Description/src/useDescription.ts
create mode 100644 frontend/vben/src/components/Drawer/index.ts
create mode 100644 frontend/vben/src/components/Drawer/src/BasicDrawer.vue
create mode 100644 frontend/vben/src/components/Drawer/src/components/DrawerFooter.vue
create mode 100644 frontend/vben/src/components/Drawer/src/components/DrawerHeader.vue
create mode 100644 frontend/vben/src/components/Drawer/src/props.ts
create mode 100644 frontend/vben/src/components/Drawer/src/typing.ts
create mode 100644 frontend/vben/src/components/Drawer/src/useDrawer.ts
create mode 100644 frontend/vben/src/components/Dropdown/index.ts
create mode 100644 frontend/vben/src/components/Dropdown/src/Dropdown.vue
create mode 100644 frontend/vben/src/components/Dropdown/src/typing.ts
create mode 100644 frontend/vben/src/components/Icon/data/icons.data.ts
create mode 100644 frontend/vben/src/components/Icon/index.ts
create mode 100644 frontend/vben/src/components/Icon/src/Icon.vue
create mode 100644 frontend/vben/src/components/Icon/src/IconPicker.vue
create mode 100644 frontend/vben/src/components/Icon/src/SvgIcon.vue
create mode 100644 frontend/vben/src/components/Loading/index.ts
create mode 100644 frontend/vben/src/components/Loading/src/Loading.vue
create mode 100644 frontend/vben/src/components/Loading/src/createLoading.ts
create mode 100644 frontend/vben/src/components/Loading/src/typing.ts
create mode 100644 frontend/vben/src/components/Loading/src/useLoading.ts
create mode 100644 frontend/vben/src/components/Menu/index.ts
create mode 100644 frontend/vben/src/components/Menu/src/BasicMenu.vue
create mode 100644 frontend/vben/src/components/Menu/src/components/BasicMenuItem.vue
create mode 100644 frontend/vben/src/components/Menu/src/components/BasicSubMenuItem.vue
create mode 100644 frontend/vben/src/components/Menu/src/components/MenuItemContent.vue
create mode 100644 frontend/vben/src/components/Menu/src/index.less
create mode 100644 frontend/vben/src/components/Menu/src/props.ts
create mode 100644 frontend/vben/src/components/Menu/src/types.ts
create mode 100644 frontend/vben/src/components/Menu/src/useOpenKeys.ts
create mode 100644 frontend/vben/src/components/Modal/index.ts
create mode 100644 frontend/vben/src/components/Modal/src/BasicModal.vue
create mode 100644 frontend/vben/src/components/Modal/src/components/Modal.tsx
create mode 100644 frontend/vben/src/components/Modal/src/components/ModalClose.vue
create mode 100644 frontend/vben/src/components/Modal/src/components/ModalFooter.vue
create mode 100644 frontend/vben/src/components/Modal/src/components/ModalHeader.vue
create mode 100644 frontend/vben/src/components/Modal/src/components/ModalWrapper.vue
create mode 100644 frontend/vben/src/components/Modal/src/hooks/useModal.ts
create mode 100644 frontend/vben/src/components/Modal/src/hooks/useModalContext.ts
create mode 100644 frontend/vben/src/components/Modal/src/hooks/useModalDrag.ts
create mode 100644 frontend/vben/src/components/Modal/src/hooks/useModalFullScreen.ts
create mode 100644 frontend/vben/src/components/Modal/src/index.less
create mode 100644 frontend/vben/src/components/Modal/src/props.ts
create mode 100644 frontend/vben/src/components/Modal/src/typing.ts
create mode 100644 frontend/vben/src/components/Page/index.ts
create mode 100644 frontend/vben/src/components/Page/src/PageFooter.vue
create mode 100644 frontend/vben/src/components/Page/src/PageWrapper.vue
create mode 100644 frontend/vben/src/components/Scrollbar/index.ts
create mode 100644 frontend/vben/src/components/Scrollbar/src/Scrollbar.vue
create mode 100644 frontend/vben/src/components/Scrollbar/src/bar.ts
create mode 100644 frontend/vben/src/components/Scrollbar/src/types.d.ts
create mode 100644 frontend/vben/src/components/Scrollbar/src/util.ts
create mode 100644 frontend/vben/src/components/SimpleMenu/index.ts
create mode 100644 frontend/vben/src/components/SimpleMenu/src/SimpleMenu.vue
create mode 100644 frontend/vben/src/components/SimpleMenu/src/SimpleMenuTag.vue
create mode 100644 frontend/vben/src/components/SimpleMenu/src/SimpleSubMenu.vue
create mode 100644 frontend/vben/src/components/SimpleMenu/src/components/Menu.vue
create mode 100644 frontend/vben/src/components/SimpleMenu/src/components/MenuCollapseTransition.vue
create mode 100644 frontend/vben/src/components/SimpleMenu/src/components/MenuItem.vue
create mode 100644 frontend/vben/src/components/SimpleMenu/src/components/SubMenuItem.vue
create mode 100644 frontend/vben/src/components/SimpleMenu/src/components/menu.less
create mode 100644 frontend/vben/src/components/SimpleMenu/src/components/types.ts
create mode 100644 frontend/vben/src/components/SimpleMenu/src/components/useMenu.ts
create mode 100644 frontend/vben/src/components/SimpleMenu/src/components/useSimpleMenuContext.ts
create mode 100644 frontend/vben/src/components/SimpleMenu/src/index.less
create mode 100644 frontend/vben/src/components/SimpleMenu/src/types.ts
create mode 100644 frontend/vben/src/components/SimpleMenu/src/useOpenKeys.ts
create mode 100644 frontend/vben/src/components/StrengthMeter/index.ts
create mode 100644 frontend/vben/src/components/StrengthMeter/src/StrengthMeter.vue
create mode 100644 frontend/vben/src/components/Transition/index.ts
create mode 100644 frontend/vben/src/components/Transition/src/CollapseTransition.vue
create mode 100644 frontend/vben/src/components/Transition/src/CreateTransition.tsx
create mode 100644 frontend/vben/src/components/Transition/src/ExpandTransition.ts
create mode 100644 frontend/vben/src/components/registerGlobComp.ts
create mode 100644 frontend/vben/src/design/ant/btn.less
create mode 100644 frontend/vben/src/design/ant/index.less
create mode 100644 frontend/vben/src/design/ant/input.less
create mode 100644 frontend/vben/src/design/ant/pagination.less
create mode 100644 frontend/vben/src/design/ant/table.less
create mode 100644 frontend/vben/src/design/color.less
create mode 100644 frontend/vben/src/design/config.less
create mode 100644 frontend/vben/src/design/index.less
create mode 100644 frontend/vben/src/design/public.less
create mode 100644 frontend/vben/src/design/theme.less
create mode 100644 frontend/vben/src/design/transition/base.less
create mode 100644 frontend/vben/src/design/transition/fade.less
create mode 100644 frontend/vben/src/design/transition/index.less
create mode 100644 frontend/vben/src/design/transition/scale.less
create mode 100644 frontend/vben/src/design/transition/scroll.less
create mode 100644 frontend/vben/src/design/transition/slide.less
create mode 100644 frontend/vben/src/design/transition/zoom.less
create mode 100644 frontend/vben/src/design/var/breakpoint.less
create mode 100644 frontend/vben/src/design/var/easing.less
create mode 100644 frontend/vben/src/design/var/index.less
create mode 100644 frontend/vben/src/directives/clickOutside.ts
create mode 100644 frontend/vben/src/directives/index.ts
create mode 100644 frontend/vben/src/directives/loading.ts
create mode 100644 frontend/vben/src/directives/permission.ts
create mode 100644 frontend/vben/src/directives/repeatClick.ts
create mode 100644 frontend/vben/src/directives/ripple/index.less
create mode 100644 frontend/vben/src/directives/ripple/index.ts
create mode 100644 frontend/vben/src/enums/appEnum.ts
create mode 100644 frontend/vben/src/enums/breakpointEnum.ts
create mode 100644 frontend/vben/src/enums/cacheEnum.ts
create mode 100644 frontend/vben/src/enums/exceptionEnum.ts
create mode 100644 frontend/vben/src/enums/httpEnum.ts
create mode 100644 frontend/vben/src/enums/menuEnum.ts
create mode 100644 frontend/vben/src/enums/pageEnum.ts
create mode 100644 frontend/vben/src/enums/roleEnum.ts
create mode 100644 frontend/vben/src/enums/sizeEnum.ts
create mode 100644 frontend/vben/src/hooks/component/useFormItem.ts
create mode 100644 frontend/vben/src/hooks/component/usePageContext.ts
create mode 100644 frontend/vben/src/hooks/core/onMountedOrActivated.ts
create mode 100644 frontend/vben/src/hooks/core/useAttrs.ts
create mode 100644 frontend/vben/src/hooks/core/useContext.ts
create mode 100644 frontend/vben/src/hooks/core/useRefs.ts
create mode 100644 frontend/vben/src/hooks/core/useTimeout.ts
create mode 100644 frontend/vben/src/hooks/event/useBreakpoint.ts
create mode 100644 frontend/vben/src/hooks/event/useEventListener.ts
create mode 100644 frontend/vben/src/hooks/event/useIntersectionObserver.ts
create mode 100644 frontend/vben/src/hooks/event/useScroll.ts
create mode 100644 frontend/vben/src/hooks/event/useScrollTo.ts
create mode 100644 frontend/vben/src/hooks/event/useWindowSizeFn.ts
create mode 100644 frontend/vben/src/hooks/setting/index.ts
create mode 100644 frontend/vben/src/hooks/setting/useHeaderSetting.ts
create mode 100644 frontend/vben/src/hooks/setting/useMenuSetting.ts
create mode 100644 frontend/vben/src/hooks/setting/useMultipleTabSetting.ts
create mode 100644 frontend/vben/src/hooks/setting/useRootSetting.ts
create mode 100644 frontend/vben/src/hooks/setting/useTransitionSetting.ts
create mode 100644 frontend/vben/src/hooks/web/useAppInject.ts
create mode 100644 frontend/vben/src/hooks/web/useContentHeight.ts
create mode 100644 frontend/vben/src/hooks/web/useCopyToClipboard.ts
create mode 100644 frontend/vben/src/hooks/web/useDesign.ts
create mode 100644 frontend/vben/src/hooks/web/useECharts.ts
create mode 100644 frontend/vben/src/hooks/web/useFullContent.ts
create mode 100644 frontend/vben/src/hooks/web/useI18n.ts
create mode 100644 frontend/vben/src/hooks/web/useMessage.tsx
create mode 100644 frontend/vben/src/hooks/web/usePage.ts
create mode 100644 frontend/vben/src/hooks/web/usePagination.ts
create mode 100644 frontend/vben/src/hooks/web/usePermission.ts
create mode 100644 frontend/vben/src/hooks/web/useScript.ts
create mode 100644 frontend/vben/src/hooks/web/useSortable.ts
create mode 100644 frontend/vben/src/hooks/web/useTabs.ts
create mode 100644 frontend/vben/src/hooks/web/useTitle.ts
create mode 100644 frontend/vben/src/hooks/web/useWatermark.ts
create mode 100644 frontend/vben/src/layouts/default/content/index.vue
create mode 100644 frontend/vben/src/layouts/default/content/useContentContext.ts
create mode 100644 frontend/vben/src/layouts/default/content/useContentViewHeight.ts
create mode 100644 frontend/vben/src/layouts/default/feature/index.vue
create mode 100644 frontend/vben/src/layouts/default/footer/index.vue
create mode 100644 frontend/vben/src/layouts/default/header/MultipleHeader.vue
create mode 100644 frontend/vben/src/layouts/default/header/components/Breadcrumb.vue
create mode 100644 frontend/vben/src/layouts/default/header/components/FullScreen.vue
create mode 100644 frontend/vben/src/layouts/default/header/components/index.ts
create mode 100644 frontend/vben/src/layouts/default/header/components/notify/NoticeList.vue
create mode 100644 frontend/vben/src/layouts/default/header/components/notify/data.ts
create mode 100644 frontend/vben/src/layouts/default/header/components/notify/index.vue
create mode 100644 frontend/vben/src/layouts/default/header/components/user-dropdown/DropMenuItem.vue
create mode 100644 frontend/vben/src/layouts/default/header/components/user-dropdown/index.vue
create mode 100644 frontend/vben/src/layouts/default/header/index.less
create mode 100644 frontend/vben/src/layouts/default/header/index.vue
create mode 100644 frontend/vben/src/layouts/default/index.vue
create mode 100644 frontend/vben/src/layouts/default/menu/index.vue
create mode 100644 frontend/vben/src/layouts/default/menu/useLayoutMenu.ts
create mode 100644 frontend/vben/src/layouts/default/setting/SettingDrawer.tsx
create mode 100644 frontend/vben/src/layouts/default/setting/components/InputNumberItem.vue
create mode 100644 frontend/vben/src/layouts/default/setting/components/SelectItem.vue
create mode 100644 frontend/vben/src/layouts/default/setting/components/SettingFooter.vue
create mode 100644 frontend/vben/src/layouts/default/setting/components/SwitchItem.vue
create mode 100644 frontend/vben/src/layouts/default/setting/components/ThemeColorPicker.vue
create mode 100644 frontend/vben/src/layouts/default/setting/components/TypePicker.vue
create mode 100644 frontend/vben/src/layouts/default/setting/components/index.ts
create mode 100644 frontend/vben/src/layouts/default/setting/enum.ts
create mode 100644 frontend/vben/src/layouts/default/setting/handler.ts
create mode 100644 frontend/vben/src/layouts/default/setting/index.vue
create mode 100644 frontend/vben/src/layouts/default/sider/DragBar.vue
create mode 100644 frontend/vben/src/layouts/default/sider/LayoutSider.vue
create mode 100644 frontend/vben/src/layouts/default/sider/MixSider.vue
create mode 100644 frontend/vben/src/layouts/default/sider/index.vue
create mode 100644 frontend/vben/src/layouts/default/sider/useLayoutSider.ts
create mode 100644 frontend/vben/src/layouts/default/tabs/components/FoldButton.vue
create mode 100644 frontend/vben/src/layouts/default/tabs/components/TabContent.vue
create mode 100644 frontend/vben/src/layouts/default/tabs/components/TabRedo.vue
create mode 100644 frontend/vben/src/layouts/default/tabs/index.less
create mode 100644 frontend/vben/src/layouts/default/tabs/index.vue
create mode 100644 frontend/vben/src/layouts/default/tabs/types.ts
create mode 100644 frontend/vben/src/layouts/default/tabs/useMultipleTabs.ts
create mode 100644 frontend/vben/src/layouts/default/tabs/useTabDropdown.ts
create mode 100644 frontend/vben/src/layouts/default/trigger/HeaderTrigger.vue
create mode 100644 frontend/vben/src/layouts/default/trigger/SiderTrigger.vue
create mode 100644 frontend/vben/src/layouts/default/trigger/index.vue
create mode 100644 frontend/vben/src/layouts/page/index.vue
create mode 100644 frontend/vben/src/layouts/page/transition.ts
create mode 100644 frontend/vben/src/locales/helper.ts
create mode 100644 frontend/vben/src/locales/lang/en.ts
create mode 100644 frontend/vben/src/locales/lang/en/common.ts
create mode 100644 frontend/vben/src/locales/lang/en/component.ts
create mode 100644 frontend/vben/src/locales/lang/en/layout.ts
create mode 100644 frontend/vben/src/locales/lang/en/routes/basic.ts
create mode 100644 frontend/vben/src/locales/lang/en/routes/dashboard.ts
create mode 100644 frontend/vben/src/locales/lang/en/routes/demo.ts
create mode 100644 frontend/vben/src/locales/lang/en/sys.ts
create mode 100644 frontend/vben/src/locales/lang/zh-CN/antdLocale/DatePicker.ts
create mode 100644 frontend/vben/src/locales/lang/zh-CN/common.ts
create mode 100644 frontend/vben/src/locales/lang/zh-CN/component.ts
create mode 100644 frontend/vben/src/locales/lang/zh-CN/layout.ts
create mode 100644 frontend/vben/src/locales/lang/zh-CN/routes/basic.ts
create mode 100644 frontend/vben/src/locales/lang/zh-CN/routes/dashboard.ts
create mode 100644 frontend/vben/src/locales/lang/zh-CN/routes/demo.ts
create mode 100644 frontend/vben/src/locales/lang/zh-CN/sys.ts
create mode 100644 frontend/vben/src/locales/lang/zh_CN.ts
create mode 100644 frontend/vben/src/locales/setupI18n.ts
create mode 100644 frontend/vben/src/locales/useLocale.ts
create mode 100644 frontend/vben/src/logics/initAppConfig.ts
create mode 100644 frontend/vben/src/logics/mitt/routeChange.ts
create mode 100644 frontend/vben/src/logics/theme/dark.ts
create mode 100644 frontend/vben/src/logics/theme/index.ts
create mode 100644 frontend/vben/src/logics/theme/updateBackground.ts
create mode 100644 frontend/vben/src/logics/theme/updateColorWeak.ts
create mode 100644 frontend/vben/src/logics/theme/updateGrayMode.ts
create mode 100644 frontend/vben/src/logics/theme/util.ts
create mode 100644 frontend/vben/src/main.ts
create mode 100644 frontend/vben/src/router/constant.ts
create mode 100644 frontend/vben/src/router/guard/index.ts
create mode 100644 frontend/vben/src/router/guard/paramMenuGuard.ts
create mode 100644 frontend/vben/src/router/guard/permissionGuard.ts
create mode 100644 frontend/vben/src/router/guard/stateGuard.ts
create mode 100644 frontend/vben/src/router/helper/menuHelper.ts
create mode 100644 frontend/vben/src/router/helper/routeHelper.ts
create mode 100644 frontend/vben/src/router/index.ts
create mode 100644 frontend/vben/src/router/menus/index.ts
create mode 100644 frontend/vben/src/router/routes/basic.ts
create mode 100644 frontend/vben/src/router/routes/index.ts
create mode 100644 frontend/vben/src/router/routes/modules/about.ts
create mode 100644 frontend/vben/src/router/routes/modules/dashboard.ts
create mode 100644 frontend/vben/src/router/routes/modules/setup.ts
create mode 100644 frontend/vben/src/router/types.ts
create mode 100644 frontend/vben/src/settings/componentSetting.ts
create mode 100644 frontend/vben/src/settings/designSetting.ts
create mode 100644 frontend/vben/src/settings/encryptionSetting.ts
create mode 100644 frontend/vben/src/settings/localeSetting.ts
create mode 100644 frontend/vben/src/settings/projectSetting.ts
create mode 100644 frontend/vben/src/settings/siteSetting.ts
create mode 100644 frontend/vben/src/store/index.ts
create mode 100644 frontend/vben/src/store/modules/app.ts
create mode 100644 frontend/vben/src/store/modules/locale.ts
create mode 100644 frontend/vben/src/store/modules/multipleTab.ts
create mode 100644 frontend/vben/src/store/modules/permission.ts
create mode 100644 frontend/vben/src/store/modules/user.ts
create mode 100644 frontend/vben/src/utils/auth/index.ts
create mode 100644 frontend/vben/src/utils/bem.ts
create mode 100644 frontend/vben/src/utils/cache/index.ts
create mode 100644 frontend/vben/src/utils/cache/memory.ts
create mode 100644 frontend/vben/src/utils/cache/persistent.ts
create mode 100644 frontend/vben/src/utils/cache/storageCache.ts
create mode 100644 frontend/vben/src/utils/cipher.ts
create mode 100644 frontend/vben/src/utils/color.ts
create mode 100644 frontend/vben/src/utils/dateUtil.ts
create mode 100644 frontend/vben/src/utils/domUtils.ts
create mode 100644 frontend/vben/src/utils/env.ts
create mode 100644 frontend/vben/src/utils/event/index.ts
create mode 100644 frontend/vben/src/utils/factory/createAsyncComponent.tsx
create mode 100644 frontend/vben/src/utils/file/base64Conver.ts
create mode 100644 frontend/vben/src/utils/file/download.ts
create mode 100644 frontend/vben/src/utils/helper/treeHelper.ts
create mode 100644 frontend/vben/src/utils/helper/tsxHelper.tsx
create mode 100644 frontend/vben/src/utils/http/axios/Axios.ts
create mode 100644 frontend/vben/src/utils/http/axios/axiosCancel.ts
create mode 100644 frontend/vben/src/utils/http/axios/axiosRetry.ts
create mode 100644 frontend/vben/src/utils/http/axios/axiosTransform.ts
create mode 100644 frontend/vben/src/utils/http/axios/checkStatus.ts
create mode 100644 frontend/vben/src/utils/http/axios/helper.ts
create mode 100644 frontend/vben/src/utils/http/axios/index.ts
create mode 100644 frontend/vben/src/utils/index.ts
create mode 100644 frontend/vben/src/utils/is.ts
create mode 100644 frontend/vben/src/utils/lib/echarts.ts
create mode 100644 frontend/vben/src/utils/log.ts
create mode 100644 frontend/vben/src/utils/mitt.ts
create mode 100644 frontend/vben/src/utils/propTypes.ts
create mode 100644 frontend/vben/src/utils/props.ts
create mode 100644 frontend/vben/src/utils/types.ts
create mode 100644 frontend/vben/src/utils/uuid.ts
create mode 100644 frontend/vben/src/views/dashboard/analysis/components/GrowCard.vue
create mode 100644 frontend/vben/src/views/dashboard/analysis/components/SalesProductPie.vue
create mode 100644 frontend/vben/src/views/dashboard/analysis/components/SiteAnalysis.vue
create mode 100644 frontend/vben/src/views/dashboard/analysis/components/VisitAnalysis.vue
create mode 100644 frontend/vben/src/views/dashboard/analysis/components/VisitAnalysisBar.vue
create mode 100644 frontend/vben/src/views/dashboard/analysis/components/VisitRadar.vue
create mode 100644 frontend/vben/src/views/dashboard/analysis/components/VisitSource.vue
create mode 100644 frontend/vben/src/views/dashboard/analysis/components/props.ts
create mode 100644 frontend/vben/src/views/dashboard/analysis/data.ts
create mode 100644 frontend/vben/src/views/dashboard/analysis/index.vue
create mode 100644 frontend/vben/src/views/dashboard/workbench/components/DynamicInfo.vue
create mode 100644 frontend/vben/src/views/dashboard/workbench/components/ProjectCard.vue
create mode 100644 frontend/vben/src/views/dashboard/workbench/components/QuickNav.vue
create mode 100644 frontend/vben/src/views/dashboard/workbench/components/SaleRadar.vue
create mode 100644 frontend/vben/src/views/dashboard/workbench/components/WorkbenchHeader.vue
create mode 100644 frontend/vben/src/views/dashboard/workbench/components/data.ts
create mode 100644 frontend/vben/src/views/dashboard/workbench/index.vue
create mode 100644 frontend/vben/src/views/setup/index.vue
create mode 100644 frontend/vben/src/views/sys/about/index.vue
create mode 100644 frontend/vben/src/views/sys/exception/Exception.vue
create mode 100644 frontend/vben/src/views/sys/exception/index.ts
create mode 100644 frontend/vben/src/views/sys/login/ForgetPasswordForm.vue
create mode 100644 frontend/vben/src/views/sys/login/Login.vue
create mode 100644 frontend/vben/src/views/sys/login/LoginForm.vue
create mode 100644 frontend/vben/src/views/sys/login/LoginFormTitle.vue
create mode 100644 frontend/vben/src/views/sys/login/MobileForm.vue
create mode 100644 frontend/vben/src/views/sys/login/RegisterForm.vue
create mode 100644 frontend/vben/src/views/sys/login/SessionTimeoutLogin.vue
create mode 100644 frontend/vben/src/views/sys/login/useLogin.ts
create mode 100644 frontend/vben/src/views/sys/redirect/index.vue
create mode 100644 frontend/vben/stylelint.config.js
create mode 100644 frontend/vben/tsconfig.json
create mode 100644 frontend/vben/types/axios.d.ts
create mode 100644 frontend/vben/types/config.d.ts
create mode 100644 frontend/vben/types/global.d.ts
create mode 100644 frontend/vben/types/index.d.ts
create mode 100644 frontend/vben/types/module.d.ts
create mode 100644 frontend/vben/types/store.d.ts
create mode 100644 frontend/vben/types/utils.d.ts
create mode 100644 frontend/vben/types/vue-router.d.ts
create mode 100644 frontend/vben/vite.config.ts
create mode 100644 frontend/vben/windi.config.ts
create mode 100644 frontend/vben/yarn.lock
diff --git a/.gitignore b/.gitignore
index 66fd13c..1b3a47d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,6 @@
# Dependency directories (remove the comment below to include it)
# vendor/
+
+# MacOS storage file
+.DS_Store
diff --git a/frontend/vben/.editorconfig b/frontend/vben/.editorconfig
new file mode 100644
index 0000000..dccf841
--- /dev/null
+++ b/frontend/vben/.editorconfig
@@ -0,0 +1,19 @@
+root = true
+
+[*]
+charset=utf-8
+end_of_line=lf
+insert_final_newline=true
+indent_style=space
+indent_size=2
+max_line_length = 100
+
+[*.{yml,yaml,json}]
+indent_style = space
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
+
+[Makefile]
+indent_style = tab
diff --git a/frontend/vben/.env b/frontend/vben/.env
new file mode 100644
index 0000000..d696d5f
--- /dev/null
+++ b/frontend/vben/.env
@@ -0,0 +1,8 @@
+# port
+VITE_PORT = 3100
+
+# spa-title
+VITE_GLOB_APP_TITLE = 试卷网络管理系统
+
+# spa shortname
+VITE_GLOB_APP_SHORT_NAME = paper_manager
diff --git a/frontend/vben/.env.development b/frontend/vben/.env.development
new file mode 100644
index 0000000..9236123
--- /dev/null
+++ b/frontend/vben/.env.development
@@ -0,0 +1,22 @@
+# Whether to open mock
+VITE_USE_MOCK = true
+
+# public path
+VITE_PUBLIC_PATH = /
+
+# Cross-domain proxy, you can configure multiple
+# Please note that no line breaks
+VITE_PROXY = [["/basic-api","http://localhost:3000"],["/upload","http://localhost:3300/upload"]]
+# VITE_PROXY=[["/api","https://vvbin.cn/test"]]
+
+# Delete console
+VITE_DROP_CONSOLE = false
+
+# Basic interface address SPA
+VITE_GLOB_API_URL=/basic-api
+
+# File upload address, optional
+VITE_GLOB_UPLOAD_URL=/upload
+
+# Interface prefix
+VITE_GLOB_API_URL_PREFIX=
diff --git a/frontend/vben/.env.production b/frontend/vben/.env.production
new file mode 100644
index 0000000..9785c06
--- /dev/null
+++ b/frontend/vben/.env.production
@@ -0,0 +1,35 @@
+# Whether to open mock
+VITE_USE_MOCK = false
+
+# public path
+VITE_PUBLIC_PATH = /
+
+# Delete console
+VITE_DROP_CONSOLE = true
+
+# Whether to enable gzip or brotli compression
+# Optional: gzip | brotli | none
+# If you need multiple forms, you can use `,` to separate
+VITE_BUILD_COMPRESS = 'none'
+
+# Whether to delete origin files when using compress, default false
+VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
+
+# Basic interface address SPA
+VITE_GLOB_API_URL=/basic-api
+
+# File upload address, optional
+# It can be forwarded by nginx or write the actual address directly
+VITE_GLOB_UPLOAD_URL=/upload
+
+# Interface prefix
+VITE_GLOB_API_URL_PREFIX=
+
+# Whether to enable image compression
+VITE_USE_IMAGEMIN= true
+
+# use pwa
+VITE_USE_PWA = false
+
+# Is it compatible with older browsers
+VITE_LEGACY = false
diff --git a/frontend/vben/.eslintignore b/frontend/vben/.eslintignore
new file mode 100644
index 0000000..348631b
--- /dev/null
+++ b/frontend/vben/.eslintignore
@@ -0,0 +1,15 @@
+
+*.sh
+node_modules
+*.md
+*.woff
+*.ttf
+.vscode
+.idea
+dist
+/public
+/docs
+.husky
+.local
+/bin
+Dockerfile
diff --git a/frontend/vben/.eslintrc.js b/frontend/vben/.eslintrc.js
new file mode 100644
index 0000000..9aa3e10
--- /dev/null
+++ b/frontend/vben/.eslintrc.js
@@ -0,0 +1,76 @@
+module.exports = {
+ root: true,
+ env: {
+ browser: true,
+ node: true,
+ es6: true,
+ },
+ parser: 'vue-eslint-parser',
+ parserOptions: {
+ parser: '@typescript-eslint/parser',
+ ecmaVersion: 2020,
+ sourceType: 'module',
+ jsxPragma: 'React',
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ extends: [
+ 'plugin:vue/vue3-recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:prettier/recommended',
+ ],
+ rules: {
+ 'vue/script-setup-uses-vars': 'error',
+ '@typescript-eslint/ban-ts-ignore': 'off',
+ '@typescript-eslint/explicit-function-return-type': 'off',
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/no-var-requires': 'off',
+ '@typescript-eslint/no-empty-function': 'off',
+ 'vue/custom-event-name-casing': 'off',
+ 'no-use-before-define': 'off',
+ '@typescript-eslint/no-use-before-define': 'off',
+ '@typescript-eslint/ban-ts-comment': 'off',
+ '@typescript-eslint/ban-types': 'off',
+ '@typescript-eslint/no-non-null-assertion': 'off',
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ {
+ argsIgnorePattern: '^_',
+ varsIgnorePattern: '^_',
+ },
+ ],
+ 'no-unused-vars': [
+ 'error',
+ {
+ argsIgnorePattern: '^_',
+ varsIgnorePattern: '^_',
+ },
+ ],
+ 'space-before-function-paren': 'off',
+
+ 'vue/attributes-order': 'off',
+ 'vue/one-component-per-file': 'off',
+ 'vue/html-closing-bracket-newline': 'off',
+ 'vue/max-attributes-per-line': 'off',
+ 'vue/multiline-html-element-content-newline': 'off',
+ 'vue/singleline-html-element-content-newline': 'off',
+ 'vue/attribute-hyphenation': 'off',
+ 'vue/require-default-prop': 'off',
+ 'vue/require-explicit-emits': 'off',
+ 'vue/html-self-closing': [
+ 'error',
+ {
+ html: {
+ void: 'always',
+ normal: 'never',
+ component: 'always',
+ },
+ svg: 'always',
+ math: 'always',
+ },
+ ],
+ 'vue/multi-word-component-names': 'off',
+ },
+};
diff --git a/frontend/vben/.gitignore b/frontend/vben/.gitignore
new file mode 100644
index 0000000..e6922c4
--- /dev/null
+++ b/frontend/vben/.gitignore
@@ -0,0 +1,33 @@
+node_modules
+.DS_Store
+dist
+.npmrc
+.cache
+
+tests/server/static
+tests/server/static/upload
+
+.local
+# local env files
+.env.local
+.env.*.local
+.eslintcache
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+# .vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+package-lock.json
+pnpm-lock.yaml
+
diff --git a/frontend/vben/.gitpod.yml b/frontend/vben/.gitpod.yml
new file mode 100644
index 0000000..866381f
--- /dev/null
+++ b/frontend/vben/.gitpod.yml
@@ -0,0 +1,6 @@
+ports:
+ - port: 3344
+ onOpen: open-preview
+tasks:
+ - init: pnpm install
+ command: pnpm run dev
diff --git a/frontend/vben/.prettierignore b/frontend/vben/.prettierignore
new file mode 100644
index 0000000..f7e39e6
--- /dev/null
+++ b/frontend/vben/.prettierignore
@@ -0,0 +1,9 @@
+/dist/*
+.local
+.output.js
+/node_modules/**
+
+**/*.svg
+**/*.sh
+
+/public/*
diff --git a/frontend/vben/.stylelintignore b/frontend/vben/.stylelintignore
new file mode 100644
index 0000000..0517076
--- /dev/null
+++ b/frontend/vben/.stylelintignore
@@ -0,0 +1,3 @@
+/dist/*
+/public/*
+public/*
diff --git a/frontend/vben/LICENSE b/frontend/vben/LICENSE
new file mode 100644
index 0000000..9e4c0d4
--- /dev/null
+++ b/frontend/vben/LICENSE
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2020-2023, Vben
+Copyright (c) 2023-2023, fumiama(源文雨)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/frontend/vben/README.md b/frontend/vben/README.md
new file mode 100644
index 0000000..7e34298
--- /dev/null
+++ b/frontend/vben/README.md
@@ -0,0 +1,101 @@
+
+
+[](LICENSE)
+
+
Vue vben admin
+
+
+## 简介
+
+精简 Vue Vben Admin。
+
+## 特性
+
+- **最新技术栈**:使用 Vue3/vite2 等前端前沿技术开发
+- **TypeScript**: 应用程序级 JavaScript 的语言
+- **主题**:可配置的主题
+- **国际化**:内置完善的国际化方案
+- **Mock 数据** 内置 Mock 数据方案
+- **权限** 内置完善的动态路由权限生成方案
+- **组件** 二次封装了多个常用的组件
+
+## 预览
+
+- [vue-vben-admin](https://vvbin.cn/next/) - 完整版中文站点
+- [vue-vben-admin-gh-pages](https://anncwb.github.io/vue-vben-admin/) - 完整版 github 站点
+- [vben-admin-thin-next](https://vvbin.cn/thin/next/) - 简化版中文站点
+- [vben-admin-thin-gh-pages](https://anncwb.github.io/vben-admin-thin-next/) - 简化版 github 站点
+
+## 准备
+
+- [node](http://nodejs.org/) 和 [git](https://git-scm.com/) -项目开发环境
+- [Vite](https://vitejs.dev/) - 熟悉 vite 特性
+- [Vue3](https://v3.vuejs.org/) - 熟悉 Vue 基础语法
+- [TypeScript](https://www.typescriptlang.org/) - 熟悉`TypeScript`基本语法
+- [Es6+](http://es6.ruanyifeng.com/) - 熟悉 es6 基本语法
+- [Vue-Router-Next](https://next.router.vuejs.org/) - 熟悉 vue-router 基本使用
+- [Ant-Design-Vue](https://2x.antdv.com/docs/vue/introduce-cn/) - ui 基本使用
+- [Mock.js](https://github.com/nuysoft/Mock) - mockjs 基本语法
+
+## 安装使用
+
+- 获取项目代码
+
+```bash
+git clone https://github.com/anncwb/vue-vben-admin.git
+```
+
+- 安装依赖
+
+```bash
+cd vue-vben-admin
+git checkout thin
+pnpm install
+
+```
+
+- 运行
+
+```bash
+pnpm serve
+```
+
+- 打包
+
+```bash
+pnpm build
+```
+
+## Git 贡献提交规范
+
+- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
+
+ - `feat` 增加新功能
+ - `fix` 修复问题/BUG
+ - `style` 代码风格相关无影响运行结果的
+ - `perf` 优化/性能提升
+ - `refactor` 重构
+ - `revert` 撤销修改
+ - `test` 测试相关
+ - `docs` 文档/注释
+ - `chore` 依赖更新/脚手架配置修改等
+ - `workflow` 工作流改进
+ - `ci` 持续集成
+ - `types` 类型定义文件更改
+ - `wip` 开发中
+
+## 相关仓库
+
+如果这些插件对你有帮助,可以给一个 star 支持下
+
+- [vite-plugin-mock](https://github.com/anncwb/vite-plugin-mock) - 用于本地及开发环境数据 mock
+- [vite-plugin-html](https://github.com/anncwb/vite-plugin-html) - 用于 html 模版转换及压缩
+- [vite-plugin-style-import](https://github.com/anncwb/vite-plugin-style-import) - 用于组件库样式按需引入
+- [vite-plugin-theme](https://github.com/anncwb/vite-plugin-theme) - 用于在线切换主题色等颜色相关配置
+- [vite-plugin-imagemin](https://github.com/anncwb/vite-plugin-imagemin) - 用于打包压缩图片资源
+- [vite-plugin-compression](https://github.com/anncwb/vite-plugin-compression) - 用于打包输出.gz|.brotil 文件
+- [vite-plugin-svg-icons](https://github.com/anncwb/vite-plugin-svg-icons) - 用于快速生成 svg 雪碧图
+
+## License
+
+[MIT © Vben-2020](./LICENSE)
diff --git a/frontend/vben/build/config/themeConfig.ts b/frontend/vben/build/config/themeConfig.ts
new file mode 100644
index 0000000..c816b6d
--- /dev/null
+++ b/frontend/vben/build/config/themeConfig.ts
@@ -0,0 +1,79 @@
+import { generate } from '@ant-design/colors';
+
+export const primaryColor = '#0960bd';
+
+export const darkMode = 'light';
+
+type Fn = (...arg: any) => any;
+
+type GenerateTheme = 'default' | 'dark';
+
+export interface GenerateColorsParams {
+ mixLighten: Fn;
+ mixDarken: Fn;
+ tinycolor: any;
+ color?: string;
+}
+
+export function generateAntColors(color: string, theme: GenerateTheme = 'default') {
+ return generate(color, {
+ theme,
+ });
+}
+
+export function getThemeColors(color?: string) {
+ const tc = color || primaryColor;
+ const lightColors = generateAntColors(tc);
+ const primary = lightColors[5];
+ const modeColors = generateAntColors(primary, 'dark');
+
+ return [...lightColors, ...modeColors];
+}
+
+export function generateColors({
+ color = primaryColor,
+ mixLighten,
+ mixDarken,
+ tinycolor,
+}: GenerateColorsParams) {
+ const arr = new Array(19).fill(0);
+ const lightens = arr.map((_t, i) => {
+ return mixLighten(color, i / 5);
+ });
+
+ const darkens = arr.map((_t, i) => {
+ return mixDarken(color, i / 5);
+ });
+
+ const alphaColors = arr.map((_t, i) => {
+ return tinycolor(color)
+ .setAlpha(i / 20)
+ .toRgbString();
+ });
+
+ const shortAlphaColors = alphaColors.map((item) => item.replace(/\s/g, '').replace(/0\./g, '.'));
+
+ const tinycolorLightens = arr
+ .map((_t, i) => {
+ return tinycolor(color)
+ .lighten(i * 5)
+ .toHexString();
+ })
+ .filter((item) => item !== '#ffffff');
+
+ const tinycolorDarkens = arr
+ .map((_t, i) => {
+ return tinycolor(color)
+ .darken(i * 5)
+ .toHexString();
+ })
+ .filter((item) => item !== '#000000');
+ return [
+ ...lightens,
+ ...darkens,
+ ...alphaColors,
+ ...shortAlphaColors,
+ ...tinycolorDarkens,
+ ...tinycolorLightens,
+ ].filter((item) => !item.includes('-'));
+}
diff --git a/frontend/vben/build/constant.ts b/frontend/vben/build/constant.ts
new file mode 100644
index 0000000..2c6119c
--- /dev/null
+++ b/frontend/vben/build/constant.ts
@@ -0,0 +1,6 @@
+/**
+ * The name of the configuration file entered in the production environment
+ */
+export const GLOB_CONFIG_FILE_NAME = '_app.config.js';
+
+export const OUTPUT_DIR = 'dist';
diff --git a/frontend/vben/build/generate/generateModifyVars.ts b/frontend/vben/build/generate/generateModifyVars.ts
new file mode 100644
index 0000000..44670e2
--- /dev/null
+++ b/frontend/vben/build/generate/generateModifyVars.ts
@@ -0,0 +1,37 @@
+import { generateAntColors, primaryColor } from '../config/themeConfig';
+import { getThemeVariables } from 'ant-design-vue/dist/theme';
+import { resolve } from 'path';
+
+/**
+ * less global variable
+ */
+export function generateModifyVars(dark = false) {
+ const palettes = generateAntColors(primaryColor);
+ const primary = palettes[5];
+
+ const primaryColorObj: Record = {};
+
+ for (let index = 0; index < 10; index++) {
+ primaryColorObj[`primary-${index + 1}`] = palettes[index];
+ }
+
+ const modifyVars = getThemeVariables({ dark });
+ return {
+ ...modifyVars,
+ // Used for global import to avoid the need to import each style file separately
+ // reference: Avoid repeated references
+ hack: `${modifyVars.hack} @import (reference) "${resolve('src/design/config.less')}";`,
+ 'primary-color': primary,
+ ...primaryColorObj,
+ 'info-color': primary,
+ 'processing-color': primary,
+ 'success-color': '#55D187', // Success color
+ 'error-color': '#ED6F6F', // False color
+ 'warning-color': '#EFBD47', // Warning color
+ //'border-color-base': '#EEEEEE',
+ 'font-size-base': '14px', // Main font size
+ 'border-radius-base': '2px', // Component/float fillet
+ 'link-color': primary, // Link color
+ 'app-content-background': '#fafafa', // Link color
+ };
+}
diff --git a/frontend/vben/build/generate/icon/index.ts b/frontend/vben/build/generate/icon/index.ts
new file mode 100644
index 0000000..b01fec4
--- /dev/null
+++ b/frontend/vben/build/generate/icon/index.ts
@@ -0,0 +1,72 @@
+import path from 'path';
+import fs from 'fs-extra';
+import inquirer from 'inquirer';
+import colors from 'picocolors';
+import pkg from '../../../package.json';
+
+async function generateIcon() {
+ const dir = path.resolve(process.cwd(), 'node_modules/@iconify/json');
+
+ const raw = await fs.readJSON(path.join(dir, 'collections.json'));
+
+ const collections = Object.entries(raw).map(([id, v]) => ({
+ ...(v as any),
+ id,
+ }));
+
+ const choices = collections.map((item) => ({ key: item.id, value: item.id, name: item.name }));
+
+ inquirer
+ .prompt([
+ {
+ type: 'list',
+ name: 'useType',
+ choices: [
+ { key: 'local', value: 'local', name: 'Local' },
+ { key: 'onLine', value: 'onLine', name: 'OnLine' },
+ ],
+ message: 'How to use icons?',
+ },
+ {
+ type: 'list',
+ name: 'iconSet',
+ choices: choices,
+ message: 'Select the icon set that needs to be generated?',
+ },
+ {
+ type: 'input',
+ name: 'output',
+ message: 'Select the icon set that needs to be generated?',
+ default: 'src/components/Icon/data',
+ },
+ ])
+ .then(async (answers) => {
+ const { iconSet, output, useType } = answers;
+ const outputDir = path.resolve(process.cwd(), output);
+ fs.ensureDir(outputDir);
+ const genCollections = collections.filter((item) => [iconSet].includes(item.id));
+ const prefixSet: string[] = [];
+ for (const info of genCollections) {
+ const data = await fs.readJSON(path.join(dir, 'json', `${info.id}.json`));
+ if (data) {
+ const { prefix } = data;
+ const isLocal = useType === 'local';
+ const icons = Object.keys(data.icons).map(
+ (item) => `${isLocal ? prefix + ':' : ''}${item}`,
+ );
+
+ await fs.writeFileSync(
+ path.join(output, `icons.data.ts`),
+ `export default ${isLocal ? JSON.stringify(icons) : JSON.stringify({ prefix, icons })}`,
+ );
+ prefixSet.push(prefix);
+ }
+ }
+ fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite'));
+ console.log(
+ `✨ ${colors.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`,
+ );
+ });
+}
+
+generateIcon();
diff --git a/frontend/vben/build/getConfigFileName.ts b/frontend/vben/build/getConfigFileName.ts
new file mode 100644
index 0000000..d61cd41
--- /dev/null
+++ b/frontend/vben/build/getConfigFileName.ts
@@ -0,0 +1,9 @@
+/**
+ * Get the configuration file variable name
+ * @param env
+ */
+export const getConfigFileName = (env: Record) => {
+ return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
+ .toUpperCase()
+ .replace(/\s/g, '');
+};
diff --git a/frontend/vben/build/script/buildConf.ts b/frontend/vben/build/script/buildConf.ts
new file mode 100644
index 0000000..0c8089c
--- /dev/null
+++ b/frontend/vben/build/script/buildConf.ts
@@ -0,0 +1,47 @@
+/**
+ * Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
+ */
+import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
+import fs, { writeFileSync } from 'fs-extra';
+import colors from 'picocolors';
+
+import { getEnvConfig, getRootPath } from '../utils';
+import { getConfigFileName } from '../getConfigFileName';
+
+import pkg from '../../package.json';
+
+interface CreateConfigParams {
+ configName: string;
+ config: any;
+ configFileName?: string;
+}
+
+function createConfig(params: CreateConfigParams) {
+ const { configName, config, configFileName } = params;
+ try {
+ const windowConf = `window.${configName}`;
+ // Ensure that the variable will not be modified
+ let configStr = `${windowConf}=${JSON.stringify(config)};`;
+ configStr += `
+ Object.freeze(${windowConf});
+ Object.defineProperty(window, "${configName}", {
+ configurable: false,
+ writable: false,
+ });
+ `.replace(/\s/g, '');
+
+ fs.mkdirp(getRootPath(OUTPUT_DIR));
+ writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr);
+
+ console.log(colors.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
+ console.log(colors.gray(OUTPUT_DIR + '/' + colors.green(configFileName)) + '\n');
+ } catch (error) {
+ console.log(colors.red('configuration file configuration file failed to package:\n' + error));
+ }
+}
+
+export function runBuildConfig() {
+ const config = getEnvConfig();
+ const configFileName = getConfigFileName(config);
+ createConfig({ config, configName: configFileName, configFileName: GLOB_CONFIG_FILE_NAME });
+}
diff --git a/frontend/vben/build/script/postBuild.ts b/frontend/vben/build/script/postBuild.ts
new file mode 100644
index 0000000..42635d8
--- /dev/null
+++ b/frontend/vben/build/script/postBuild.ts
@@ -0,0 +1,23 @@
+// #!/usr/bin/env node
+
+import { runBuildConfig } from './buildConf';
+import colors from 'picocolors';
+
+import pkg from '../../package.json';
+
+export const runBuild = async () => {
+ try {
+ const argvList = process.argv.splice(2);
+
+ // Generate configuration file
+ if (!argvList.includes('disabled-config')) {
+ runBuildConfig();
+ }
+
+ console.log(`✨ ${colors.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
+ } catch (error) {
+ console.log(colors.red('vite build error:\n' + error));
+ process.exit(1);
+ }
+};
+runBuild();
diff --git a/frontend/vben/build/utils.ts b/frontend/vben/build/utils.ts
new file mode 100644
index 0000000..c201514
--- /dev/null
+++ b/frontend/vben/build/utils.ts
@@ -0,0 +1,92 @@
+import fs from 'fs';
+import path from 'path';
+import dotenv from 'dotenv';
+
+export function isDevFn(mode: string): boolean {
+ return mode === 'development';
+}
+
+export function isProdFn(mode: string): boolean {
+ return mode === 'production';
+}
+
+/**
+ * Whether to generate package preview
+ */
+export function isReportMode(): boolean {
+ return process.env.REPORT === 'true';
+}
+
+// Read all environment variable configuration files to process.env
+export function wrapperEnv(envConf: Recordable): ViteEnv {
+ const ret: any = {};
+
+ for (const envName of Object.keys(envConf)) {
+ let realName = envConf[envName].replace(/\\n/g, '\n');
+ realName = realName === 'true' ? true : realName === 'false' ? false : realName;
+
+ if (envName === 'VITE_PORT') {
+ realName = Number(realName);
+ }
+ if (envName === 'VITE_PROXY' && realName) {
+ try {
+ realName = JSON.parse(realName.replace(/'/g, '"'));
+ } catch (error) {
+ realName = '';
+ }
+ }
+ ret[envName] = realName;
+ if (typeof realName === 'string') {
+ process.env[envName] = realName;
+ } else if (typeof realName === 'object') {
+ process.env[envName] = JSON.stringify(realName);
+ }
+ }
+ return ret;
+}
+
+/**
+ * 获取当前环境下生效的配置文件名
+ */
+function getConfFiles() {
+ const script = process.env.npm_lifecycle_script;
+ const reg = new RegExp('--mode ([a-z_\\d]+)');
+ const result = reg.exec(script as string) as any;
+ if (result) {
+ const mode = result[1] as string;
+ return ['.env', `.env.${mode}`];
+ }
+ return ['.env', '.env.production'];
+}
+
+/**
+ * Get the environment variables starting with the specified prefix
+ * @param match prefix
+ * @param confFiles ext
+ */
+export function getEnvConfig(match = 'VITE_GLOB_', confFiles = getConfFiles()) {
+ let envConfig = {};
+ confFiles.forEach((item) => {
+ try {
+ const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
+ envConfig = { ...envConfig, ...env };
+ } catch (e) {
+ console.error(`Error in parsing ${item}`, e);
+ }
+ });
+ const reg = new RegExp(`^(${match})`);
+ Object.keys(envConfig).forEach((key) => {
+ if (!reg.test(key)) {
+ Reflect.deleteProperty(envConfig, key);
+ }
+ });
+ return envConfig;
+}
+
+/**
+ * Get user root directory
+ * @param dir file path
+ */
+export function getRootPath(...dir: string[]) {
+ return path.resolve(process.cwd(), ...dir);
+}
diff --git a/frontend/vben/build/vite/plugin/compress.ts b/frontend/vben/build/vite/plugin/compress.ts
new file mode 100644
index 0000000..ff4f631
--- /dev/null
+++ b/frontend/vben/build/vite/plugin/compress.ts
@@ -0,0 +1,35 @@
+/**
+ * Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
+ * https://github.com/anncwb/vite-plugin-compression
+ */
+import type { PluginOption } from 'vite';
+import compressPlugin from 'vite-plugin-compression';
+
+export function configCompressPlugin(
+ compress: 'gzip' | 'brotli' | 'none',
+ deleteOriginFile = false,
+): PluginOption | PluginOption[] {
+ const compressList = compress.split(',');
+
+ const plugins: PluginOption[] = [];
+
+ if (compressList.includes('gzip')) {
+ plugins.push(
+ compressPlugin({
+ ext: '.gz',
+ deleteOriginFile,
+ }),
+ );
+ }
+
+ if (compressList.includes('brotli')) {
+ plugins.push(
+ compressPlugin({
+ ext: '.br',
+ algorithm: 'brotliCompress',
+ deleteOriginFile,
+ }),
+ );
+ }
+ return plugins;
+}
diff --git a/frontend/vben/build/vite/plugin/html.ts b/frontend/vben/build/vite/plugin/html.ts
new file mode 100644
index 0000000..6af034a
--- /dev/null
+++ b/frontend/vben/build/vite/plugin/html.ts
@@ -0,0 +1,40 @@
+/**
+ * Plugin to minimize and use ejs template syntax in index.html.
+ * https://github.com/anncwb/vite-plugin-html
+ */
+import type { PluginOption } from 'vite';
+import { createHtmlPlugin } from 'vite-plugin-html';
+import pkg from '../../../package.json';
+import { GLOB_CONFIG_FILE_NAME } from '../../constant';
+
+export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
+ const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env;
+
+ const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`;
+
+ const getAppConfigSrc = () => {
+ return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
+ };
+
+ const htmlPlugin: PluginOption[] = createHtmlPlugin({
+ minify: isBuild,
+ inject: {
+ // Inject data into ejs template
+ data: {
+ title: VITE_GLOB_APP_TITLE,
+ },
+ // Embed the generated app.config.js file
+ tags: isBuild
+ ? [
+ {
+ tag: 'script',
+ attrs: {
+ src: getAppConfigSrc(),
+ },
+ },
+ ]
+ : [],
+ },
+ });
+ return htmlPlugin;
+}
diff --git a/frontend/vben/build/vite/plugin/imagemin.ts b/frontend/vben/build/vite/plugin/imagemin.ts
new file mode 100644
index 0000000..a023573
--- /dev/null
+++ b/frontend/vben/build/vite/plugin/imagemin.ts
@@ -0,0 +1,34 @@
+// Image resource files used to compress the output of the production environment
+// https://github.com/anncwb/vite-plugin-imagemin
+import viteImagemin from 'vite-plugin-imagemin';
+
+export function configImageminPlugin() {
+ const plugin = viteImagemin({
+ gifsicle: {
+ optimizationLevel: 7,
+ interlaced: false,
+ },
+ optipng: {
+ optimizationLevel: 7,
+ },
+ mozjpeg: {
+ quality: 20,
+ },
+ pngquant: {
+ quality: [0.8, 0.9],
+ speed: 4,
+ },
+ svgo: {
+ plugins: [
+ {
+ name: 'removeViewBox',
+ },
+ {
+ name: 'removeEmptyAttrs',
+ active: false,
+ },
+ ],
+ },
+ });
+ return plugin;
+}
diff --git a/frontend/vben/build/vite/plugin/index.ts b/frontend/vben/build/vite/plugin/index.ts
new file mode 100644
index 0000000..66956a3
--- /dev/null
+++ b/frontend/vben/build/vite/plugin/index.ts
@@ -0,0 +1,82 @@
+import { PluginOption } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import vueJsx from '@vitejs/plugin-vue-jsx'
+import legacy from '@vitejs/plugin-legacy'
+import purgeIcons from 'vite-plugin-purge-icons'
+import windiCSS from 'vite-plugin-windicss'
+import VitePluginCertificate from 'vite-plugin-mkcert'
+//import vueSetupExtend from 'vite-plugin-vue-setup-extend';
+import { configHtmlPlugin } from './html'
+import { configPwaConfig } from './pwa'
+import { configMockPlugin } from './mock'
+import { configCompressPlugin } from './compress'
+import { configStyleImportPlugin } from './styleImport'
+import { configVisualizerConfig } from './visualizer'
+import { configThemePlugin } from './theme'
+import { configImageminPlugin } from './imagemin'
+import { configSvgIconsPlugin } from './svgSprite'
+
+export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
+ const {
+ VITE_USE_IMAGEMIN,
+ VITE_USE_MOCK,
+ VITE_LEGACY,
+ VITE_BUILD_COMPRESS,
+ VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE,
+ } = viteEnv
+
+ const vitePlugins: (PluginOption | PluginOption[])[] = [
+ // have to
+ vue(),
+ // have to
+ vueJsx(),
+ // support name
+ //vueSetupExtend(),
+ VitePluginCertificate({
+ source: 'coding',
+ }),
+ ]
+
+ // vite-plugin-windicss
+ vitePlugins.push(windiCSS())
+
+ // @vitejs/plugin-legacy
+ VITE_LEGACY && isBuild && vitePlugins.push(legacy())
+
+ // vite-plugin-html
+ vitePlugins.push(configHtmlPlugin(viteEnv, isBuild))
+
+ // vite-plugin-svg-icons
+ vitePlugins.push(configSvgIconsPlugin(isBuild))
+
+ // vite-plugin-mock
+ VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild))
+
+ // vite-plugin-purge-icons
+ vitePlugins.push(purgeIcons())
+
+ // vite-plugin-style-import
+ vitePlugins.push(configStyleImportPlugin(isBuild))
+
+ // rollup-plugin-visualizer
+ vitePlugins.push(configVisualizerConfig())
+
+ // vite-plugin-theme
+ vitePlugins.push(configThemePlugin(isBuild))
+
+ // The following plugins only work in the production environment
+ if (isBuild) {
+ // vite-plugin-imagemin
+ VITE_USE_IMAGEMIN && vitePlugins.push(configImageminPlugin())
+
+ // rollup-plugin-gzip
+ vitePlugins.push(
+ configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE),
+ )
+
+ // vite-plugin-pwa
+ vitePlugins.push(configPwaConfig(viteEnv))
+ }
+
+ return vitePlugins
+}
diff --git a/frontend/vben/build/vite/plugin/mock.ts b/frontend/vben/build/vite/plugin/mock.ts
new file mode 100644
index 0000000..d241e26
--- /dev/null
+++ b/frontend/vben/build/vite/plugin/mock.ts
@@ -0,0 +1,19 @@
+/**
+ * Mock plugin for development and production.
+ * https://github.com/anncwb/vite-plugin-mock
+ */
+import { viteMockServe } from 'vite-plugin-mock';
+
+export function configMockPlugin(isBuild: boolean) {
+ return viteMockServe({
+ ignore: /^\_/,
+ mockPath: 'mock',
+ localEnabled: !isBuild,
+ prodEnabled: isBuild,
+ injectCode: `
+ import { setupProdMockServer } from '../mock/_createProductionServer';
+
+ setupProdMockServer();
+ `,
+ });
+}
diff --git a/frontend/vben/build/vite/plugin/pwa.ts b/frontend/vben/build/vite/plugin/pwa.ts
new file mode 100644
index 0000000..90ef5bc
--- /dev/null
+++ b/frontend/vben/build/vite/plugin/pwa.ts
@@ -0,0 +1,33 @@
+/**
+ * Zero-config PWA for Vite
+ * https://github.com/antfu/vite-plugin-pwa
+ */
+import { VitePWA } from 'vite-plugin-pwa';
+
+export function configPwaConfig(env: ViteEnv) {
+ const { VITE_USE_PWA, VITE_GLOB_APP_TITLE, VITE_GLOB_APP_SHORT_NAME } = env;
+
+ if (VITE_USE_PWA) {
+ // vite-plugin-pwa
+ const pwaPlugin = VitePWA({
+ manifest: {
+ name: VITE_GLOB_APP_TITLE,
+ short_name: VITE_GLOB_APP_SHORT_NAME,
+ icons: [
+ {
+ src: './resource/img/pwa-192x192.png',
+ sizes: '192x192',
+ type: 'image/png',
+ },
+ {
+ src: './resource/img/pwa-512x512.png',
+ sizes: '512x512',
+ type: 'image/png',
+ },
+ ],
+ },
+ });
+ return pwaPlugin;
+ }
+ return [];
+}
diff --git a/frontend/vben/build/vite/plugin/styleImport.ts b/frontend/vben/build/vite/plugin/styleImport.ts
new file mode 100644
index 0000000..60d5684
--- /dev/null
+++ b/frontend/vben/build/vite/plugin/styleImport.ts
@@ -0,0 +1,81 @@
+/**
+ * Introduces component library styles on demand.
+ * https://github.com/anncwb/vite-plugin-style-import
+ */
+import { createStyleImportPlugin } from 'vite-plugin-style-import'
+
+export function configStyleImportPlugin(_isBuild: boolean) {
+ if (!_isBuild) {
+ return []
+ }
+ const styleImportPlugin = createStyleImportPlugin({
+ libs: [
+ {
+ libraryName: 'ant-design-vue',
+ esModule: true,
+ resolveStyle: (name) => {
+ // 这里是无需额外引入样式文件的“子组件”列表
+ const ignoreList = [
+ 'anchor-link',
+ 'sub-menu',
+ 'menu-item',
+ 'menu-divider',
+ 'menu-item-group',
+ 'breadcrumb-item',
+ 'breadcrumb-separator',
+ 'form-item',
+ 'step',
+ 'select-option',
+ 'select-opt-group',
+ 'card-grid',
+ 'card-meta',
+ 'collapse-panel',
+ 'descriptions-item',
+ 'list-item',
+ 'list-item-meta',
+ 'table-column',
+ 'table-column-group',
+ 'tab-pane',
+ 'tab-content',
+ 'timeline-item',
+ 'tree-node',
+ 'skeleton-input',
+ 'skeleton-avatar',
+ 'skeleton-title',
+ 'skeleton-paragraph',
+ 'skeleton-image',
+ 'skeleton-button',
+ ]
+ // 这里是需要额外引入样式的子组件列表
+ // 单独引入子组件时需引入组件样式,否则会在打包后导致子组件样式丢失
+ const replaceList = {
+ 'typography-text': 'typography',
+ 'typography-title': 'typography',
+ 'typography-paragraph': 'typography',
+ 'typography-link': 'typography',
+ 'dropdown-button': 'dropdown',
+ 'input-password': 'input',
+ 'input-search': 'input',
+ 'input-group': 'input',
+ 'radio-group': 'radio',
+ 'checkbox-group': 'checkbox',
+ 'layout-sider': 'layout',
+ 'layout-content': 'layout',
+ 'layout-footer': 'layout',
+ 'layout-header': 'layout',
+ 'month-picker': 'date-picker',
+ 'range-picker': 'date-picker',
+ 'image-preview-group': 'image',
+ }
+
+ return ignoreList.includes(name)
+ ? ''
+ : replaceList.hasOwnProperty(name)
+ ? `ant-design-vue/es/${replaceList[name]}/style/index`
+ : `ant-design-vue/es/${name}/style/index`
+ },
+ },
+ ],
+ })
+ return styleImportPlugin
+}
diff --git a/frontend/vben/build/vite/plugin/svgSprite.ts b/frontend/vben/build/vite/plugin/svgSprite.ts
new file mode 100644
index 0000000..61f637f
--- /dev/null
+++ b/frontend/vben/build/vite/plugin/svgSprite.ts
@@ -0,0 +1,17 @@
+/**
+ * Vite Plugin for fast creating SVG sprites.
+ * https://github.com/anncwb/vite-plugin-svg-icons
+ */
+
+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
+import path from 'path';
+
+export function configSvgIconsPlugin(isBuild: boolean) {
+ const svgIconsPlugin = createSvgIconsPlugin({
+ iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
+ svgoOptions: isBuild,
+ // default
+ symbolId: 'icon-[dir]-[name]',
+ });
+ return svgIconsPlugin;
+}
diff --git a/frontend/vben/build/vite/plugin/theme.ts b/frontend/vben/build/vite/plugin/theme.ts
new file mode 100644
index 0000000..118983f
--- /dev/null
+++ b/frontend/vben/build/vite/plugin/theme.ts
@@ -0,0 +1,89 @@
+/**
+ * Vite plugin for website theme color switching
+ * https://github.com/anncwb/vite-plugin-theme
+ */
+import type { PluginOption } from 'vite';
+import path from 'path';
+import {
+ viteThemePlugin,
+ antdDarkThemePlugin,
+ mixLighten,
+ mixDarken,
+ tinycolor,
+} from 'vite-plugin-theme';
+import { getThemeColors, generateColors } from '../../config/themeConfig';
+import { generateModifyVars } from '../../generate/generateModifyVars';
+
+export function configThemePlugin(isBuild: boolean): PluginOption[] {
+ const colors = generateColors({
+ mixDarken,
+ mixLighten,
+ tinycolor,
+ });
+ const plugin = [
+ viteThemePlugin({
+ resolveSelector: (s) => {
+ s = s.trim();
+ switch (s) {
+ case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
+ return '.ant-steps-item-icon > .ant-steps-icon';
+ case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)':
+ case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover':
+ case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active':
+ return s;
+ case '.ant-steps-item-icon > .ant-steps-icon':
+ return s;
+ case '.ant-select-item-option-selected:not(.ant-select-item-option-disabled)':
+ return s;
+ default:
+ if (s.indexOf('.ant-btn') >= -1) {
+ // 按钮被重新定制过,需要过滤掉class防止覆盖
+ return s;
+ }
+ }
+ return s.startsWith('[data-theme') ? s : `[data-theme] ${s}`;
+ },
+ colorVariables: [...getThemeColors(), ...colors],
+ }),
+ antdDarkThemePlugin({
+ preloadFiles: [
+ path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.less'),
+ //path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.dark.less'),
+ path.resolve(process.cwd(), 'src/design/index.less'),
+ ],
+ filter: (id) => (isBuild ? !id.endsWith('antd.less') : true),
+ // extractCss: false,
+ darkModifyVars: {
+ ...generateModifyVars(true),
+ 'text-color': '#c9d1d9',
+ 'primary-1': 'rgb(255 255 255 / 8%)',
+ 'text-color-base': '#c9d1d9',
+ 'component-background': '#151515',
+ 'heading-color': 'rgb(255 255 255 / 65%)',
+ // black: '#0e1117',
+ // #8b949e
+ 'text-color-secondary': '#8b949e',
+ 'border-color-base': '#303030',
+ // 'border-color-split': '#30363d',
+ 'item-active-bg': '#111b26',
+ 'app-content-background': '#1e1e1e',
+ 'tree-node-selected-bg': '#11263c',
+
+ 'alert-success-border-color': '#274916',
+ 'alert-success-bg-color': '#162312',
+ 'alert-success-icon-color': '#49aa19',
+ 'alert-info-border-color': '#153450',
+ 'alert-info-bg-color': '#111b26',
+ 'alert-info-icon-color': '#177ddc',
+ 'alert-warning-border-color': '#594214',
+ 'alert-warning-bg-color': '#2b2111',
+ 'alert-warning-icon-color': '#d89614',
+ 'alert-error-border-color': '#58181c',
+ 'alert-error-bg-color': '#2a1215',
+ 'alert-error-icon-color': '#a61d24',
+ },
+ }),
+ ];
+
+ return plugin as unknown as PluginOption[];
+}
diff --git a/frontend/vben/build/vite/plugin/visualizer.ts b/frontend/vben/build/vite/plugin/visualizer.ts
new file mode 100644
index 0000000..75d4451
--- /dev/null
+++ b/frontend/vben/build/vite/plugin/visualizer.ts
@@ -0,0 +1,17 @@
+/**
+ * Package file volume analysis
+ */
+import visualizer from 'rollup-plugin-visualizer';
+import { isReportMode } from '../../utils';
+
+export function configVisualizerConfig() {
+ if (isReportMode()) {
+ return visualizer({
+ filename: './node_modules/.cache/visualizer/stats.html',
+ open: true,
+ gzipSize: true,
+ brotliSize: true,
+ }) as Plugin;
+ }
+ return [];
+}
diff --git a/frontend/vben/build/vite/proxy.ts b/frontend/vben/build/vite/proxy.ts
new file mode 100644
index 0000000..8525397
--- /dev/null
+++ b/frontend/vben/build/vite/proxy.ts
@@ -0,0 +1,34 @@
+/**
+ * Used to parse the .env.development proxy configuration
+ */
+import type { ProxyOptions } from 'vite';
+
+type ProxyItem = [string, string];
+
+type ProxyList = ProxyItem[];
+
+type ProxyTargetList = Record;
+
+const httpsRE = /^https:\/\//;
+
+/**
+ * Generate proxy
+ * @param list
+ */
+export function createProxy(list: ProxyList = []) {
+ const ret: ProxyTargetList = {};
+ for (const [prefix, target] of list) {
+ const isHttps = httpsRE.test(target);
+
+ // https://github.com/http-party/node-http-proxy#options
+ ret[prefix] = {
+ target: target,
+ changeOrigin: true,
+ ws: true,
+ rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''),
+ // https is require secure=false
+ ...(isHttps ? { secure: false } : {}),
+ };
+ }
+ return ret;
+}
diff --git a/frontend/vben/commitlint.config.js b/frontend/vben/commitlint.config.js
new file mode 100644
index 0000000..dbe4b09
--- /dev/null
+++ b/frontend/vben/commitlint.config.js
@@ -0,0 +1,107 @@
+const fs = require('fs')
+const path = require('path')
+const { execSync } = require('child_process')
+
+const scopes = fs
+ .readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
+ .filter((dirent) => dirent.isDirectory())
+ .map((dirent) => dirent.name.replace(/s$/, ''))
+
+// precomputed scope
+const scopeComplete = execSync('git status --porcelain || true')
+ .toString()
+ .trim()
+ .split('\n')
+ .find((r) => ~r.indexOf('M src'))
+ ?.replace(/(\/)/g, '%%')
+ ?.match(/src%%((\w|-)*)/)?.[1]
+ ?.replace(/s$/, '')
+
+/** @type {import('cz-git').UserConfig} */
+module.exports = {
+ ignores: [(commit) => commit.includes('init')],
+ extends: ['@commitlint/config-conventional'],
+ rules: {
+ 'body-leading-blank': [2, 'always'],
+ 'footer-leading-blank': [1, 'always'],
+ 'header-max-length': [2, 'always', 108],
+ 'subject-empty': [2, 'never'],
+ 'type-empty': [2, 'never'],
+ 'subject-case': [0],
+ 'type-enum': [
+ 2,
+ 'always',
+ [
+ 'feat',
+ 'fix',
+ 'perf',
+ 'style',
+ 'docs',
+ 'test',
+ 'refactor',
+ 'build',
+ 'ci',
+ 'chore',
+ 'revert',
+ 'wip',
+ 'workflow',
+ 'types',
+ 'release',
+ ],
+ ],
+ },
+ prompt: {
+ /** @use `yarn commit :f` */
+ alias: {
+ f: 'docs: fix typos',
+ r: 'docs: update README',
+ s: 'style: update code format',
+ b: 'build: bump dependencies',
+ c: 'chore: update config',
+ },
+ customScopesAlign: !scopeComplete ? 'top' : 'bottom',
+ defaultScope: scopeComplete,
+ scopes: [...scopes, 'mock'],
+ allowEmptyIssuePrefixs: false,
+ allowCustomIssuePrefixs: false,
+
+ // English
+ typesAppend: [
+ { value: 'wip', name: 'wip: work in process' },
+ { value: 'workflow', name: 'workflow: workflow improvements' },
+ { value: 'types', name: 'types: type definition file changes' },
+ ],
+
+ // 中英文对照版
+ // messages: {
+ // type: '选择你要提交的类型 :',
+ // scope: '选择一个提交范围 (可选):',
+ // customScope: '请输入自定义的提交范围 :',
+ // subject: '填写简短精炼的变更描述 :\n',
+ // body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
+ // breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
+ // footerPrefixsSelect: '选择关联issue前缀 (可选):',
+ // customFooterPrefixs: '输入自定义issue前缀 :',
+ // footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
+ // confirmCommit: '是否提交或修改commit ?',
+ // },
+ // types: [
+ // { value: 'feat', name: 'feat: 新增功能' },
+ // { value: 'fix', name: 'fix: 修复缺陷' },
+ // { value: 'docs', name: 'docs: 文档变更' },
+ // { value: 'style', name: 'style: 代码格式' },
+ // { value: 'refactor', name: 'refactor: 代码重构' },
+ // { value: 'perf', name: 'perf: 性能优化' },
+ // { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' },
+ // { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
+ // { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
+ // { value: 'revert', name: 'revert: 回滚 commit' },
+ // { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
+ // { value: 'wip', name: 'wip: 正在开发中' },
+ // { value: 'workflow', name: 'workflow: 工作流程改进' },
+ // { value: 'types', name: 'types: 类型定义文件修改' },
+ // ],
+ // emptyScopesAlias: 'empty: 不填写',
+ // customScopesAlias: 'custom: 自定义',
+ },
+}
diff --git a/frontend/vben/index.html b/frontend/vben/index.html
new file mode 100644
index 0000000..3f1aa76
--- /dev/null
+++ b/frontend/vben/index.html
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+ <%= title %>
+
+
+
+
+
+
+
+
+

+
+
+
+
<%= title %>
+
+
+
+
+
+
diff --git a/frontend/vben/mock/_createProductionServer.ts b/frontend/vben/mock/_createProductionServer.ts
new file mode 100644
index 0000000..a44310b
--- /dev/null
+++ b/frontend/vben/mock/_createProductionServer.ts
@@ -0,0 +1,18 @@
+import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
+
+const modules = import.meta.globEager('./**/*.ts');
+
+const mockModules: any[] = [];
+Object.keys(modules).forEach((key) => {
+ if (key.includes('/_')) {
+ return;
+ }
+ mockModules.push(...modules[key].default);
+});
+
+/**
+ * Used in a production environment. Need to manually import all modules
+ */
+export function setupProdMockServer() {
+ createProdMockServer(mockModules);
+}
diff --git a/frontend/vben/mock/_util.ts b/frontend/vben/mock/_util.ts
new file mode 100644
index 0000000..de4d558
--- /dev/null
+++ b/frontend/vben/mock/_util.ts
@@ -0,0 +1,62 @@
+// Interface data format used to return a unified format
+import { ResultEnum } from '/@/enums/httpEnum';
+
+export function resultSuccess(result: T, { message = 'ok' } = {}) {
+ return {
+ code: ResultEnum.SUCCESS,
+ result,
+ message,
+ type: 'success',
+ };
+}
+
+export function resultPageSuccess(
+ page: number,
+ pageSize: number,
+ list: T[],
+ { message = 'ok' } = {},
+) {
+ const pageData = pagination(page, pageSize, list);
+
+ return {
+ ...resultSuccess({
+ items: pageData,
+ total: list.length,
+ }),
+ message,
+ };
+}
+
+export function resultError(
+ message = 'Request failed',
+ { code = ResultEnum.ERROR, result = null } = {},
+) {
+ return {
+ code,
+ result,
+ message,
+ type: 'error',
+ };
+}
+
+export function pagination(pageNo: number, pageSize: number, array: T[]): T[] {
+ const offset = (pageNo - 1) * Number(pageSize);
+ return offset + Number(pageSize) >= array.length
+ ? array.slice(offset, array.length)
+ : array.slice(offset, offset + Number(pageSize));
+}
+
+export interface requestParams {
+ method: string;
+ body: any;
+ headers?: { authorization?: string };
+ query: any;
+}
+
+/**
+ * @description 本函数用于从request数据中获取token,请根据项目的实际情况修改
+ *
+ */
+export function getRequestToken({ headers }: requestParams): string | undefined {
+ return headers?.authorization;
+}
diff --git a/frontend/vben/mock/demo/account.ts b/frontend/vben/mock/demo/account.ts
new file mode 100644
index 0000000..a392493
--- /dev/null
+++ b/frontend/vben/mock/demo/account.ts
@@ -0,0 +1,71 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultSuccess, resultError } from '../_util';
+import { ResultEnum } from '../../src/enums/httpEnum';
+
+const userInfo = {
+ name: 'Vben',
+ userid: '00000001',
+ email: 'test@gmail.com',
+ signature: '海纳百川,有容乃大',
+ introduction: '微笑着,努力着,欣赏着',
+ title: '交互专家',
+ group: '某某某事业群-某某平台部-某某技术部-UED',
+ tags: [
+ {
+ key: '0',
+ label: '很有想法的',
+ },
+ {
+ key: '1',
+ label: '专注设计',
+ },
+ {
+ key: '2',
+ label: '辣~',
+ },
+ {
+ key: '3',
+ label: '大长腿',
+ },
+ {
+ key: '4',
+ label: '川妹子',
+ },
+ {
+ key: '5',
+ label: '海纳百川',
+ },
+ ],
+ notifyCount: 12,
+ unreadCount: 11,
+ country: 'China',
+ address: 'Xiamen City 77',
+ phone: '0592-268888888',
+};
+
+export default [
+ {
+ url: '/basic-api/account/getAccountInfo',
+ timeout: 1000,
+ method: 'get',
+ response: () => {
+ return resultSuccess(userInfo);
+ },
+ },
+ {
+ url: '/basic-api/user/sessionTimeout',
+ method: 'post',
+ statusCode: 401,
+ response: () => {
+ return resultError();
+ },
+ },
+ {
+ url: '/basic-api/user/tokenExpired',
+ method: 'post',
+ statusCode: 200,
+ response: () => {
+ return resultError('Token Expired!', { code: ResultEnum.TIMEOUT as number });
+ },
+ },
+] as MockMethod[];
diff --git a/frontend/vben/mock/demo/api-cascader.ts b/frontend/vben/mock/demo/api-cascader.ts
new file mode 100644
index 0000000..6334ef5
--- /dev/null
+++ b/frontend/vben/mock/demo/api-cascader.ts
@@ -0,0 +1,325 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultSuccess } from '../_util';
+
+const areaList: any[] = [
+ {
+ id: '530825900854620160',
+ code: '430000',
+ parentCode: '100000',
+ levelType: 1,
+ name: '湖南省',
+ province: '湖南省',
+ city: null,
+ district: null,
+ town: null,
+ village: null,
+ parentPath: '430000',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 16:33:42',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530825900883980288',
+ code: '430100',
+ parentCode: '430000',
+ levelType: 2,
+ name: '长沙市',
+ province: '湖南省',
+ city: '长沙市',
+ district: null,
+ town: null,
+ village: null,
+ parentPath: '430000,430100',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 16:33:42',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530825900951089152',
+ code: '430102',
+ parentCode: '430100',
+ levelType: 3,
+ name: '芙蓉区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '芙蓉区',
+ town: null,
+ village: null,
+ parentPath: '430000,430100,430102',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 16:33:42',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530825901014003712',
+ code: '430104',
+ parentCode: '430100',
+ levelType: 3,
+ name: '岳麓区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '岳麓区',
+ town: null,
+ village: null,
+ parentPath: '430000,430100,430104',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 16:33:42',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530825900988837888',
+ code: '430103',
+ parentCode: '430100',
+ levelType: 3,
+ name: '天心区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: null,
+ village: null,
+ parentPath: '430000,430100,430103',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 16:33:42',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530826672489115648',
+ code: '430103002',
+ parentCode: '430103',
+ levelType: 4,
+ name: '坡子街街道',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: null,
+ parentPath: '430000,430100,430103,430103002',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-12-14 15:26:43',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241171607552',
+ code: '430103002001',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '八角亭社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '八角亭社区',
+ parentPath: '430000,430100,430103,430103002,430103002001',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2021-01-20 14:07:23',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241200967680',
+ code: '430103002002',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '西牌楼社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '西牌楼社区',
+ parentPath: '430000,430100,430103,430103002,430103002002',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241230327808',
+ code: '430103002003',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '太平街社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '太平街社区',
+ parentPath: '430000,430100,430103,430103002,430103002003',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241259687936',
+ code: '430103002005',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '坡子街社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '坡子街社区',
+ parentPath: '430000,430100,430103,430103002,430103002005',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241284853760',
+ code: '430103002006',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '青山祠社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '青山祠社区',
+ parentPath: '430000,430100,430103,430103002,430103002006',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241310019584',
+ code: '430103002007',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '沙河社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '沙河社区',
+ parentPath: '430000,430100,430103,430103002,430103002007',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241381322752',
+ code: '430103002008',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '碧湘社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '碧湘社区',
+ parentPath: '430000,430100,430103,430103002,430103002008',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241410682880',
+ code: '430103002009',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '创远社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '创远社区',
+ parentPath: '430000,430100,430103,430103002,430103002009',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241431654400',
+ code: '430103002010',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '楚湘社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '楚湘社区',
+ parentPath: '430000,430100,430103,430103002,430103002010',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241465208832',
+ code: '430103002011',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '西湖社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '西湖社区',
+ parentPath: '430000,430100,430103,430103002,430103002011',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241502957568',
+ code: '430103002012',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '登仁桥社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '登仁桥社区',
+ parentPath: '430000,430100,430103,430103002,430103002012',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241553289216',
+ code: '430103002013',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '文庙坪社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '文庙坪社区',
+ parentPath: '430000,430100,430103,430103002,430103002013',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+];
+export default [
+ {
+ url: '/basic-api/cascader/getAreaRecord',
+ timeout: 1000,
+ method: 'post',
+ response: ({ body }) => {
+ const { parentCode } = body || {};
+ if (!parentCode) {
+ return resultSuccess(areaList.filter((it) => it.code === '430000'));
+ }
+ return resultSuccess(areaList.filter((it) => it.parentCode === parentCode));
+ },
+ },
+] as MockMethod[];
diff --git a/frontend/vben/mock/demo/select-demo.ts b/frontend/vben/mock/demo/select-demo.ts
new file mode 100644
index 0000000..631c6bb
--- /dev/null
+++ b/frontend/vben/mock/demo/select-demo.ts
@@ -0,0 +1,28 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultSuccess } from '../_util';
+
+const demoList = (keyword, count = 20) => {
+ const result = {
+ list: [] as any[],
+ };
+ for (let index = 0; index < count; index++) {
+ result.list.push({
+ name: `${keyword ?? ''}选项${index}`,
+ id: `${index}`,
+ });
+ }
+ return result;
+};
+
+export default [
+ {
+ url: '/basic-api/select/getDemoOptions',
+ timeout: 1000,
+ method: 'get',
+ response: ({ query }) => {
+ const { keyword, count } = query;
+ console.log(keyword);
+ return resultSuccess(demoList(keyword, count));
+ },
+ },
+] as MockMethod[];
diff --git a/frontend/vben/mock/demo/system.ts b/frontend/vben/mock/demo/system.ts
new file mode 100644
index 0000000..c417727
--- /dev/null
+++ b/frontend/vben/mock/demo/system.ts
@@ -0,0 +1,202 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultError, resultPageSuccess, resultSuccess } from '../_util';
+
+const accountList = (() => {
+ const result: any[] = [];
+ for (let index = 0; index < 20; index++) {
+ result.push({
+ id: `${index}`,
+ account: '@first',
+ email: '@email',
+ nickname: '@cname()',
+ role: '@first',
+ createTime: '@datetime',
+ remark: '@cword(10,20)',
+ 'status|1': ['0', '1'],
+ });
+ }
+ return result;
+})();
+
+const roleList = (() => {
+ const result: any[] = [];
+ for (let index = 0; index < 4; index++) {
+ result.push({
+ id: index + 1,
+ orderNo: `${index + 1}`,
+ roleName: ['超级管理员', '管理员', '文章管理员', '普通用户'][index],
+ roleValue: '@first',
+ createTime: '@datetime',
+ remark: '@cword(10,20)',
+ menu: [['0', '1', '2'], ['0', '1'], ['0', '2'], ['2']][index],
+ 'status|1': ['0', '1'],
+ });
+ }
+ return result;
+})();
+
+const deptList = (() => {
+ const result: any[] = [];
+ for (let index = 0; index < 3; index++) {
+ result.push({
+ id: `${index}`,
+ deptName: ['华东分部', '华南分部', '西北分部'][index],
+ orderNo: index + 1,
+ createTime: '@datetime',
+ remark: '@cword(10,20)',
+ 'status|1': ['0', '0', '1'],
+ children: (() => {
+ const children: any[] = [];
+ for (let j = 0; j < 4; j++) {
+ children.push({
+ id: `${index}-${j}`,
+ deptName: ['研发部', '市场部', '商务部', '财务部'][j],
+ orderNo: j + 1,
+ createTime: '@datetime',
+ remark: '@cword(10,20)',
+ 'status|1': ['0', '1'],
+ parentDept: `${index}`,
+ children: undefined,
+ });
+ }
+ return children;
+ })(),
+ });
+ }
+ return result;
+})();
+
+const menuList = (() => {
+ const result: any[] = [];
+ for (let index = 0; index < 3; index++) {
+ result.push({
+ id: `${index}`,
+ icon: ['ion:layers-outline', 'ion:git-compare-outline', 'ion:tv-outline'][index],
+ component: 'LAYOUT',
+ type: '0',
+ menuName: ['Dashboard', '权限管理', '功能'][index],
+ permission: '',
+ orderNo: index + 1,
+ createTime: '@datetime',
+ 'status|1': ['0', '0', '1'],
+ children: (() => {
+ const children: any[] = [];
+ for (let j = 0; j < 4; j++) {
+ children.push({
+ id: `${index}-${j}`,
+ type: '1',
+ menuName: ['菜单1', '菜单2', '菜单3', '菜单4'][j],
+ icon: 'ion:document',
+ permission: ['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index],
+ component: [
+ '/dashboard/welcome/index',
+ '/dashboard/analysis/index',
+ '/dashboard/workbench/index',
+ '/dashboard/test/index',
+ ][j],
+ orderNo: j + 1,
+ createTime: '@datetime',
+ 'status|1': ['0', '1'],
+ parentMenu: `${index}`,
+ children: (() => {
+ const children: any[] = [];
+ for (let k = 0; k < 4; k++) {
+ children.push({
+ id: `${index}-${j}-${k}`,
+ type: '2',
+ menuName: '按钮' + (j + 1) + '-' + (k + 1),
+ icon: '',
+ permission:
+ ['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index] +
+ ':btn' +
+ (k + 1),
+ component: [
+ '/dashboard/welcome/index',
+ '/dashboard/analysis/index',
+ '/dashboard/workbench/index',
+ '/dashboard/test/index',
+ ][j],
+ orderNo: j + 1,
+ createTime: '@datetime',
+ 'status|1': ['0', '1'],
+ parentMenu: `${index}-${j}`,
+ children: undefined,
+ });
+ }
+ return children;
+ })(),
+ });
+ }
+ return children;
+ })(),
+ });
+ }
+ return result;
+})();
+
+export default [
+ {
+ url: '/basic-api/system/getAccountList',
+ timeout: 100,
+ method: 'get',
+ response: ({ query }) => {
+ const { page = 1, pageSize = 20 } = query;
+ return resultPageSuccess(page, pageSize, accountList);
+ },
+ },
+ {
+ url: '/basic-api/system/getRoleListByPage',
+ timeout: 100,
+ method: 'get',
+ response: ({ query }) => {
+ const { page = 1, pageSize = 20 } = query;
+ return resultPageSuccess(page, pageSize, roleList);
+ },
+ },
+ {
+ url: '/basic-api/system/setRoleStatus',
+ timeout: 500,
+ method: 'post',
+ response: ({ query }) => {
+ const { id, status } = query;
+ return resultSuccess({ id, status });
+ },
+ },
+ {
+ url: '/basic-api/system/getAllRoleList',
+ timeout: 100,
+ method: 'get',
+ response: () => {
+ return resultSuccess(roleList);
+ },
+ },
+ {
+ url: '/basic-api/system/getDeptList',
+ timeout: 100,
+ method: 'get',
+ response: () => {
+ return resultSuccess(deptList);
+ },
+ },
+ {
+ url: '/basic-api/system/getMenuList',
+ timeout: 100,
+ method: 'get',
+ response: () => {
+ return resultSuccess(menuList);
+ },
+ },
+ {
+ url: '/basic-api/system/accountExist',
+ timeout: 500,
+ method: 'post',
+ response: ({ body }) => {
+ const { account } = body || {};
+ if (account && account.indexOf('admin') !== -1) {
+ return resultError('该字段不能包含admin');
+ } else {
+ return resultSuccess(`${account} can use`);
+ }
+ },
+ },
+] as MockMethod[];
diff --git a/frontend/vben/mock/demo/table-demo.ts b/frontend/vben/mock/demo/table-demo.ts
new file mode 100644
index 0000000..f3a0f16
--- /dev/null
+++ b/frontend/vben/mock/demo/table-demo.ts
@@ -0,0 +1,52 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { Random } from 'mockjs';
+import { resultPageSuccess } from '../_util';
+
+function getRandomPics(count = 10): string[] {
+ const arr: string[] = [];
+ for (let i = 0; i < count; i++) {
+ arr.push(Random.image('800x600', Random.color(), Random.color(), Random.title()));
+ }
+ return arr;
+}
+
+const demoList = (() => {
+ const result: any[] = [];
+ for (let index = 0; index < 200; index++) {
+ result.push({
+ id: `${index}`,
+ beginTime: '@datetime',
+ endTime: '@datetime',
+ address: '@city()',
+ name: '@cname()',
+ name1: '@cname()',
+ name2: '@cname()',
+ name3: '@cname()',
+ name4: '@cname()',
+ name5: '@cname()',
+ name6: '@cname()',
+ name7: '@cname()',
+ name8: '@cname()',
+ avatar: Random.image('400x400', Random.color(), Random.color(), Random.first()),
+ imgArr: getRandomPics(Math.ceil(Math.random() * 3) + 1),
+ imgs: getRandomPics(Math.ceil(Math.random() * 3) + 1),
+ date: `@date('yyyy-MM-dd')`,
+ time: `@time('HH:mm')`,
+ 'no|100000-10000000': 100000,
+ 'status|1': ['normal', 'enable', 'disable'],
+ });
+ }
+ return result;
+})();
+
+export default [
+ {
+ url: '/basic-api/table/getDemoList',
+ timeout: 100,
+ method: 'get',
+ response: ({ query }) => {
+ const { page = 1, pageSize = 20 } = query;
+ return resultPageSuccess(page, pageSize, demoList);
+ },
+ },
+] as MockMethod[];
diff --git a/frontend/vben/mock/demo/tree-demo.ts b/frontend/vben/mock/demo/tree-demo.ts
new file mode 100644
index 0000000..6fdcb85
--- /dev/null
+++ b/frontend/vben/mock/demo/tree-demo.ts
@@ -0,0 +1,38 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultSuccess } from '../_util';
+
+const demoTreeList = (keyword) => {
+ const result = {
+ list: [] as Recordable[],
+ };
+ for (let index = 0; index < 5; index++) {
+ const children: Recordable[] = [];
+ for (let j = 0; j < 3; j++) {
+ children.push({
+ title: `${keyword ?? ''}选项${index}-${j}`,
+ value: `${index}-${j}`,
+ key: `${index}-${j}`,
+ });
+ }
+ result.list.push({
+ title: `${keyword ?? ''}选项${index}`,
+ value: `${index}`,
+ key: `${index}`,
+ children,
+ });
+ }
+ return result;
+};
+
+export default [
+ {
+ url: '/basic-api/tree/getDemoOptions',
+ timeout: 1000,
+ method: 'get',
+ response: ({ query }) => {
+ const { keyword } = query;
+ console.log(keyword);
+ return resultSuccess(demoTreeList(keyword));
+ },
+ },
+] as MockMethod[];
diff --git a/frontend/vben/mock/sys/menu.ts b/frontend/vben/mock/sys/menu.ts
new file mode 100644
index 0000000..1df2a22
--- /dev/null
+++ b/frontend/vben/mock/sys/menu.ts
@@ -0,0 +1,260 @@
+import { resultSuccess, resultError, getRequestToken, requestParams } from '../_util'
+import { MockMethod } from 'vite-plugin-mock'
+import { createFakeUserList } from './user'
+
+// single
+const dashboardRoute = {
+ path: '/dashboard',
+ name: 'Dashboard',
+ component: 'LAYOUT',
+ redirect: '/dashboard/analysis',
+ meta: {
+ title: 'routes.dashboard.dashboard',
+ hideChildrenInMenu: true,
+ icon: 'bx:bx-home',
+ },
+ children: [
+ {
+ path: 'analysis',
+ name: 'Analysis',
+ component: '/dashboard/analysis/index',
+ meta: {
+ hideMenu: true,
+ hideBreadcrumb: true,
+ title: 'routes.dashboard.analysis',
+ currentActiveMenu: '/dashboard',
+ icon: 'bx:bx-home',
+ },
+ },
+ {
+ path: 'workbench',
+ name: 'Workbench',
+ component: '/dashboard/workbench/index',
+ meta: {
+ hideMenu: true,
+ hideBreadcrumb: true,
+ title: 'routes.dashboard.workbench',
+ currentActiveMenu: '/dashboard',
+ icon: 'bx:bx-home',
+ },
+ },
+ ],
+}
+
+const backRoute = {
+ path: 'back',
+ name: 'PermissionBackDemo',
+ meta: {
+ title: 'routes.demo.permission.back',
+ },
+
+ children: [
+ {
+ path: 'page',
+ name: 'BackAuthPage',
+ component: '/demo/permission/back/index',
+ meta: {
+ title: 'routes.demo.permission.backPage',
+ },
+ },
+ {
+ path: 'btn',
+ name: 'BackAuthBtn',
+ component: '/demo/permission/back/Btn',
+ meta: {
+ title: 'routes.demo.permission.backBtn',
+ },
+ },
+ ],
+}
+
+const authRoute = {
+ path: '/permission',
+ name: 'Permission',
+ component: 'LAYOUT',
+ redirect: '/permission/front/page',
+ meta: {
+ icon: 'carbon:user-role',
+ title: 'routes.demo.permission.permission',
+ },
+ children: [backRoute],
+}
+
+const levelRoute = {
+ path: '/level',
+ name: 'Level',
+ component: 'LAYOUT',
+ redirect: '/level/menu1/menu1-1',
+ meta: {
+ icon: 'carbon:user-role',
+ title: 'routes.demo.level.level',
+ },
+
+ children: [
+ {
+ path: 'menu1',
+ name: 'Menu1Demo',
+ meta: {
+ title: 'Menu1',
+ },
+ children: [
+ {
+ path: 'menu1-1',
+ name: 'Menu11Demo',
+ meta: {
+ title: 'Menu1-1',
+ },
+ children: [
+ {
+ path: 'menu1-1-1',
+ name: 'Menu111Demo',
+ component: '/demo/level/Menu111',
+ meta: {
+ title: 'Menu111',
+ },
+ },
+ ],
+ },
+ {
+ path: 'menu1-2',
+ name: 'Menu12Demo',
+ component: '/demo/level/Menu12',
+ meta: {
+ title: 'Menu1-2',
+ },
+ },
+ ],
+ },
+ {
+ path: 'menu2',
+ name: 'Menu2Demo',
+ component: '/demo/level/Menu2',
+ meta: {
+ title: 'Menu2',
+ },
+ },
+ ],
+}
+
+const sysRoute = {
+ path: '/system',
+ name: 'System',
+ component: 'LAYOUT',
+ redirect: '/system/account',
+ meta: {
+ icon: 'ion:settings-outline',
+ title: 'routes.demo.system.moduleName',
+ },
+ children: [
+ {
+ path: 'account',
+ name: 'AccountManagement',
+ meta: {
+ title: 'routes.demo.system.account',
+ ignoreKeepAlive: true,
+ },
+ component: '/demo/system/account/index',
+ },
+ {
+ path: 'account_detail/:id',
+ name: 'AccountDetail',
+ meta: {
+ hideMenu: true,
+ title: 'routes.demo.system.account_detail',
+ ignoreKeepAlive: true,
+ showMenu: false,
+ currentActiveMenu: '/system/account',
+ },
+ component: '/demo/system/account/AccountDetail',
+ },
+ {
+ path: 'role',
+ name: 'RoleManagement',
+ meta: {
+ title: 'routes.demo.system.role',
+ ignoreKeepAlive: true,
+ },
+ component: '/demo/system/role/index',
+ },
+ {
+ path: 'dept',
+ name: 'DeptManagement',
+ meta: {
+ title: 'routes.demo.system.dept',
+ ignoreKeepAlive: true,
+ },
+ component: '/demo/system/dept/index',
+ },
+ {
+ path: 'changePassword',
+ name: 'ChangePassword',
+ meta: {
+ title: 'routes.demo.system.password',
+ ignoreKeepAlive: true,
+ },
+ component: '/demo/system/password/index',
+ },
+ ],
+}
+
+const linkRoute = {
+ path: '/link',
+ name: 'Link',
+ component: 'LAYOUT',
+ meta: {
+ icon: 'ion:tv-outline',
+ title: 'routes.demo.iframe.frame',
+ },
+ children: [
+ {
+ path: 'doc',
+ name: 'Doc',
+ meta: {
+ title: 'routes.demo.iframe.doc',
+ frameSrc: 'https://vvbin.cn/doc-next/',
+ },
+ },
+ {
+ path: 'https://vvbin.cn/doc-next/',
+ name: 'DocExternal',
+ component: 'LAYOUT',
+ meta: {
+ title: 'routes.demo.iframe.docExternal',
+ },
+ },
+ ],
+}
+
+export default [
+ {
+ url: '/basic-api/getMenuList',
+ timeout: 1000,
+ method: 'get',
+ response: (request: requestParams) => {
+ const token = getRequestToken(request)
+ if (!token) {
+ return resultError('Invalid token!')
+ }
+ const checkUser = createFakeUserList().find((item) => item.token === token)
+ if (!checkUser) {
+ return resultError('Invalid user token!')
+ }
+ const id = checkUser.userId
+ let menu: Object[]
+ switch (id) {
+ case '1':
+ dashboardRoute.redirect = dashboardRoute.path + '/' + dashboardRoute.children[0].path
+ menu = [dashboardRoute, authRoute, levelRoute, sysRoute, linkRoute]
+ break
+ case '2':
+ dashboardRoute.redirect = dashboardRoute.path + '/' + dashboardRoute.children[1].path
+ menu = [dashboardRoute, authRoute, levelRoute, linkRoute]
+ break
+ default:
+ menu = []
+ }
+
+ return resultSuccess(menu)
+ },
+ },
+] as MockMethod[]
diff --git a/frontend/vben/mock/sys/user.ts b/frontend/vben/mock/sys/user.ts
new file mode 100644
index 0000000..5b569d4
--- /dev/null
+++ b/frontend/vben/mock/sys/user.ts
@@ -0,0 +1,122 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultError, resultSuccess, getRequestToken, requestParams } from '../_util';
+
+export function createFakeUserList() {
+ return [
+ {
+ userId: '1',
+ username: 'vben',
+ realName: 'Vben Admin',
+ avatar: 'https://q1.qlogo.cn/g?b=qq&nk=190848757&s=640',
+ desc: 'manager',
+ password: '123456',
+ token: 'fakeToken1',
+ homePath: '/dashboard/analysis',
+ roles: [
+ {
+ roleName: 'Super Admin',
+ value: 'super',
+ },
+ ],
+ },
+ {
+ userId: '2',
+ username: 'test',
+ password: '123456',
+ realName: 'test user',
+ avatar: 'https://q1.qlogo.cn/g?b=qq&nk=339449197&s=640',
+ desc: 'tester',
+ token: 'fakeToken2',
+ homePath: '/dashboard/workbench',
+ roles: [
+ {
+ roleName: 'Tester',
+ value: 'test',
+ },
+ ],
+ },
+ ];
+}
+
+const fakeCodeList: any = {
+ '1': ['1000', '3000', '5000'],
+
+ '2': ['2000', '4000', '6000'],
+};
+export default [
+ // mock user login
+ {
+ url: '/basic-api/login',
+ timeout: 200,
+ method: 'post',
+ response: ({ body }) => {
+ const { username, password } = body;
+ const checkUser = createFakeUserList().find(
+ (item) => item.username === username && password === item.password,
+ );
+ if (!checkUser) {
+ return resultError('Incorrect account or password!');
+ }
+ const { userId, username: _username, token, realName, desc, roles } = checkUser;
+ return resultSuccess({
+ roles,
+ userId,
+ username: _username,
+ token,
+ realName,
+ desc,
+ });
+ },
+ },
+ {
+ url: '/basic-api/getUserInfo',
+ method: 'get',
+ response: (request: requestParams) => {
+ const token = getRequestToken(request);
+ if (!token) return resultError('Invalid token');
+ const checkUser = createFakeUserList().find((item) => item.token === token);
+ if (!checkUser) {
+ return resultError('The corresponding user information was not obtained!');
+ }
+ return resultSuccess(checkUser);
+ },
+ },
+ {
+ url: '/basic-api/getPermCode',
+ timeout: 200,
+ method: 'get',
+ response: (request: requestParams) => {
+ const token = getRequestToken(request);
+ if (!token) return resultError('Invalid token');
+ const checkUser = createFakeUserList().find((item) => item.token === token);
+ if (!checkUser) {
+ return resultError('Invalid token!');
+ }
+ const codeList = fakeCodeList[checkUser.userId];
+
+ return resultSuccess(codeList);
+ },
+ },
+ {
+ url: '/basic-api/logout',
+ timeout: 200,
+ method: 'get',
+ response: (request: requestParams) => {
+ const token = getRequestToken(request);
+ if (!token) return resultError('Invalid token');
+ const checkUser = createFakeUserList().find((item) => item.token === token);
+ if (!checkUser) {
+ return resultError('Invalid token!');
+ }
+ return resultSuccess(undefined, { message: 'Token has been destroyed' });
+ },
+ },
+ {
+ url: '/basic-api/testRetry',
+ statusCode: 405,
+ method: 'get',
+ response: () => {
+ return resultError('Error!');
+ },
+ },
+] as MockMethod[];
diff --git a/frontend/vben/package.json b/frontend/vben/package.json
new file mode 100644
index 0000000..8fcf5fb
--- /dev/null
+++ b/frontend/vben/package.json
@@ -0,0 +1,191 @@
+{
+ "name": "vben-admin",
+ "version": "2.8.0",
+ "author": {
+ "name": "vben",
+ "email": "anncwb@126.com",
+ "url": "https://github.com/anncwb"
+ },
+ "scripts": {
+ "commit": "czg",
+ "bootstrap": "pnpm install",
+ "serve": "npm run dev",
+ "dev": "vite",
+ "build": "cross-env NODE_ENV=production vite build && esno ./build/script/postBuild.ts",
+ "build:test": "cross-env vite build --mode test && esno ./build/script/postBuild.ts",
+ "build:no-cache": "pnpm clean:cache && npm run build",
+ "report": "cross-env REPORT=true npm run build",
+ "type:check": "vue-tsc --noEmit --skipLibCheck",
+ "preview": "npm run build && vite preview",
+ "preview:dist": "vite preview",
+ "log": "conventional-changelog -p angular -i CHANGELOG.md -s",
+ "clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
+ "clean:lib": "rimraf node_modules",
+ "lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
+ "lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
+ "lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
+ "lint:lint-staged": "lint-staged",
+ "test:unit": "jest",
+ "test:gzip": "npx http-server dist --cors --gzip -c-1",
+ "test:br": "npx http-server dist --cors --brotli -c-1",
+ "reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
+ "prepare": "husky install",
+ "gen:icon": "esno ./build/generate/icon/index.ts"
+ },
+ "dependencies": {
+ "@ant-design/colors": "^6.0.0",
+ "@ant-design/icons-vue": "^6.1.0",
+ "@iconify/iconify": "^2.2.1",
+ "@logicflow/core": "^1.1.13",
+ "@logicflow/extension": "^1.1.13",
+ "@vue/runtime-core": "^3.2.33",
+ "@vue/shared": "^3.2.33",
+ "@vueuse/core": "^8.3.0",
+ "@vueuse/shared": "^8.3.0",
+ "@zxcvbn-ts/core": "^2.0.1",
+ "ant-design-vue": "^3.2.0",
+ "axios": "^0.26.1",
+ "codemirror": "^5.65.3",
+ "cropperjs": "^1.5.12",
+ "crypto-js": "^4.1.1",
+ "dayjs": "^1.11.1",
+ "echarts": "^5.3.2",
+ "intro.js": "^5.1.0",
+ "lodash-es": "^4.17.21",
+ "mockjs": "^1.1.0",
+ "nprogress": "^0.2.0",
+ "path-to-regexp": "^6.2.0",
+ "pinia": "2.0.12",
+ "qs": "^6.10.3",
+ "resize-observer-polyfill": "^1.5.1",
+ "showdown": "^2.1.0",
+ "sortablejs": "^1.15.0",
+ "tinymce": "^5.10.3",
+ "vditor": "^3.8.13",
+ "vue": "^3.2.33",
+ "vue-i18n": "^9.1.9",
+ "vue-json-pretty": "^2.0.6",
+ "vue-router": "^4.0.14",
+ "vue-types": "^4.1.1",
+ "xlsx": "^0.18.5"
+ },
+ "devDependencies": {
+ "@commitlint/cli": "^16.2.3",
+ "@commitlint/config-conventional": "^16.2.1",
+ "@iconify/json": "^2.1.30",
+ "@purge-icons/generated": "^0.8.1",
+ "@types/codemirror": "^5.60.5",
+ "@types/crypto-js": "^4.1.1",
+ "@types/fs-extra": "^9.0.13",
+ "@types/inquirer": "^8.2.1",
+ "@types/intro.js": "^3.0.2",
+ "@types/lodash-es": "^4.17.6",
+ "@types/mockjs": "^1.0.6",
+ "@types/node": "^17.0.25",
+ "@types/nprogress": "^0.2.0",
+ "@types/qs": "^6.9.7",
+ "@types/showdown": "^1.9.4",
+ "@types/sortablejs": "^1.10.7",
+ "@typescript-eslint/eslint-plugin": "^5.20.0",
+ "@typescript-eslint/parser": "^5.20.0",
+ "@vitejs/plugin-legacy": "^1.8.1",
+ "@vitejs/plugin-vue": "^2.3.1",
+ "@vitejs/plugin-vue-jsx": "^1.3.10",
+ "@vue/compiler-sfc": "^3.2.33",
+ "@vue/test-utils": "^2.0.0-rc.21",
+ "autoprefixer": "^10.4.4",
+ "conventional-changelog-cli": "^2.2.2",
+ "cross-env": "^7.0.3",
+ "cz-git": "^1.3.11",
+ "czg": "^1.3.11",
+ "dotenv": "^16.0.0",
+ "eslint": "^8.13.0",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-prettier": "^4.0.0",
+ "eslint-plugin-vue": "^8.6.0",
+ "esno": "^0.14.1",
+ "fs-extra": "^10.1.0",
+ "husky": "^7.0.4",
+ "inquirer": "^8.2.2",
+ "less": "^4.1.2",
+ "lint-staged": "12.3.7",
+ "npm-run-all": "^4.1.5",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.4.12",
+ "postcss-html": "^1.4.1",
+ "postcss-less": "^6.0.0",
+ "prettier": "^2.6.2",
+ "rimraf": "^3.0.2",
+ "rollup": "^2.70.2",
+ "rollup-plugin-visualizer": "^5.6.0",
+ "stylelint": "^14.7.1",
+ "stylelint-config-prettier": "^9.0.3",
+ "stylelint-config-recommended": "^7.0.0",
+ "stylelint-config-recommended-vue": "^1.4.0",
+ "stylelint-config-standard": "^25.0.0",
+ "stylelint-order": "^5.0.0",
+ "ts-node": "^10.7.0",
+ "typescript": "^4.6.3",
+ "vite": "^2.9.5",
+ "vite-plugin-compression": "^0.5.1",
+ "vite-plugin-html": "^3.2.0",
+ "vite-plugin-imagemin": "^0.6.1",
+ "vite-plugin-mkcert": "^1.6.0",
+ "vite-plugin-mock": "^2.9.6",
+ "vite-plugin-purge-icons": "^0.8.1",
+ "vite-plugin-pwa": "^0.11.13",
+ "vite-plugin-style-import": "^2.0.0",
+ "vite-plugin-svg-icons": "^2.0.1",
+ "vite-plugin-theme": "^0.8.6",
+ "vite-plugin-vue-setup-extend": "^0.4.0",
+ "vite-plugin-windicss": "^1.8.4",
+ "vue-eslint-parser": "^8.3.0",
+ "vue-tsc": "^0.33.9"
+ },
+ "resolutions": {
+ "bin-wrapper": "npm:bin-wrapper-china",
+ "rollup": "^2.56.3",
+ "gifsicle": "5.2.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/anncwb/vue-vben-admin.git"
+ },
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/anncwb/vue-vben-admin/issues"
+ },
+ "homepage": "https://github.com/anncwb/vue-vben-admin",
+ "engines": {
+ "node": "^12 || >=14"
+ },
+ "lint-staged": {
+ "*.{js,jsx,ts,tsx}": [
+ "eslint --fix",
+ "prettier --write"
+ ],
+ "{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
+ "prettier --write--parser json"
+ ],
+ "package.json": [
+ "prettier --write"
+ ],
+ "*.vue": [
+ "eslint --fix",
+ "prettier --write",
+ "stylelint --fix"
+ ],
+ "*.{scss,less,styl,html}": [
+ "stylelint --fix",
+ "prettier --write"
+ ],
+ "*.md": [
+ "prettier --write"
+ ]
+ },
+ "config": {
+ "commitizen": {
+ "path": "node_modules/cz-git"
+ }
+ }
+}
diff --git a/frontend/vben/postcss.config.js b/frontend/vben/postcss.config.js
new file mode 100755
index 0000000..a47ef4f
--- /dev/null
+++ b/frontend/vben/postcss.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ plugins: {
+ autoprefixer: {},
+ },
+};
diff --git a/frontend/vben/prettier.config.js b/frontend/vben/prettier.config.js
new file mode 100644
index 0000000..a5bfe16
--- /dev/null
+++ b/frontend/vben/prettier.config.js
@@ -0,0 +1,10 @@
+module.exports = {
+ printWidth: 100,
+ semi: false,
+ vueIndentScriptAndStyle: true,
+ singleQuote: true,
+ trailingComma: 'all',
+ proseWrap: 'never',
+ htmlWhitespaceSensitivity: 'strict',
+ endOfLine: 'auto',
+}
diff --git a/frontend/vben/public/favicon.ico b/frontend/vben/public/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..d92e0b8c4963f1ae81021bbed63e94c2699dea66
GIT binary patch
literal 894
zcmZQzU<5(|0R|u`!H~hsz#zuJz@P!dKp_SNAO?x!2k(<*|Mp73(S^ghaCG}W3micV
zd6}gJRR66~2Cn|nK^;8mfr=prt{%vMt3Q887jFKoe=I;N;Of6uAgs8$R~xS7)?Ze*
z7G(86jaaP!YD8B5HAMw(#m^dPxRwit(0u_9G`M>FR>0MN^(+15GZl&w=KiVL1wn6T
z6kkZ}DT8x=g_XnA|4v>HSO33$3S12u1Fjy#fSC_upeciM
zfBAL8wfs)p0oU^X|9`j|GzMHfvK44*kh#ACs}Nd}*5GjrTo*z;*cWhd@)!pg85kNs
n7^oeNfqano0bpKwAP>a+3=AJYn4N*)2ax^&6#E0jpkM$1?7KMX
literal 0
HcmV?d00001
diff --git a/frontend/vben/public/resource/img/logo.png b/frontend/vben/public/resource/img/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..cd4c33d8b71f31d32aaed9fefe14f237b81e764f
GIT binary patch
literal 4042
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
literal 0
HcmV?d00001
diff --git a/frontend/vben/public/resource/img/pwa-192x192.png b/frontend/vben/public/resource/img/pwa-192x192.png
new file mode 100644
index 0000000000000000000000000000000000000000..00fb815215e1fcb171d1c62fca6c54018663cb72
GIT binary patch
literal 12205
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