From 8cd71184570a7f33eb1d6dd8f1d652a5bffde7cf Mon Sep 17 00:00:00 2001 From: xingyuv Date: Sat, 18 Mar 2023 21:10:54 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 19 + .env | 8 + .env.development | 24 + .env.production | 31 + .env.test | 32 + .eslintignore | 14 + .eslintrc.js | 74 + .gitattributes | 11 + .gitignore | 33 + .gitpod.yml | 6 + .hintrc | 16 + .husky/commit-msg | 8 + .husky/common.sh | 9 + .husky/pre-commit | 10 + .lintstagedrc.js | 8 + .prettierignore | 9 + .stylelintignore | 3 + .vscode/extensions.json | 13 + .vscode/launch.json | 13 + .vscode/settings.json | 167 + CNAME | 1 + LICENSE | 21 + README.md | 122 + build/config/themeConfig.ts | 69 + build/constant.ts | 6 + build/generate/generateModifyVars.ts | 37 + build/generate/icon/index.ts | 68 + build/getConfigFileName.ts | 7 + build/script/buildConf.ts | 47 + build/script/postBuild.ts | 23 + build/utils.ts | 92 + build/vite/optimize.ts | 35 + build/vite/plugin/compress.ts | 32 + build/vite/plugin/html.ts | 40 + build/vite/plugin/index.ts | 76 + build/vite/plugin/mock.ts | 19 + build/vite/plugin/pwa.ts | 33 + build/vite/plugin/styleImport.ts | 82 + build/vite/plugin/svgSprite.ts | 17 + build/vite/plugin/theme.ts | 83 + build/vite/plugin/visualizer.ts | 17 + build/vite/proxy.ts | 34 + commitlint.config.js | 91 + index.html | 158 + mock/_createProductionServer.ts | 34 + mock/_util.ts | 52 + mock/demo/account.ts | 71 + mock/demo/api-cascader.ts | 325 + mock/demo/select-demo.ts | 28 + mock/demo/system.ts | 194 + mock/demo/table-demo.ts | 55 + mock/demo/tree-demo.ts | 38 + mock/sys/menu.ts | 270 + mock/sys/user.ts | 120 + package.json | 189 + pnpm-lock.yaml | 9745 +++++++++++++++++ postcss.config.js | 5 + prettier.config.js | 37 + public/favicon.ico | Bin 0 -> 894 bytes public/resource/img/logo.png | Bin 0 -> 4042 bytes public/resource/img/pwa-192x192.png | Bin 0 -> 12205 bytes public/resource/img/pwa-512x512.png | Bin 0 -> 52656 bytes 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 + src/App.vue | 27 + src/api/demo/account.ts | 22 + src/api/demo/cascader.ts | 10 + src/api/demo/error.ts | 14 + src/api/demo/model/accountModel.ts | 7 + src/api/demo/model/areaModel.ts | 12 + src/api/demo/model/optionsModel.ts | 15 + src/api/demo/model/systemModel.ts | 74 + src/api/demo/model/tableModel.ts | 20 + src/api/demo/select.ts | 12 + src/api/demo/system.ts | 51 + src/api/demo/table.ts | 21 + src/api/demo/tree.ts | 12 + src/api/model/baseModel.ts | 9 + src/api/sys/menu.ts | 14 + src/api/sys/model/menuModel.ts | 16 + src/api/sys/model/uploadModel.ts | 5 + src/api/sys/model/userModel.ts | 38 + src/api/sys/upload.ts | 19 + src/api/sys/user.ts | 55 + 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 + src/assets/icons/moon.svg | 16 + src/assets/icons/sun.svg | 42 + src/assets/icons/test.svg | 21 + src/assets/icons/total-sales.svg | 1 + src/assets/icons/transaction.svg | 1 + src/assets/icons/visit-count.svg | 1 + src/assets/images/demo.png | Bin 0 -> 33342 bytes src/assets/images/header.jpg | Bin 0 -> 16880 bytes src/assets/images/logo.png | Bin 0 -> 28304 bytes src/assets/svg/illustration.svg | 1 + src/assets/svg/login-bg-dark.svg | 19 + src/assets/svg/login-bg.svg | 17 + src/assets/svg/login-box-bg.svg | 1 + src/assets/svg/net-error.svg | 1 + src/assets/svg/no-data.svg | 1 + src/assets/svg/preview/p-rotate.svg | 1 + src/assets/svg/preview/resume.svg | 1 + src/assets/svg/preview/scale.svg | 1 + src/assets/svg/preview/unrotate.svg | 1 + src/assets/svg/preview/unscale.svg | 1 + src/components/Application/index.ts | 17 + .../Application/src/AppDarkModeToggle.vue | 76 + .../Application/src/AppLocalePicker.vue | 72 + src/components/Application/src/AppLogo.vue | 85 + .../Application/src/AppProvider.vue | 77 + .../Application/src/AppSizePicker.vue | 68 + .../Application/src/search/AppSearch.vue | 33 + .../src/search/AppSearchFooter.vue | 55 + .../src/search/AppSearchKeyItem.vue | 11 + .../Application/src/search/AppSearchModal.vue | 259 + .../Application/src/search/useMenuSearch.ts | 166 + .../Application/src/useAppContext.ts | 17 + src/components/Authority/index.ts | 4 + src/components/Authority/src/Authority.vue | 44 + src/components/Basic/index.ts | 8 + src/components/Basic/src/BasicArrow.vue | 80 + src/components/Basic/src/BasicHelp.vue | 112 + src/components/Basic/src/BasicTitle.vue | 75 + src/components/Button/index.ts | 9 + src/components/Button/src/BasicButton.vue | 32 + .../Button/src/PopConfirmButton.vue | 54 + src/components/Button/src/props.ts | 26 + src/components/CardList/index.ts | 4 + src/components/CardList/src/CardList.vue | 164 + src/components/CardList/src/data.ts | 25 + src/components/CodeEditor/index.ts | 8 + src/components/CodeEditor/src/CodeEditor.vue | 49 + .../CodeEditor/src/codemirror/CodeMirror.vue | 110 + .../CodeEditor/src/codemirror/codeMirror.ts | 21 + .../CodeEditor/src/codemirror/codemirror.css | 525 + .../src/json-preview/JsonPreview.vue | 12 + src/components/CodeEditor/src/typing.ts | 5 + src/components/Container/index.ts | 10 + .../Container/src/LazyContainer.vue | 125 + .../Container/src/ScrollContainer.vue | 90 + .../src/collapse/CollapseContainer.vue | 118 + .../Container/src/collapse/CollapseHeader.vue | 42 + src/components/Container/src/typing.ts | 17 + src/components/ContextMenu/index.ts | 3 + .../ContextMenu/src/ContextMenu.vue | 204 + .../ContextMenu/src/createContextMenu.ts | 75 + src/components/ContextMenu/src/typing.ts | 36 + src/components/CountDown/index.ts | 6 + src/components/CountDown/src/CountButton.vue | 52 + .../CountDown/src/CountdownInput.vue | 41 + src/components/CountDown/src/useCountdown.ts | 51 + src/components/CountTo/index.ts | 4 + src/components/CountTo/src/CountTo.vue | 105 + src/components/Cropper/index.ts | 7 + src/components/Cropper/src/CopperModal.vue | 233 + src/components/Cropper/src/Cropper.vue | 173 + src/components/Cropper/src/CropperAvatar.vue | 116 + src/components/Cropper/src/typing.ts | 8 + src/components/Description/index.ts | 6 + .../Description/src/Description.vue | 184 + src/components/Description/src/typing.ts | 47 + .../Description/src/useDescription.ts | 28 + src/components/Drawer/index.ts | 6 + src/components/Drawer/src/BasicDrawer.vue | 218 + .../Drawer/src/components/DrawerFooter.vue | 70 + .../Drawer/src/components/DrawerHeader.vue | 65 + src/components/Drawer/src/props.ts | 42 + src/components/Drawer/src/typing.ts | 193 + src/components/Drawer/src/useDrawer.ts | 146 + src/components/Dropdown/index.ts | 5 + src/components/Dropdown/src/Dropdown.vue | 87 + src/components/Dropdown/src/typing.ts | 9 + src/components/Excel/index.ts | 8 + src/components/Excel/src/Export2Excel.ts | 76 + src/components/Excel/src/ExportExcelModal.vue | 65 + src/components/Excel/src/ImportExcel.vue | 211 + src/components/Excel/src/typing.ts | 27 + src/components/FlowChart/index.ts | 4 + src/components/FlowChart/src/FlowChart.vue | 142 + .../FlowChart/src/FlowChartToolbar.vue | 153 + .../FlowChart/src/adpterForTurbo.ts | 75 + src/components/FlowChart/src/config.ts | 96 + src/components/FlowChart/src/enum.ts | 11 + src/components/FlowChart/src/types.ts | 14 + .../FlowChart/src/useFlowContext.ts | 17 + src/components/Form/index.ts | 17 + src/components/Form/src/BasicForm.vue | 320 + src/components/Form/src/componentMap.ts | 83 + .../Form/src/components/ApiCascader.vue | 182 + .../Form/src/components/ApiRadioGroup.vue | 120 + .../Form/src/components/ApiSelect.vue | 140 + .../Form/src/components/ApiTransfer.vue | 125 + .../Form/src/components/ApiTree.vue | 86 + .../Form/src/components/ApiTreeSelect.vue | 81 + .../Form/src/components/FormAction.vue | 101 + .../Form/src/components/FormItem.vue | 374 + .../Form/src/components/RadioButtonGroup.vue | 60 + src/components/Form/src/helper.ts | 83 + src/components/Form/src/hooks/useAdvanced.ts | 159 + src/components/Form/src/hooks/useAutoFocus.ts | 35 + .../Form/src/hooks/useComponentRegister.ts | 11 + src/components/Form/src/hooks/useForm.ts | 116 + .../Form/src/hooks/useFormContext.ts | 17 + .../Form/src/hooks/useFormEvents.ts | 316 + .../Form/src/hooks/useFormValues.ts | 138 + .../Form/src/hooks/useLabelWidth.ts | 37 + src/components/Form/src/props.ts | 103 + src/components/Form/src/types/form.ts | 211 + src/components/Form/src/types/formItem.ts | 91 + src/components/Form/src/types/hooks.ts | 6 + src/components/Form/src/types/index.ts | 117 + src/components/Icon/data/icons.data.ts | 793 ++ src/components/Icon/index.ts | 7 + src/components/Icon/src/Icon.vue | 94 + src/components/Icon/src/IconPicker.vue | 176 + src/components/Icon/src/SvgIcon.vue | 56 + src/components/Loading/index.ts | 5 + src/components/Loading/src/Loading.vue | 61 + src/components/Loading/src/createLoading.ts | 65 + src/components/Loading/src/typing.ts | 10 + src/components/Loading/src/useLoading.ts | 47 + src/components/Markdown/index.ts | 7 + src/components/Markdown/src/Markdown.vue | 140 + .../Markdown/src/MarkdownViewer.vue | 62 + src/components/Markdown/src/getTheme.ts | 16 + src/components/Markdown/src/typing.ts | 4 + src/components/Menu/index.ts | 3 + src/components/Menu/src/BasicMenu.vue | 137 + .../Menu/src/components/BasicMenuItem.vue | 14 + .../Menu/src/components/BasicSubMenuItem.vue | 36 + .../Menu/src/components/MenuItemContent.vue | 19 + src/components/Menu/src/index.less | 74 + src/components/Menu/src/props.ts | 59 + src/components/Menu/src/types.ts | 25 + src/components/Menu/src/useOpenKeys.ts | 78 + src/components/Modal/index.ts | 8 + src/components/Modal/src/BasicModal.vue | 205 + src/components/Modal/src/components/Modal.tsx | 31 + .../Modal/src/components/ModalClose.vue | 95 + .../Modal/src/components/ModalFooter.vue | 28 + .../Modal/src/components/ModalHeader.vue | 15 + .../Modal/src/components/ModalWrapper.vue | 140 + src/components/Modal/src/hooks/useModal.ts | 148 + .../Modal/src/hooks/useModalContext.ts | 16 + .../Modal/src/hooks/useModalDrag.ts | 107 + .../Modal/src/hooks/useModalFullScreen.ts | 43 + src/components/Modal/src/index.less | 130 + src/components/Modal/src/props.ts | 83 + src/components/Modal/src/typing.ts | 209 + src/components/Page/index.ts | 9 + src/components/Page/src/PageFooter.vue | 42 + src/components/Page/src/PageWrapper.vue | 162 + src/components/Preview/index.ts | 2 + src/components/Preview/src/Functional.vue | 536 + src/components/Preview/src/Preview.vue | 79 + src/components/Preview/src/functional.ts | 17 + src/components/Preview/src/typing.ts | 55 + src/components/Qrcode/index.ts | 5 + src/components/Qrcode/src/Qrcode.vue | 111 + src/components/Qrcode/src/drawCanvas.ts | 32 + src/components/Qrcode/src/drawLogo.ts | 81 + src/components/Qrcode/src/qrcodePlus.ts | 4 + src/components/Qrcode/src/toCanvas.ts | 10 + src/components/Qrcode/src/typing.ts | 38 + src/components/Scrollbar/index.ts | 8 + src/components/Scrollbar/src/Scrollbar.vue | 178 + src/components/Scrollbar/src/bar.ts | 92 + src/components/Scrollbar/src/types.d.ts | 18 + src/components/Scrollbar/src/util.ts | 49 + src/components/SimpleMenu/index.ts | 2 + src/components/SimpleMenu/src/SimpleMenu.vue | 133 + .../SimpleMenu/src/SimpleMenuTag.vue | 57 + .../SimpleMenu/src/SimpleSubMenu.vue | 87 + .../SimpleMenu/src/components/Menu.vue | 141 + .../src/components/MenuCollapseTransition.vue | 69 + .../SimpleMenu/src/components/MenuItem.vue | 100 + .../SimpleMenu/src/components/SubMenuItem.vue | 283 + .../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 + src/components/SimpleMenu/src/useOpenKeys.ts | 50 + src/components/StrengthMeter/index.ts | 4 + .../StrengthMeter/src/StrengthMeter.vue | 122 + src/components/Table/index.ts | 11 + src/components/Table/src/BasicTable.vue | 404 + src/components/Table/src/componentMap.ts | 30 + .../src/components/EditTableHeaderIcon.vue | 12 + .../Table/src/components/HeaderCell.vue | 37 + .../Table/src/components/TableAction.vue | 185 + .../Table/src/components/TableFooter.vue | 88 + .../Table/src/components/TableHeader.vue | 61 + .../Table/src/components/TableImg.vue | 75 + .../Table/src/components/TableTitle.vue | 46 + .../src/components/editable/CellComponent.ts | 38 + .../src/components/editable/EditableCell.vue | 526 + .../Table/src/components/editable/helper.ts | 28 + .../Table/src/components/editable/index.ts | 68 + .../src/components/settings/ColumnSetting.vue | 414 + .../components/settings/FullScreenSetting.vue | 20 + .../src/components/settings/RedoSetting.vue | 21 + .../src/components/settings/SizeSetting.vue | 46 + .../Table/src/components/settings/index.vue | 56 + src/components/Table/src/const.ts | 31 + src/components/Table/src/hooks/useColumns.ts | 313 + .../Table/src/hooks/useCustomRow.ts | 94 + .../Table/src/hooks/useDataSource.ts | 349 + src/components/Table/src/hooks/useLoading.ts | 21 + .../Table/src/hooks/usePagination.tsx | 85 + .../Table/src/hooks/useRowSelection.ts | 118 + src/components/Table/src/hooks/useScrollTo.ts | 50 + src/components/Table/src/hooks/useTable.ts | 168 + .../Table/src/hooks/useTableContext.ts | 22 + .../Table/src/hooks/useTableExpand.ts | 61 + .../Table/src/hooks/useTableFooter.ts | 52 + .../Table/src/hooks/useTableForm.ts | 48 + .../Table/src/hooks/useTableHeader.ts | 50 + .../Table/src/hooks/useTableScroll.ts | 209 + .../Table/src/hooks/useTableStyle.ts | 20 + src/components/Table/src/props.ts | 135 + src/components/Table/src/types/column.ts | 195 + .../Table/src/types/componentType.ts | 14 + src/components/Table/src/types/pagination.ts | 109 + src/components/Table/src/types/table.ts | 464 + src/components/Table/src/types/tableAction.ts | 39 + src/components/Tinymce/index.ts | 4 + src/components/Tinymce/src/Editor.vue | 302 + src/components/Tinymce/src/ImgUpload.vue | 75 + src/components/Tinymce/src/helper.ts | 81 + src/components/Tinymce/src/tinymce.ts | 13 + src/components/Transition/index.ts | 21 + .../Transition/src/CollapseTransition.vue | 69 + .../Transition/src/CreateTransition.tsx | 67 + .../Transition/src/ExpandTransition.ts | 89 + src/components/Tree/index.ts | 6 + src/components/Tree/src/BasicTree.vue | 421 + src/components/Tree/src/TreeIcon.ts | 13 + .../Tree/src/components/TreeHeader.vue | 162 + src/components/Tree/src/hooks/useTree.ts | 209 + src/components/Tree/src/types/tree.ts | 180 + src/components/Tree/style/index.less | 52 + src/components/Tree/style/index.ts | 1 + src/components/Upload/index.ts | 4 + src/components/Upload/src/BasicUpload.vue | 103 + src/components/Upload/src/FileList.vue | 102 + src/components/Upload/src/ThumbUrl.vue | 25 + src/components/Upload/src/UploadModal.vue | 277 + .../Upload/src/UploadPreviewModal.vue | 88 + src/components/Upload/src/data.tsx | 147 + src/components/Upload/src/helper.ts | 27 + src/components/Upload/src/props.ts | 82 + src/components/Upload/src/typing.ts | 55 + src/components/Upload/src/useUpload.ts | 60 + src/components/Verify/index.ts | 7 + src/components/Verify/src/DragVerify.vue | 365 + src/components/Verify/src/ImgRotate.vue | 214 + src/components/Verify/src/props.ts | 86 + src/components/Verify/src/typing.ts | 14 + src/components/VirtualScroll/index.ts | 4 + .../VirtualScroll/src/VirtualScroll.vue | 180 + src/components/registerGlobComp.ts | 7 + src/design/ant/btn.less | 286 + src/design/ant/index.less | 68 + src/design/ant/input.less | 27 + src/design/ant/pagination.less | 96 + src/design/ant/table.less | 76 + src/design/color.less | 138 + src/design/config.less | 2 + src/design/index.less | 45 + src/design/public.less | 51 + src/design/theme.less | 54 + src/design/transition/base.less | 18 + src/design/transition/fade.less | 93 + src/design/transition/index.less | 11 + src/design/transition/scale.less | 21 + src/design/transition/scroll.less | 67 + src/design/transition/slide.less | 39 + src/design/transition/zoom.less | 27 + src/design/var/breakpoint.less | 33 + src/design/var/easing.less | 18 + src/design/var/index.less | 40 + src/directives/clickOutside.ts | 78 + src/directives/index.ts | 11 + src/directives/loading.ts | 39 + src/directives/permission.ts | 32 + src/directives/repeatClick.ts | 31 + src/directives/ripple/index.less | 21 + src/directives/ripple/index.ts | 180 + src/enums/appEnum.ts | 52 + src/enums/breakpointEnum.ts | 28 + src/enums/cacheEnum.ts | 31 + src/enums/exceptionEnum.ts | 27 + src/enums/httpEnum.ts | 31 + src/enums/menuEnum.ts | 50 + src/enums/pageEnum.ts | 10 + src/enums/roleEnum.ts | 7 + src/enums/sizeEnum.ts | 19 + src/hooks/component/useFormItem.ts | 46 + src/hooks/component/usePageContext.ts | 18 + src/hooks/core/onMountedOrActivated.ts | 18 + src/hooks/core/useAttrs.ts | 40 + src/hooks/core/useContext.ts | 29 + src/hooks/core/useLockFn.ts | 17 + src/hooks/core/useRefs.ts | 16 + src/hooks/core/useTimeout.ts | 45 + src/hooks/event/useBreakpoint.ts | 93 + src/hooks/event/useEventListener.ts | 58 + src/hooks/event/useIntersectionObserver.ts | 42 + src/hooks/event/useScroll.ts | 65 + src/hooks/event/useScrollTo.ts | 59 + src/hooks/event/useWindowSizeFn.ts | 35 + src/hooks/setting/index.ts | 25 + src/hooks/setting/useHeaderSetting.ts | 86 + src/hooks/setting/useMenuSetting.ts | 155 + src/hooks/setting/useMultipleTabSetting.ts | 28 + src/hooks/setting/useRootSetting.ts | 90 + src/hooks/setting/useTransitionSetting.ts | 31 + src/hooks/web/useAppInject.ts | 10 + src/hooks/web/useContentHeight.ts | 183 + src/hooks/web/useContextMenu.ts | 12 + src/hooks/web/useCopyToClipboard.ts | 68 + src/hooks/web/useDesign.ts | 22 + src/hooks/web/useECharts.ts | 126 + src/hooks/web/useFullContent.ts | 28 + src/hooks/web/useI18n.ts | 55 + src/hooks/web/useLockPage.ts | 72 + src/hooks/web/useMessage.tsx | 123 + src/hooks/web/usePage.ts | 53 + src/hooks/web/usePagination.ts | 31 + src/hooks/web/usePermission.ts | 107 + src/hooks/web/useScript.ts | 46 + src/hooks/web/useSortable.ts | 21 + src/hooks/web/useTabs.ts | 103 + src/hooks/web/useTitle.ts | 35 + src/hooks/web/useWatermark.ts | 98 + src/layouts/default/content/index.vue | 40 + .../default/content/useContentContext.ts | 17 + .../default/content/useContentViewHeight.ts | 42 + src/layouts/default/feature/index.vue | 82 + src/layouts/default/footer/index.vue | 79 + src/layouts/default/header/MultipleHeader.vue | 95 + .../default/header/components/Breadcrumb.vue | 195 + .../default/header/components/ErrorAction.vue | 30 + .../default/header/components/FullScreen.vue | 24 + .../default/header/components/index.ts | 14 + .../header/components/lock/LockModal.vue | 103 + .../header/components/notify/NoticeList.vue | 171 + .../default/header/components/notify/data.ts | 192 + .../header/components/notify/index.vue | 80 + .../components/user-dropdown/DropMenuItem.vue | 25 + .../header/components/user-dropdown/index.vue | 144 + src/layouts/default/header/index.less | 196 + src/layouts/default/header/index.vue | 129 + src/layouts/default/index.vue | 71 + src/layouts/default/menu/index.vue | 183 + src/layouts/default/menu/useLayoutMenu.ts | 105 + src/layouts/default/setting/SettingDrawer.tsx | 343 + .../setting/components/InputNumberItem.vue | 39 + .../default/setting/components/SelectItem.vue | 64 + .../setting/components/SettingFooter.vue | 85 + .../default/setting/components/SwitchItem.vue | 54 + .../setting/components/ThemeColorPicker.vue | 77 + .../default/setting/components/TypePicker.vue | 166 + .../default/setting/components/index.ts | 8 + src/layouts/default/setting/enum.ts | 150 + src/layouts/default/setting/handler.ts | 174 + src/layouts/default/setting/index.vue | 14 + src/layouts/default/sider/DragBar.vue | 55 + src/layouts/default/sider/LayoutSider.vue | 144 + src/layouts/default/sider/MixSider.vue | 573 + src/layouts/default/sider/index.vue | 48 + src/layouts/default/sider/useLayoutSider.ts | 137 + .../default/tabs/components/FoldButton.vue | 32 + .../default/tabs/components/TabContent.vue | 54 + .../default/tabs/components/TabRedo.vue | 25 + src/layouts/default/tabs/index.less | 207 + src/layouts/default/tabs/index.vue | 120 + src/layouts/default/tabs/types.ts | 25 + src/layouts/default/tabs/useMultipleTabs.ts | 78 + src/layouts/default/tabs/useTabDropdown.ts | 139 + src/layouts/default/trigger/HeaderTrigger.vue | 15 + src/layouts/default/trigger/SiderTrigger.vue | 12 + src/layouts/default/trigger/index.vue | 15 + src/layouts/iframe/index.vue | 17 + src/layouts/iframe/useFrameKeepAlive.ts | 59 + src/layouts/page/index.vue | 55 + src/layouts/page/transition.ts | 33 + src/locales/helper.ts | 37 + src/locales/lang/en.ts | 12 + src/locales/lang/en/common.ts | 20 + src/locales/lang/en/component.ts | 129 + src/locales/lang/en/layout.ts | 114 + src/locales/lang/en/routes/basic.ts | 4 + src/locales/lang/en/routes/dashboard.ts | 6 + src/locales/lang/en/routes/demo.ts | 200 + src/locales/lang/en/sys.ts | 104 + .../lang/zh-CN/antdLocale/DatePicker.ts | 6 + src/locales/lang/zh-CN/common.ts | 20 + src/locales/lang/zh-CN/component.ts | 134 + src/locales/lang/zh-CN/layout.ts | 115 + src/locales/lang/zh-CN/routes/basic.ts | 4 + src/locales/lang/zh-CN/routes/dashboard.ts | 6 + src/locales/lang/zh-CN/routes/demo.ts | 191 + src/locales/lang/zh-CN/sys.ts | 100 + src/locales/lang/zh_CN.ts | 10 + src/locales/setupI18n.ts | 44 + src/locales/useLocale.ts | 69 + src/logics/error-handle/index.ts | 178 + src/logics/initAppConfig.ts | 84 + src/logics/mitt/routeChange.ts | 28 + src/logics/theme/dark.ts | 24 + src/logics/theme/index.ts | 17 + src/logics/theme/updateBackground.ts | 75 + src/logics/theme/updateColorWeak.ts | 9 + src/logics/theme/updateGrayMode.ts | 9 + src/logics/theme/util.ts | 11 + src/main.ts | 67 + src/router/constant.ts | 24 + src/router/guard/index.ts | 147 + src/router/guard/paramMenuGuard.ts | 47 + src/router/guard/permissionGuard.ts | 117 + src/router/guard/stateGuard.ts | 24 + src/router/helper/menuHelper.ts | 106 + src/router/helper/routeHelper.ts | 172 + src/router/index.ts | 42 + src/router/menus/index.ts | 136 + src/router/routes/basic.ts | 73 + src/router/routes/index.ts | 43 + src/router/routes/mainOut.ts | 22 + src/router/routes/modules/about.ts | 31 + src/router/routes/modules/dashboard.ts | 37 + src/router/routes/modules/demo/charts.ts | 80 + src/router/routes/modules/demo/comp.ts | 555 + src/router/routes/modules/demo/feat.ts | 315 + src/router/routes/modules/demo/flow.ts | 28 + src/router/routes/modules/demo/iframe.ts | 48 + src/router/routes/modules/demo/level.ts | 68 + src/router/routes/modules/demo/page.ts | 255 + src/router/routes/modules/demo/permission.ts | 92 + src/router/routes/modules/demo/setup.ts | 31 + src/router/routes/modules/demo/system.ts | 78 + src/router/types.ts | 55 + src/settings/componentSetting.ts | 51 + src/settings/designSetting.ts | 48 + src/settings/encryptionSetting.ts | 13 + src/settings/localeSetting.ts | 29 + src/settings/projectSetting.ts | 182 + src/settings/siteSetting.ts | 5 + src/settings/sizeSetting.ts | 35 + src/store/index.ts | 10 + src/store/modules/app.ts | 110 + src/store/modules/errorLog.ts | 73 + src/store/modules/locale.ts | 54 + src/store/modules/lock.ts | 58 + src/store/modules/multipleTab.ts | 357 + src/store/modules/permission.ts | 259 + src/store/modules/user.ts | 176 + src/types/axios.d.ts | 56 + src/types/config.d.ts | 174 + src/types/global.d.ts | 91 + src/types/index.d.ts | 27 + src/types/module.d.ts | 16 + src/types/store.d.ts | 48 + src/types/utils.d.ts | 5 + src/types/vue-router.d.ts | 47 + src/utils/auth/index.ts | 26 + src/utils/bem.ts | 52 + src/utils/cache/index.ts | 32 + src/utils/cache/memory.ts | 107 + src/utils/cache/persistent.ts | 132 + src/utils/cache/storageCache.ts | 109 + src/utils/cipher.ts | 55 + src/utils/color.ts | 145 + src/utils/dateUtil.ts | 17 + src/utils/domUtils.ts | 172 + src/utils/env.ts | 77 + src/utils/event/index.ts | 42 + src/utils/factory/createAsyncComponent.tsx | 63 + src/utils/file/base64Conver.ts | 41 + src/utils/file/download.ts | 88 + src/utils/helper/treeHelper.ts | 197 + src/utils/helper/tsxHelper.tsx | 35 + src/utils/http/axios/Axios.ts | 232 + src/utils/http/axios/axiosCancel.ts | 60 + src/utils/http/axios/axiosRetry.ts | 30 + src/utils/http/axios/axiosTransform.ts | 49 + src/utils/http/axios/checkStatus.ts | 76 + src/utils/http/axios/helper.ts | 45 + src/utils/http/axios/index.ts | 277 + src/utils/index.ts | 107 + src/utils/is.ts | 98 + src/utils/lib/echarts.ts | 49 + src/utils/log.ts | 9 + src/utils/mitt.ts | 101 + src/utils/propTypes.ts | 34 + src/utils/props.ts | 158 + src/utils/types.ts | 41 + src/utils/uuid.ts | 28 + .../analysis/components/GrowCard.vue | 38 + .../analysis/components/SalesProductPie.vue | 59 + .../analysis/components/SiteAnalysis.vue | 33 + .../analysis/components/VisitAnalysis.vue | 81 + .../analysis/components/VisitAnalysisBar.vue | 45 + .../analysis/components/VisitRadar.vue | 89 + .../analysis/components/VisitSource.vue | 76 + .../dashboard/analysis/components/props.ts | 16 + src/views/dashboard/analysis/data.ts | 43 + src/views/dashboard/analysis/index.vue | 25 + .../workbench/components/DynamicInfo.vue | 31 + .../workbench/components/ProjectCard.vue | 24 + .../workbench/components/QuickNav.vue | 15 + .../workbench/components/SaleRadar.vue | 89 + .../workbench/components/WorkbenchHeader.vue | 33 + .../dashboard/workbench/components/data.ts | 156 + src/views/dashboard/workbench/index.vue | 36 + src/views/demo/charts/Line.vue | 107 + src/views/demo/charts/Map.vue | 65 + src/views/demo/charts/Pie.vue | 125 + src/views/demo/charts/SaleRadar.vue | 90 + src/views/demo/charts/china.json | 839 ++ src/views/demo/charts/data.ts | 189 + src/views/demo/charts/map/Baidu.vue | 33 + src/views/demo/charts/map/Gaode.vue | 35 + src/views/demo/charts/map/Google.vue | 40 + src/views/demo/comp/button/index.vue | 106 + src/views/demo/comp/card-list/index.vue | 32 + src/views/demo/comp/count-to/index.vue | 34 + src/views/demo/comp/cropper/index.vue | 72 + src/views/demo/comp/desc/index.vue | 77 + src/views/demo/comp/drawer/Drawer1.vue | 6 + src/views/demo/comp/drawer/Drawer2.vue | 10 + src/views/demo/comp/drawer/Drawer3.vue | 27 + src/views/demo/comp/drawer/Drawer4.vue | 46 + src/views/demo/comp/drawer/Drawer5.vue | 9 + src/views/demo/comp/drawer/index.vue | 50 + src/views/demo/comp/flow-chart/dataTurbo.json | 240 + src/views/demo/comp/flow-chart/index.vue | 12 + src/views/demo/comp/lazy/TargetContent.vue | 12 + src/views/demo/comp/lazy/Transition.vue | 72 + src/views/demo/comp/lazy/index.vue | 47 + src/views/demo/comp/loading/index.vue | 91 + src/views/demo/comp/modal/Modal1.vue | 59 + src/views/demo/comp/modal/Modal2.vue | 10 + src/views/demo/comp/modal/Modal3.vue | 8 + src/views/demo/comp/modal/Modal4.vue | 73 + src/views/demo/comp/modal/index.vue | 89 + src/views/demo/comp/qrcode/index.vue | 103 + src/views/demo/comp/scroll/Action.vue | 49 + src/views/demo/comp/scroll/VirtualScroll.vue | 57 + src/views/demo/comp/scroll/index.vue | 26 + src/views/demo/comp/strength-meter/index.vue | 24 + src/views/demo/comp/transition/index.vue | 91 + src/views/demo/comp/upload/index.vue | 44 + src/views/demo/comp/verify/Rotate.vue | 23 + src/views/demo/comp/verify/index.vue | 83 + src/views/demo/editor/json/index.vue | 88 + src/views/demo/editor/markdown/Editor.vue | 50 + src/views/demo/editor/markdown/index.vue | 79 + src/views/demo/editor/tinymce/Editor.vue | 51 + src/views/demo/editor/tinymce/index.vue | 15 + src/views/demo/excel/ArrayExport.vue | 25 + src/views/demo/excel/CustomExport.vue | 30 + src/views/demo/excel/ImportExcel.vue | 46 + src/views/demo/excel/JsonExport.vue | 45 + src/views/demo/excel/data.ts | 59 + .../demo/feat/breadcrumb/ChildrenList.vue | 8 + .../feat/breadcrumb/ChildrenListDetail.vue | 8 + src/views/demo/feat/breadcrumb/FlatList.vue | 8 + .../demo/feat/breadcrumb/FlatListDetail.vue | 4 + src/views/demo/feat/context-menu/index.vue | 77 + src/views/demo/feat/copy/index.vue | 33 + src/views/demo/feat/download/imgBase64.ts | 2 + src/views/demo/feat/download/index.vue | 47 + src/views/demo/feat/full-screen/index.vue | 32 + src/views/demo/feat/icon/index.vue | 74 + src/views/demo/feat/img-preview/index.vue | 24 + src/views/demo/feat/menu-params/index.vue | 32 + src/views/demo/feat/msg/index.vue | 67 + src/views/demo/feat/print/index.vue | 35 + src/views/demo/feat/request-demo/index.vue | 22 + src/views/demo/feat/ripple/index.vue | 31 + src/views/demo/feat/session-timeout/index.vue | 43 + src/views/demo/feat/tab-params/index.vue | 19 + src/views/demo/feat/tabs/TabDetail.vue | 18 + src/views/demo/feat/tabs/index.vue | 49 + src/views/demo/feat/watermark/index.vue | 16 + src/views/demo/feat/ws/index.vue | 101 + src/views/demo/form/AdvancedForm.vue | 180 + src/views/demo/form/AppendForm.vue | 122 + src/views/demo/form/CustomerForm.vue | 75 + src/views/demo/form/DynamicForm.vue | 228 + src/views/demo/form/RefForm.vue | 165 + src/views/demo/form/RuleForm.vue | 241 + src/views/demo/form/TabsForm.vue | 118 + src/views/demo/form/UseForm.vue | 466 + src/views/demo/form/index.vue | 684 ++ src/views/demo/level/Menu111.vue | 10 + src/views/demo/level/Menu12.vue | 10 + src/views/demo/level/Menu2.vue | 10 + src/views/demo/main-out/index.vue | 6 + .../demo/page/account/center/Application.vue | 74 + .../demo/page/account/center/Article.vue | 79 + .../demo/page/account/center/Project.vue | 56 + src/views/demo/page/account/center/data.tsx | 124 + src/views/demo/page/account/center/index.vue | 155 + .../demo/page/account/setting/AccountBind.vue | 46 + .../demo/page/account/setting/BaseSetting.vue | 78 + .../demo/page/account/setting/MsgNotify.vue | 35 + .../page/account/setting/SecureSetting.vue | 40 + src/views/demo/page/account/setting/data.ts | 149 + src/views/demo/page/account/setting/index.vue | 61 + src/views/demo/page/desc/basic/data.tsx | 196 + src/views/demo/page/desc/basic/index.vue | 70 + src/views/demo/page/desc/high/data.tsx | 65 + src/views/demo/page/desc/high/index.vue | 109 + src/views/demo/page/form/basic/data.ts | 138 + src/views/demo/page/form/basic/index.vue | 60 + src/views/demo/page/form/high/PersonTable.vue | 125 + src/views/demo/page/form/high/data.ts | 149 + src/views/demo/page/form/high/index.vue | 61 + src/views/demo/page/form/step/Step1.vue | 90 + src/views/demo/page/form/step/Step2.vue | 65 + src/views/demo/page/form/step/Step3.vue | 38 + src/views/demo/page/form/step/data.tsx | 78 + src/views/demo/page/form/step/index.vue | 70 + src/views/demo/page/list/basic/data.tsx | 17 + src/views/demo/page/list/basic/index.vue | 146 + src/views/demo/page/list/card/data.tsx | 14 + src/views/demo/page/list/card/index.vue | 89 + src/views/demo/page/list/search/data.tsx | 37 + src/views/demo/page/list/search/index.vue | 108 + src/views/demo/page/result/fail/index.vue | 47 + src/views/demo/page/result/success/index.vue | 53 + .../demo/permission/CurrentPermissionMode.vue | 20 + src/views/demo/permission/back/Btn.vue | 76 + src/views/demo/permission/back/index.vue | 51 + src/views/demo/permission/front/AuthPageA.vue | 16 + src/views/demo/permission/front/AuthPageB.vue | 16 + src/views/demo/permission/front/Btn.vue | 73 + src/views/demo/permission/front/index.vue | 46 + src/views/demo/setup/index.vue | 36 + .../demo/system/account/AccountDetail.vue | 56 + .../demo/system/account/AccountModal.vue | 66 + src/views/demo/system/account/DeptTree.vue | 35 + src/views/demo/system/account/account.data.ts | 127 + src/views/demo/system/account/index.vue | 119 + src/views/demo/system/dept/DeptModal.vue | 54 + src/views/demo/system/dept/dept.data.ts | 108 + src/views/demo/system/dept/index.vue | 86 + src/views/demo/system/menu/MenuDrawer.vue | 55 + src/views/demo/system/menu/index.vue | 94 + src/views/demo/system/menu/menu.data.ts | 202 + src/views/demo/system/password/index.vue | 36 + src/views/demo/system/password/pwd.data.ts | 46 + src/views/demo/system/role/RoleDrawer.vue | 67 + src/views/demo/system/role/index.vue | 83 + src/views/demo/system/role/role.data.ts | 124 + src/views/demo/table/AuthColumn.vue | 142 + src/views/demo/table/Basic.vue | 63 + src/views/demo/table/CustomerCell.vue | 98 + src/views/demo/table/EditCellTable.vue | 258 + src/views/demo/table/EditRowTable.vue | 301 + src/views/demo/table/ExpandTable.vue | 65 + src/views/demo/table/FetchTable.vue | 32 + src/views/demo/table/FixedColumn.vue | 84 + src/views/demo/table/FixedHeight.vue | 37 + src/views/demo/table/FooterTable.vue | 40 + src/views/demo/table/FormTable.vue | 71 + src/views/demo/table/MergeHeader.vue | 18 + src/views/demo/table/MultipleHeader.vue | 16 + src/views/demo/table/RefTable.vue | 104 + .../demo/table/ResizeParentHeightTable.vue | 67 + src/views/demo/table/TreeTable.vue | 34 + src/views/demo/table/UseTable.vue | 124 + src/views/demo/table/tableData.tsx | 292 + src/views/demo/tree/ActionTree.vue | 116 + src/views/demo/tree/EditTree.vue | 107 + src/views/demo/tree/data.ts | 118 + src/views/demo/tree/index.vue | 111 + src/views/sys/about/index.vue | 98 + src/views/sys/error-log/DetailModal.vue | 26 + src/views/sys/error-log/data.tsx | 67 + src/views/sys/error-log/index.vue | 97 + src/views/sys/exception/Exception.vue | 142 + src/views/sys/exception/index.ts | 1 + src/views/sys/iframe/FrameBlank.vue | 6 + src/views/sys/iframe/index.vue | 85 + src/views/sys/lock/LockPage.vue | 216 + src/views/sys/lock/index.vue | 13 + src/views/sys/lock/useNow.ts | 60 + src/views/sys/login/ForgetPasswordForm.vue | 56 + src/views/sys/login/Login.vue | 210 + src/views/sys/login/LoginForm.vue | 136 + src/views/sys/login/LoginFormTitle.vue | 25 + src/views/sys/login/MobileForm.vue | 53 + src/views/sys/login/QrCodeForm.vue | 27 + src/views/sys/login/RegisterForm.vue | 78 + src/views/sys/login/SessionTimeoutLogin.vue | 53 + src/views/sys/login/useLogin.ts | 124 + src/views/sys/redirect/index.vue | 30 + stylelint.config.js | 89 + tests/server/README.md | 15 + tests/server/controller/FileController.ts | 18 + tests/server/controller/UserController.ts | 15 + tests/server/ecosystem.config.js | 18 + tests/server/index.ts | 63 + tests/server/nodemon.json | 8 + tests/server/package.json | 36 + tests/server/routes.ts | 23 + tests/server/service/FileService.ts | 54 + tests/server/service/UserService.ts | 25 + tests/server/tsconfig.json | 15 + tests/server/utils.ts | 9 + tsconfig.json | 43 + vite.config.ts | 99 + windi.config.ts | 74 + 837 files changed, 72885 insertions(+) create mode 100644 .editorconfig create mode 100644 .env create mode 100644 .env.development create mode 100644 .env.production create mode 100644 .env.test create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .gitpod.yml create mode 100644 .hintrc create mode 100644 .husky/commit-msg create mode 100644 .husky/common.sh create mode 100644 .husky/pre-commit create mode 100644 .lintstagedrc.js create mode 100644 .prettierignore create mode 100644 .stylelintignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 CNAME create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build/config/themeConfig.ts create mode 100644 build/constant.ts create mode 100644 build/generate/generateModifyVars.ts create mode 100644 build/generate/icon/index.ts create mode 100644 build/getConfigFileName.ts create mode 100644 build/script/buildConf.ts create mode 100644 build/script/postBuild.ts create mode 100644 build/utils.ts create mode 100644 build/vite/optimize.ts create mode 100644 build/vite/plugin/compress.ts create mode 100644 build/vite/plugin/html.ts create mode 100644 build/vite/plugin/index.ts create mode 100644 build/vite/plugin/mock.ts create mode 100644 build/vite/plugin/pwa.ts create mode 100644 build/vite/plugin/styleImport.ts create mode 100644 build/vite/plugin/svgSprite.ts create mode 100644 build/vite/plugin/theme.ts create mode 100644 build/vite/plugin/visualizer.ts create mode 100644 build/vite/proxy.ts create mode 100644 commitlint.config.js create mode 100644 index.html create mode 100644 mock/_createProductionServer.ts create mode 100644 mock/_util.ts create mode 100644 mock/demo/account.ts create mode 100644 mock/demo/api-cascader.ts create mode 100644 mock/demo/select-demo.ts create mode 100644 mock/demo/system.ts create mode 100644 mock/demo/table-demo.ts create mode 100644 mock/demo/tree-demo.ts create mode 100644 mock/sys/menu.ts create mode 100644 mock/sys/user.ts create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 postcss.config.js create mode 100644 prettier.config.js create mode 100644 public/favicon.ico create mode 100644 public/resource/img/logo.png create mode 100644 public/resource/img/pwa-192x192.png create mode 100644 public/resource/img/pwa-512x512.png create mode 100644 public/resource/tinymce/langs/en.js create mode 100644 public/resource/tinymce/langs/zh_CN.js create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/content.inline.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/content.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/skin.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide/content.inline.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide/content.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide/content.mobile.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff create mode 100644 public/resource/tinymce/skins/ui/oxide/skin.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css create mode 100644 public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css create mode 100644 src/App.vue create mode 100644 src/api/demo/account.ts create mode 100644 src/api/demo/cascader.ts create mode 100644 src/api/demo/error.ts create mode 100644 src/api/demo/model/accountModel.ts create mode 100644 src/api/demo/model/areaModel.ts create mode 100644 src/api/demo/model/optionsModel.ts create mode 100644 src/api/demo/model/systemModel.ts create mode 100644 src/api/demo/model/tableModel.ts create mode 100644 src/api/demo/select.ts create mode 100644 src/api/demo/system.ts create mode 100644 src/api/demo/table.ts create mode 100644 src/api/demo/tree.ts create mode 100644 src/api/model/baseModel.ts create mode 100644 src/api/sys/menu.ts create mode 100644 src/api/sys/model/menuModel.ts create mode 100644 src/api/sys/model/uploadModel.ts create mode 100644 src/api/sys/model/userModel.ts create mode 100644 src/api/sys/upload.ts create mode 100644 src/api/sys/user.ts create mode 100644 src/assets/icons/download-count.svg create mode 100644 src/assets/icons/dynamic-avatar-1.svg create mode 100644 src/assets/icons/dynamic-avatar-2.svg create mode 100644 src/assets/icons/dynamic-avatar-3.svg create mode 100644 src/assets/icons/dynamic-avatar-4.svg create mode 100644 src/assets/icons/dynamic-avatar-5.svg create mode 100644 src/assets/icons/dynamic-avatar-6.svg create mode 100644 src/assets/icons/moon.svg create mode 100644 src/assets/icons/sun.svg create mode 100644 src/assets/icons/test.svg create mode 100644 src/assets/icons/total-sales.svg create mode 100644 src/assets/icons/transaction.svg create mode 100644 src/assets/icons/visit-count.svg create mode 100644 src/assets/images/demo.png create mode 100644 src/assets/images/header.jpg create mode 100644 src/assets/images/logo.png create mode 100644 src/assets/svg/illustration.svg create mode 100644 src/assets/svg/login-bg-dark.svg create mode 100644 src/assets/svg/login-bg.svg create mode 100644 src/assets/svg/login-box-bg.svg create mode 100644 src/assets/svg/net-error.svg create mode 100644 src/assets/svg/no-data.svg create mode 100644 src/assets/svg/preview/p-rotate.svg create mode 100644 src/assets/svg/preview/resume.svg create mode 100644 src/assets/svg/preview/scale.svg create mode 100644 src/assets/svg/preview/unrotate.svg create mode 100644 src/assets/svg/preview/unscale.svg create mode 100644 src/components/Application/index.ts create mode 100644 src/components/Application/src/AppDarkModeToggle.vue create mode 100644 src/components/Application/src/AppLocalePicker.vue create mode 100644 src/components/Application/src/AppLogo.vue create mode 100644 src/components/Application/src/AppProvider.vue create mode 100644 src/components/Application/src/AppSizePicker.vue create mode 100644 src/components/Application/src/search/AppSearch.vue create mode 100644 src/components/Application/src/search/AppSearchFooter.vue create mode 100644 src/components/Application/src/search/AppSearchKeyItem.vue create mode 100644 src/components/Application/src/search/AppSearchModal.vue create mode 100644 src/components/Application/src/search/useMenuSearch.ts create mode 100644 src/components/Application/src/useAppContext.ts create mode 100644 src/components/Authority/index.ts create mode 100644 src/components/Authority/src/Authority.vue create mode 100644 src/components/Basic/index.ts create mode 100644 src/components/Basic/src/BasicArrow.vue create mode 100644 src/components/Basic/src/BasicHelp.vue create mode 100644 src/components/Basic/src/BasicTitle.vue create mode 100644 src/components/Button/index.ts create mode 100644 src/components/Button/src/BasicButton.vue create mode 100644 src/components/Button/src/PopConfirmButton.vue create mode 100644 src/components/Button/src/props.ts create mode 100644 src/components/CardList/index.ts create mode 100644 src/components/CardList/src/CardList.vue create mode 100644 src/components/CardList/src/data.ts create mode 100644 src/components/CodeEditor/index.ts create mode 100644 src/components/CodeEditor/src/CodeEditor.vue create mode 100644 src/components/CodeEditor/src/codemirror/CodeMirror.vue create mode 100644 src/components/CodeEditor/src/codemirror/codeMirror.ts create mode 100644 src/components/CodeEditor/src/codemirror/codemirror.css create mode 100644 src/components/CodeEditor/src/json-preview/JsonPreview.vue create mode 100644 src/components/CodeEditor/src/typing.ts create mode 100644 src/components/Container/index.ts create mode 100644 src/components/Container/src/LazyContainer.vue create mode 100644 src/components/Container/src/ScrollContainer.vue create mode 100644 src/components/Container/src/collapse/CollapseContainer.vue create mode 100644 src/components/Container/src/collapse/CollapseHeader.vue create mode 100644 src/components/Container/src/typing.ts create mode 100644 src/components/ContextMenu/index.ts create mode 100644 src/components/ContextMenu/src/ContextMenu.vue create mode 100644 src/components/ContextMenu/src/createContextMenu.ts create mode 100644 src/components/ContextMenu/src/typing.ts create mode 100644 src/components/CountDown/index.ts create mode 100644 src/components/CountDown/src/CountButton.vue create mode 100644 src/components/CountDown/src/CountdownInput.vue create mode 100644 src/components/CountDown/src/useCountdown.ts create mode 100644 src/components/CountTo/index.ts create mode 100644 src/components/CountTo/src/CountTo.vue create mode 100644 src/components/Cropper/index.ts create mode 100644 src/components/Cropper/src/CopperModal.vue create mode 100644 src/components/Cropper/src/Cropper.vue create mode 100644 src/components/Cropper/src/CropperAvatar.vue create mode 100644 src/components/Cropper/src/typing.ts create mode 100644 src/components/Description/index.ts create mode 100644 src/components/Description/src/Description.vue create mode 100644 src/components/Description/src/typing.ts create mode 100644 src/components/Description/src/useDescription.ts create mode 100644 src/components/Drawer/index.ts create mode 100644 src/components/Drawer/src/BasicDrawer.vue create mode 100644 src/components/Drawer/src/components/DrawerFooter.vue create mode 100644 src/components/Drawer/src/components/DrawerHeader.vue create mode 100644 src/components/Drawer/src/props.ts create mode 100644 src/components/Drawer/src/typing.ts create mode 100644 src/components/Drawer/src/useDrawer.ts create mode 100644 src/components/Dropdown/index.ts create mode 100644 src/components/Dropdown/src/Dropdown.vue create mode 100644 src/components/Dropdown/src/typing.ts create mode 100644 src/components/Excel/index.ts create mode 100644 src/components/Excel/src/Export2Excel.ts create mode 100644 src/components/Excel/src/ExportExcelModal.vue create mode 100644 src/components/Excel/src/ImportExcel.vue create mode 100644 src/components/Excel/src/typing.ts create mode 100644 src/components/FlowChart/index.ts create mode 100644 src/components/FlowChart/src/FlowChart.vue create mode 100644 src/components/FlowChart/src/FlowChartToolbar.vue create mode 100644 src/components/FlowChart/src/adpterForTurbo.ts create mode 100644 src/components/FlowChart/src/config.ts create mode 100644 src/components/FlowChart/src/enum.ts create mode 100644 src/components/FlowChart/src/types.ts create mode 100644 src/components/FlowChart/src/useFlowContext.ts create mode 100644 src/components/Form/index.ts create mode 100644 src/components/Form/src/BasicForm.vue create mode 100644 src/components/Form/src/componentMap.ts create mode 100644 src/components/Form/src/components/ApiCascader.vue create mode 100644 src/components/Form/src/components/ApiRadioGroup.vue create mode 100644 src/components/Form/src/components/ApiSelect.vue create mode 100644 src/components/Form/src/components/ApiTransfer.vue create mode 100644 src/components/Form/src/components/ApiTree.vue create mode 100644 src/components/Form/src/components/ApiTreeSelect.vue create mode 100644 src/components/Form/src/components/FormAction.vue create mode 100644 src/components/Form/src/components/FormItem.vue create mode 100644 src/components/Form/src/components/RadioButtonGroup.vue create mode 100644 src/components/Form/src/helper.ts create mode 100644 src/components/Form/src/hooks/useAdvanced.ts create mode 100644 src/components/Form/src/hooks/useAutoFocus.ts create mode 100644 src/components/Form/src/hooks/useComponentRegister.ts create mode 100644 src/components/Form/src/hooks/useForm.ts create mode 100644 src/components/Form/src/hooks/useFormContext.ts create mode 100644 src/components/Form/src/hooks/useFormEvents.ts create mode 100644 src/components/Form/src/hooks/useFormValues.ts create mode 100644 src/components/Form/src/hooks/useLabelWidth.ts create mode 100644 src/components/Form/src/props.ts create mode 100644 src/components/Form/src/types/form.ts create mode 100644 src/components/Form/src/types/formItem.ts create mode 100644 src/components/Form/src/types/hooks.ts create mode 100644 src/components/Form/src/types/index.ts create mode 100644 src/components/Icon/data/icons.data.ts create mode 100644 src/components/Icon/index.ts create mode 100644 src/components/Icon/src/Icon.vue create mode 100644 src/components/Icon/src/IconPicker.vue create mode 100644 src/components/Icon/src/SvgIcon.vue create mode 100644 src/components/Loading/index.ts create mode 100644 src/components/Loading/src/Loading.vue create mode 100644 src/components/Loading/src/createLoading.ts create mode 100644 src/components/Loading/src/typing.ts create mode 100644 src/components/Loading/src/useLoading.ts create mode 100644 src/components/Markdown/index.ts create mode 100644 src/components/Markdown/src/Markdown.vue create mode 100644 src/components/Markdown/src/MarkdownViewer.vue create mode 100644 src/components/Markdown/src/getTheme.ts create mode 100644 src/components/Markdown/src/typing.ts create mode 100644 src/components/Menu/index.ts create mode 100644 src/components/Menu/src/BasicMenu.vue create mode 100644 src/components/Menu/src/components/BasicMenuItem.vue create mode 100644 src/components/Menu/src/components/BasicSubMenuItem.vue create mode 100644 src/components/Menu/src/components/MenuItemContent.vue create mode 100644 src/components/Menu/src/index.less create mode 100644 src/components/Menu/src/props.ts create mode 100644 src/components/Menu/src/types.ts create mode 100644 src/components/Menu/src/useOpenKeys.ts create mode 100644 src/components/Modal/index.ts create mode 100644 src/components/Modal/src/BasicModal.vue create mode 100644 src/components/Modal/src/components/Modal.tsx create mode 100644 src/components/Modal/src/components/ModalClose.vue create mode 100644 src/components/Modal/src/components/ModalFooter.vue create mode 100644 src/components/Modal/src/components/ModalHeader.vue create mode 100644 src/components/Modal/src/components/ModalWrapper.vue create mode 100644 src/components/Modal/src/hooks/useModal.ts create mode 100644 src/components/Modal/src/hooks/useModalContext.ts create mode 100644 src/components/Modal/src/hooks/useModalDrag.ts create mode 100644 src/components/Modal/src/hooks/useModalFullScreen.ts create mode 100644 src/components/Modal/src/index.less create mode 100644 src/components/Modal/src/props.ts create mode 100644 src/components/Modal/src/typing.ts create mode 100644 src/components/Page/index.ts create mode 100644 src/components/Page/src/PageFooter.vue create mode 100644 src/components/Page/src/PageWrapper.vue create mode 100644 src/components/Preview/index.ts create mode 100644 src/components/Preview/src/Functional.vue create mode 100644 src/components/Preview/src/Preview.vue create mode 100644 src/components/Preview/src/functional.ts create mode 100644 src/components/Preview/src/typing.ts create mode 100644 src/components/Qrcode/index.ts create mode 100644 src/components/Qrcode/src/Qrcode.vue create mode 100644 src/components/Qrcode/src/drawCanvas.ts create mode 100644 src/components/Qrcode/src/drawLogo.ts create mode 100644 src/components/Qrcode/src/qrcodePlus.ts create mode 100644 src/components/Qrcode/src/toCanvas.ts create mode 100644 src/components/Qrcode/src/typing.ts create mode 100644 src/components/Scrollbar/index.ts create mode 100644 src/components/Scrollbar/src/Scrollbar.vue create mode 100644 src/components/Scrollbar/src/bar.ts create mode 100644 src/components/Scrollbar/src/types.d.ts create mode 100644 src/components/Scrollbar/src/util.ts create mode 100644 src/components/SimpleMenu/index.ts create mode 100644 src/components/SimpleMenu/src/SimpleMenu.vue create mode 100644 src/components/SimpleMenu/src/SimpleMenuTag.vue create mode 100644 src/components/SimpleMenu/src/SimpleSubMenu.vue create mode 100644 src/components/SimpleMenu/src/components/Menu.vue create mode 100644 src/components/SimpleMenu/src/components/MenuCollapseTransition.vue create mode 100644 src/components/SimpleMenu/src/components/MenuItem.vue create mode 100644 src/components/SimpleMenu/src/components/SubMenuItem.vue create mode 100644 src/components/SimpleMenu/src/components/menu.less create mode 100644 src/components/SimpleMenu/src/components/types.ts create mode 100644 src/components/SimpleMenu/src/components/useMenu.ts create mode 100644 src/components/SimpleMenu/src/components/useSimpleMenuContext.ts create mode 100644 src/components/SimpleMenu/src/index.less create mode 100644 src/components/SimpleMenu/src/types.ts create mode 100644 src/components/SimpleMenu/src/useOpenKeys.ts create mode 100644 src/components/StrengthMeter/index.ts create mode 100644 src/components/StrengthMeter/src/StrengthMeter.vue create mode 100644 src/components/Table/index.ts create mode 100644 src/components/Table/src/BasicTable.vue create mode 100644 src/components/Table/src/componentMap.ts create mode 100644 src/components/Table/src/components/EditTableHeaderIcon.vue create mode 100644 src/components/Table/src/components/HeaderCell.vue create mode 100644 src/components/Table/src/components/TableAction.vue create mode 100644 src/components/Table/src/components/TableFooter.vue create mode 100644 src/components/Table/src/components/TableHeader.vue create mode 100644 src/components/Table/src/components/TableImg.vue create mode 100644 src/components/Table/src/components/TableTitle.vue create mode 100644 src/components/Table/src/components/editable/CellComponent.ts create mode 100644 src/components/Table/src/components/editable/EditableCell.vue create mode 100644 src/components/Table/src/components/editable/helper.ts create mode 100644 src/components/Table/src/components/editable/index.ts create mode 100644 src/components/Table/src/components/settings/ColumnSetting.vue create mode 100644 src/components/Table/src/components/settings/FullScreenSetting.vue create mode 100644 src/components/Table/src/components/settings/RedoSetting.vue create mode 100644 src/components/Table/src/components/settings/SizeSetting.vue create mode 100644 src/components/Table/src/components/settings/index.vue create mode 100644 src/components/Table/src/const.ts create mode 100644 src/components/Table/src/hooks/useColumns.ts create mode 100644 src/components/Table/src/hooks/useCustomRow.ts create mode 100644 src/components/Table/src/hooks/useDataSource.ts create mode 100644 src/components/Table/src/hooks/useLoading.ts create mode 100644 src/components/Table/src/hooks/usePagination.tsx create mode 100644 src/components/Table/src/hooks/useRowSelection.ts create mode 100644 src/components/Table/src/hooks/useScrollTo.ts create mode 100644 src/components/Table/src/hooks/useTable.ts create mode 100644 src/components/Table/src/hooks/useTableContext.ts create mode 100644 src/components/Table/src/hooks/useTableExpand.ts create mode 100644 src/components/Table/src/hooks/useTableFooter.ts create mode 100644 src/components/Table/src/hooks/useTableForm.ts create mode 100644 src/components/Table/src/hooks/useTableHeader.ts create mode 100644 src/components/Table/src/hooks/useTableScroll.ts create mode 100644 src/components/Table/src/hooks/useTableStyle.ts create mode 100644 src/components/Table/src/props.ts create mode 100644 src/components/Table/src/types/column.ts create mode 100644 src/components/Table/src/types/componentType.ts create mode 100644 src/components/Table/src/types/pagination.ts create mode 100644 src/components/Table/src/types/table.ts create mode 100644 src/components/Table/src/types/tableAction.ts create mode 100644 src/components/Tinymce/index.ts create mode 100644 src/components/Tinymce/src/Editor.vue create mode 100644 src/components/Tinymce/src/ImgUpload.vue create mode 100644 src/components/Tinymce/src/helper.ts create mode 100644 src/components/Tinymce/src/tinymce.ts create mode 100644 src/components/Transition/index.ts create mode 100644 src/components/Transition/src/CollapseTransition.vue create mode 100644 src/components/Transition/src/CreateTransition.tsx create mode 100644 src/components/Transition/src/ExpandTransition.ts create mode 100644 src/components/Tree/index.ts create mode 100644 src/components/Tree/src/BasicTree.vue create mode 100644 src/components/Tree/src/TreeIcon.ts create mode 100644 src/components/Tree/src/components/TreeHeader.vue create mode 100644 src/components/Tree/src/hooks/useTree.ts create mode 100644 src/components/Tree/src/types/tree.ts create mode 100644 src/components/Tree/style/index.less create mode 100644 src/components/Tree/style/index.ts create mode 100644 src/components/Upload/index.ts create mode 100644 src/components/Upload/src/BasicUpload.vue create mode 100644 src/components/Upload/src/FileList.vue create mode 100644 src/components/Upload/src/ThumbUrl.vue create mode 100644 src/components/Upload/src/UploadModal.vue create mode 100644 src/components/Upload/src/UploadPreviewModal.vue create mode 100644 src/components/Upload/src/data.tsx create mode 100644 src/components/Upload/src/helper.ts create mode 100644 src/components/Upload/src/props.ts create mode 100644 src/components/Upload/src/typing.ts create mode 100644 src/components/Upload/src/useUpload.ts create mode 100644 src/components/Verify/index.ts create mode 100644 src/components/Verify/src/DragVerify.vue create mode 100644 src/components/Verify/src/ImgRotate.vue create mode 100644 src/components/Verify/src/props.ts create mode 100644 src/components/Verify/src/typing.ts create mode 100644 src/components/VirtualScroll/index.ts create mode 100644 src/components/VirtualScroll/src/VirtualScroll.vue create mode 100644 src/components/registerGlobComp.ts create mode 100644 src/design/ant/btn.less create mode 100644 src/design/ant/index.less create mode 100644 src/design/ant/input.less create mode 100644 src/design/ant/pagination.less create mode 100644 src/design/ant/table.less create mode 100644 src/design/color.less create mode 100644 src/design/config.less create mode 100644 src/design/index.less create mode 100644 src/design/public.less create mode 100644 src/design/theme.less create mode 100644 src/design/transition/base.less create mode 100644 src/design/transition/fade.less create mode 100644 src/design/transition/index.less create mode 100644 src/design/transition/scale.less create mode 100644 src/design/transition/scroll.less create mode 100644 src/design/transition/slide.less create mode 100644 src/design/transition/zoom.less create mode 100644 src/design/var/breakpoint.less create mode 100644 src/design/var/easing.less create mode 100644 src/design/var/index.less create mode 100644 src/directives/clickOutside.ts create mode 100644 src/directives/index.ts create mode 100644 src/directives/loading.ts create mode 100644 src/directives/permission.ts create mode 100644 src/directives/repeatClick.ts create mode 100644 src/directives/ripple/index.less create mode 100644 src/directives/ripple/index.ts create mode 100644 src/enums/appEnum.ts create mode 100644 src/enums/breakpointEnum.ts create mode 100644 src/enums/cacheEnum.ts create mode 100644 src/enums/exceptionEnum.ts create mode 100644 src/enums/httpEnum.ts create mode 100644 src/enums/menuEnum.ts create mode 100644 src/enums/pageEnum.ts create mode 100644 src/enums/roleEnum.ts create mode 100644 src/enums/sizeEnum.ts create mode 100644 src/hooks/component/useFormItem.ts create mode 100644 src/hooks/component/usePageContext.ts create mode 100644 src/hooks/core/onMountedOrActivated.ts create mode 100644 src/hooks/core/useAttrs.ts create mode 100644 src/hooks/core/useContext.ts create mode 100644 src/hooks/core/useLockFn.ts create mode 100644 src/hooks/core/useRefs.ts create mode 100644 src/hooks/core/useTimeout.ts create mode 100644 src/hooks/event/useBreakpoint.ts create mode 100644 src/hooks/event/useEventListener.ts create mode 100644 src/hooks/event/useIntersectionObserver.ts create mode 100644 src/hooks/event/useScroll.ts create mode 100644 src/hooks/event/useScrollTo.ts create mode 100644 src/hooks/event/useWindowSizeFn.ts create mode 100644 src/hooks/setting/index.ts create mode 100644 src/hooks/setting/useHeaderSetting.ts create mode 100644 src/hooks/setting/useMenuSetting.ts create mode 100644 src/hooks/setting/useMultipleTabSetting.ts create mode 100644 src/hooks/setting/useRootSetting.ts create mode 100644 src/hooks/setting/useTransitionSetting.ts create mode 100644 src/hooks/web/useAppInject.ts create mode 100644 src/hooks/web/useContentHeight.ts create mode 100644 src/hooks/web/useContextMenu.ts create mode 100644 src/hooks/web/useCopyToClipboard.ts create mode 100644 src/hooks/web/useDesign.ts create mode 100644 src/hooks/web/useECharts.ts create mode 100644 src/hooks/web/useFullContent.ts create mode 100644 src/hooks/web/useI18n.ts create mode 100644 src/hooks/web/useLockPage.ts create mode 100644 src/hooks/web/useMessage.tsx create mode 100644 src/hooks/web/usePage.ts create mode 100644 src/hooks/web/usePagination.ts create mode 100644 src/hooks/web/usePermission.ts create mode 100644 src/hooks/web/useScript.ts create mode 100644 src/hooks/web/useSortable.ts create mode 100644 src/hooks/web/useTabs.ts create mode 100644 src/hooks/web/useTitle.ts create mode 100644 src/hooks/web/useWatermark.ts create mode 100644 src/layouts/default/content/index.vue create mode 100644 src/layouts/default/content/useContentContext.ts create mode 100644 src/layouts/default/content/useContentViewHeight.ts create mode 100644 src/layouts/default/feature/index.vue create mode 100644 src/layouts/default/footer/index.vue create mode 100644 src/layouts/default/header/MultipleHeader.vue create mode 100644 src/layouts/default/header/components/Breadcrumb.vue create mode 100644 src/layouts/default/header/components/ErrorAction.vue create mode 100644 src/layouts/default/header/components/FullScreen.vue create mode 100644 src/layouts/default/header/components/index.ts create mode 100644 src/layouts/default/header/components/lock/LockModal.vue create mode 100644 src/layouts/default/header/components/notify/NoticeList.vue create mode 100644 src/layouts/default/header/components/notify/data.ts create mode 100644 src/layouts/default/header/components/notify/index.vue create mode 100644 src/layouts/default/header/components/user-dropdown/DropMenuItem.vue create mode 100644 src/layouts/default/header/components/user-dropdown/index.vue create mode 100644 src/layouts/default/header/index.less create mode 100644 src/layouts/default/header/index.vue create mode 100644 src/layouts/default/index.vue create mode 100644 src/layouts/default/menu/index.vue create mode 100644 src/layouts/default/menu/useLayoutMenu.ts create mode 100644 src/layouts/default/setting/SettingDrawer.tsx create mode 100644 src/layouts/default/setting/components/InputNumberItem.vue create mode 100644 src/layouts/default/setting/components/SelectItem.vue create mode 100644 src/layouts/default/setting/components/SettingFooter.vue create mode 100644 src/layouts/default/setting/components/SwitchItem.vue create mode 100644 src/layouts/default/setting/components/ThemeColorPicker.vue create mode 100644 src/layouts/default/setting/components/TypePicker.vue create mode 100644 src/layouts/default/setting/components/index.ts create mode 100644 src/layouts/default/setting/enum.ts create mode 100644 src/layouts/default/setting/handler.ts create mode 100644 src/layouts/default/setting/index.vue create mode 100644 src/layouts/default/sider/DragBar.vue create mode 100644 src/layouts/default/sider/LayoutSider.vue create mode 100644 src/layouts/default/sider/MixSider.vue create mode 100644 src/layouts/default/sider/index.vue create mode 100644 src/layouts/default/sider/useLayoutSider.ts create mode 100644 src/layouts/default/tabs/components/FoldButton.vue create mode 100644 src/layouts/default/tabs/components/TabContent.vue create mode 100644 src/layouts/default/tabs/components/TabRedo.vue create mode 100644 src/layouts/default/tabs/index.less create mode 100644 src/layouts/default/tabs/index.vue create mode 100644 src/layouts/default/tabs/types.ts create mode 100644 src/layouts/default/tabs/useMultipleTabs.ts create mode 100644 src/layouts/default/tabs/useTabDropdown.ts create mode 100644 src/layouts/default/trigger/HeaderTrigger.vue create mode 100644 src/layouts/default/trigger/SiderTrigger.vue create mode 100644 src/layouts/default/trigger/index.vue create mode 100644 src/layouts/iframe/index.vue create mode 100644 src/layouts/iframe/useFrameKeepAlive.ts create mode 100644 src/layouts/page/index.vue create mode 100644 src/layouts/page/transition.ts create mode 100644 src/locales/helper.ts create mode 100644 src/locales/lang/en.ts create mode 100644 src/locales/lang/en/common.ts create mode 100644 src/locales/lang/en/component.ts create mode 100644 src/locales/lang/en/layout.ts create mode 100644 src/locales/lang/en/routes/basic.ts create mode 100644 src/locales/lang/en/routes/dashboard.ts create mode 100644 src/locales/lang/en/routes/demo.ts create mode 100644 src/locales/lang/en/sys.ts create mode 100644 src/locales/lang/zh-CN/antdLocale/DatePicker.ts create mode 100644 src/locales/lang/zh-CN/common.ts create mode 100644 src/locales/lang/zh-CN/component.ts create mode 100644 src/locales/lang/zh-CN/layout.ts create mode 100644 src/locales/lang/zh-CN/routes/basic.ts create mode 100644 src/locales/lang/zh-CN/routes/dashboard.ts create mode 100644 src/locales/lang/zh-CN/routes/demo.ts create mode 100644 src/locales/lang/zh-CN/sys.ts create mode 100644 src/locales/lang/zh_CN.ts create mode 100644 src/locales/setupI18n.ts create mode 100644 src/locales/useLocale.ts create mode 100644 src/logics/error-handle/index.ts create mode 100644 src/logics/initAppConfig.ts create mode 100644 src/logics/mitt/routeChange.ts create mode 100644 src/logics/theme/dark.ts create mode 100644 src/logics/theme/index.ts create mode 100644 src/logics/theme/updateBackground.ts create mode 100644 src/logics/theme/updateColorWeak.ts create mode 100644 src/logics/theme/updateGrayMode.ts create mode 100644 src/logics/theme/util.ts create mode 100644 src/main.ts create mode 100644 src/router/constant.ts create mode 100644 src/router/guard/index.ts create mode 100644 src/router/guard/paramMenuGuard.ts create mode 100644 src/router/guard/permissionGuard.ts create mode 100644 src/router/guard/stateGuard.ts create mode 100644 src/router/helper/menuHelper.ts create mode 100644 src/router/helper/routeHelper.ts create mode 100644 src/router/index.ts create mode 100644 src/router/menus/index.ts create mode 100644 src/router/routes/basic.ts create mode 100644 src/router/routes/index.ts create mode 100644 src/router/routes/mainOut.ts create mode 100644 src/router/routes/modules/about.ts create mode 100644 src/router/routes/modules/dashboard.ts create mode 100644 src/router/routes/modules/demo/charts.ts create mode 100644 src/router/routes/modules/demo/comp.ts create mode 100644 src/router/routes/modules/demo/feat.ts create mode 100644 src/router/routes/modules/demo/flow.ts create mode 100644 src/router/routes/modules/demo/iframe.ts create mode 100644 src/router/routes/modules/demo/level.ts create mode 100644 src/router/routes/modules/demo/page.ts create mode 100644 src/router/routes/modules/demo/permission.ts create mode 100644 src/router/routes/modules/demo/setup.ts create mode 100644 src/router/routes/modules/demo/system.ts create mode 100644 src/router/types.ts create mode 100644 src/settings/componentSetting.ts create mode 100644 src/settings/designSetting.ts create mode 100644 src/settings/encryptionSetting.ts create mode 100644 src/settings/localeSetting.ts create mode 100644 src/settings/projectSetting.ts create mode 100644 src/settings/siteSetting.ts create mode 100644 src/settings/sizeSetting.ts create mode 100644 src/store/index.ts create mode 100644 src/store/modules/app.ts create mode 100644 src/store/modules/errorLog.ts create mode 100644 src/store/modules/locale.ts create mode 100644 src/store/modules/lock.ts create mode 100644 src/store/modules/multipleTab.ts create mode 100644 src/store/modules/permission.ts create mode 100644 src/store/modules/user.ts create mode 100644 src/types/axios.d.ts create mode 100644 src/types/config.d.ts create mode 100644 src/types/global.d.ts create mode 100644 src/types/index.d.ts create mode 100644 src/types/module.d.ts create mode 100644 src/types/store.d.ts create mode 100644 src/types/utils.d.ts create mode 100644 src/types/vue-router.d.ts create mode 100644 src/utils/auth/index.ts create mode 100644 src/utils/bem.ts create mode 100644 src/utils/cache/index.ts create mode 100644 src/utils/cache/memory.ts create mode 100644 src/utils/cache/persistent.ts create mode 100644 src/utils/cache/storageCache.ts create mode 100644 src/utils/cipher.ts create mode 100644 src/utils/color.ts create mode 100644 src/utils/dateUtil.ts create mode 100644 src/utils/domUtils.ts create mode 100644 src/utils/env.ts create mode 100644 src/utils/event/index.ts create mode 100644 src/utils/factory/createAsyncComponent.tsx create mode 100644 src/utils/file/base64Conver.ts create mode 100644 src/utils/file/download.ts create mode 100644 src/utils/helper/treeHelper.ts create mode 100644 src/utils/helper/tsxHelper.tsx create mode 100644 src/utils/http/axios/Axios.ts create mode 100644 src/utils/http/axios/axiosCancel.ts create mode 100644 src/utils/http/axios/axiosRetry.ts create mode 100644 src/utils/http/axios/axiosTransform.ts create mode 100644 src/utils/http/axios/checkStatus.ts create mode 100644 src/utils/http/axios/helper.ts create mode 100644 src/utils/http/axios/index.ts create mode 100644 src/utils/index.ts create mode 100644 src/utils/is.ts create mode 100644 src/utils/lib/echarts.ts create mode 100644 src/utils/log.ts create mode 100644 src/utils/mitt.ts create mode 100644 src/utils/propTypes.ts create mode 100644 src/utils/props.ts create mode 100644 src/utils/types.ts create mode 100644 src/utils/uuid.ts create mode 100644 src/views/dashboard/analysis/components/GrowCard.vue create mode 100644 src/views/dashboard/analysis/components/SalesProductPie.vue create mode 100644 src/views/dashboard/analysis/components/SiteAnalysis.vue create mode 100644 src/views/dashboard/analysis/components/VisitAnalysis.vue create mode 100644 src/views/dashboard/analysis/components/VisitAnalysisBar.vue create mode 100644 src/views/dashboard/analysis/components/VisitRadar.vue create mode 100644 src/views/dashboard/analysis/components/VisitSource.vue create mode 100644 src/views/dashboard/analysis/components/props.ts create mode 100644 src/views/dashboard/analysis/data.ts create mode 100644 src/views/dashboard/analysis/index.vue create mode 100644 src/views/dashboard/workbench/components/DynamicInfo.vue create mode 100644 src/views/dashboard/workbench/components/ProjectCard.vue create mode 100644 src/views/dashboard/workbench/components/QuickNav.vue create mode 100644 src/views/dashboard/workbench/components/SaleRadar.vue create mode 100644 src/views/dashboard/workbench/components/WorkbenchHeader.vue create mode 100644 src/views/dashboard/workbench/components/data.ts create mode 100644 src/views/dashboard/workbench/index.vue create mode 100644 src/views/demo/charts/Line.vue create mode 100644 src/views/demo/charts/Map.vue create mode 100644 src/views/demo/charts/Pie.vue create mode 100644 src/views/demo/charts/SaleRadar.vue create mode 100644 src/views/demo/charts/china.json create mode 100644 src/views/demo/charts/data.ts create mode 100644 src/views/demo/charts/map/Baidu.vue create mode 100644 src/views/demo/charts/map/Gaode.vue create mode 100644 src/views/demo/charts/map/Google.vue create mode 100644 src/views/demo/comp/button/index.vue create mode 100644 src/views/demo/comp/card-list/index.vue create mode 100644 src/views/demo/comp/count-to/index.vue create mode 100644 src/views/demo/comp/cropper/index.vue create mode 100644 src/views/demo/comp/desc/index.vue create mode 100644 src/views/demo/comp/drawer/Drawer1.vue create mode 100644 src/views/demo/comp/drawer/Drawer2.vue create mode 100644 src/views/demo/comp/drawer/Drawer3.vue create mode 100644 src/views/demo/comp/drawer/Drawer4.vue create mode 100644 src/views/demo/comp/drawer/Drawer5.vue create mode 100644 src/views/demo/comp/drawer/index.vue create mode 100644 src/views/demo/comp/flow-chart/dataTurbo.json create mode 100644 src/views/demo/comp/flow-chart/index.vue create mode 100644 src/views/demo/comp/lazy/TargetContent.vue create mode 100644 src/views/demo/comp/lazy/Transition.vue create mode 100644 src/views/demo/comp/lazy/index.vue create mode 100644 src/views/demo/comp/loading/index.vue create mode 100644 src/views/demo/comp/modal/Modal1.vue create mode 100644 src/views/demo/comp/modal/Modal2.vue create mode 100644 src/views/demo/comp/modal/Modal3.vue create mode 100644 src/views/demo/comp/modal/Modal4.vue create mode 100644 src/views/demo/comp/modal/index.vue create mode 100644 src/views/demo/comp/qrcode/index.vue create mode 100644 src/views/demo/comp/scroll/Action.vue create mode 100644 src/views/demo/comp/scroll/VirtualScroll.vue create mode 100644 src/views/demo/comp/scroll/index.vue create mode 100644 src/views/demo/comp/strength-meter/index.vue create mode 100644 src/views/demo/comp/transition/index.vue create mode 100644 src/views/demo/comp/upload/index.vue create mode 100644 src/views/demo/comp/verify/Rotate.vue create mode 100644 src/views/demo/comp/verify/index.vue create mode 100644 src/views/demo/editor/json/index.vue create mode 100644 src/views/demo/editor/markdown/Editor.vue create mode 100644 src/views/demo/editor/markdown/index.vue create mode 100644 src/views/demo/editor/tinymce/Editor.vue create mode 100644 src/views/demo/editor/tinymce/index.vue create mode 100644 src/views/demo/excel/ArrayExport.vue create mode 100644 src/views/demo/excel/CustomExport.vue create mode 100644 src/views/demo/excel/ImportExcel.vue create mode 100644 src/views/demo/excel/JsonExport.vue create mode 100644 src/views/demo/excel/data.ts create mode 100644 src/views/demo/feat/breadcrumb/ChildrenList.vue create mode 100644 src/views/demo/feat/breadcrumb/ChildrenListDetail.vue create mode 100644 src/views/demo/feat/breadcrumb/FlatList.vue create mode 100644 src/views/demo/feat/breadcrumb/FlatListDetail.vue create mode 100644 src/views/demo/feat/context-menu/index.vue create mode 100644 src/views/demo/feat/copy/index.vue create mode 100644 src/views/demo/feat/download/imgBase64.ts create mode 100644 src/views/demo/feat/download/index.vue create mode 100644 src/views/demo/feat/full-screen/index.vue create mode 100644 src/views/demo/feat/icon/index.vue create mode 100644 src/views/demo/feat/img-preview/index.vue create mode 100644 src/views/demo/feat/menu-params/index.vue create mode 100644 src/views/demo/feat/msg/index.vue create mode 100644 src/views/demo/feat/print/index.vue create mode 100644 src/views/demo/feat/request-demo/index.vue create mode 100644 src/views/demo/feat/ripple/index.vue create mode 100644 src/views/demo/feat/session-timeout/index.vue create mode 100644 src/views/demo/feat/tab-params/index.vue create mode 100644 src/views/demo/feat/tabs/TabDetail.vue create mode 100644 src/views/demo/feat/tabs/index.vue create mode 100644 src/views/demo/feat/watermark/index.vue create mode 100644 src/views/demo/feat/ws/index.vue create mode 100644 src/views/demo/form/AdvancedForm.vue create mode 100644 src/views/demo/form/AppendForm.vue create mode 100644 src/views/demo/form/CustomerForm.vue create mode 100644 src/views/demo/form/DynamicForm.vue create mode 100644 src/views/demo/form/RefForm.vue create mode 100644 src/views/demo/form/RuleForm.vue create mode 100644 src/views/demo/form/TabsForm.vue create mode 100644 src/views/demo/form/UseForm.vue create mode 100644 src/views/demo/form/index.vue create mode 100644 src/views/demo/level/Menu111.vue create mode 100644 src/views/demo/level/Menu12.vue create mode 100644 src/views/demo/level/Menu2.vue create mode 100644 src/views/demo/main-out/index.vue create mode 100644 src/views/demo/page/account/center/Application.vue create mode 100644 src/views/demo/page/account/center/Article.vue create mode 100644 src/views/demo/page/account/center/Project.vue create mode 100644 src/views/demo/page/account/center/data.tsx create mode 100644 src/views/demo/page/account/center/index.vue create mode 100644 src/views/demo/page/account/setting/AccountBind.vue create mode 100644 src/views/demo/page/account/setting/BaseSetting.vue create mode 100644 src/views/demo/page/account/setting/MsgNotify.vue create mode 100644 src/views/demo/page/account/setting/SecureSetting.vue create mode 100644 src/views/demo/page/account/setting/data.ts create mode 100644 src/views/demo/page/account/setting/index.vue create mode 100644 src/views/demo/page/desc/basic/data.tsx create mode 100644 src/views/demo/page/desc/basic/index.vue create mode 100644 src/views/demo/page/desc/high/data.tsx create mode 100644 src/views/demo/page/desc/high/index.vue create mode 100644 src/views/demo/page/form/basic/data.ts create mode 100644 src/views/demo/page/form/basic/index.vue create mode 100644 src/views/demo/page/form/high/PersonTable.vue create mode 100644 src/views/demo/page/form/high/data.ts create mode 100644 src/views/demo/page/form/high/index.vue create mode 100644 src/views/demo/page/form/step/Step1.vue create mode 100644 src/views/demo/page/form/step/Step2.vue create mode 100644 src/views/demo/page/form/step/Step3.vue create mode 100644 src/views/demo/page/form/step/data.tsx create mode 100644 src/views/demo/page/form/step/index.vue create mode 100644 src/views/demo/page/list/basic/data.tsx create mode 100644 src/views/demo/page/list/basic/index.vue create mode 100644 src/views/demo/page/list/card/data.tsx create mode 100644 src/views/demo/page/list/card/index.vue create mode 100644 src/views/demo/page/list/search/data.tsx create mode 100644 src/views/demo/page/list/search/index.vue create mode 100644 src/views/demo/page/result/fail/index.vue create mode 100644 src/views/demo/page/result/success/index.vue create mode 100644 src/views/demo/permission/CurrentPermissionMode.vue create mode 100644 src/views/demo/permission/back/Btn.vue create mode 100644 src/views/demo/permission/back/index.vue create mode 100644 src/views/demo/permission/front/AuthPageA.vue create mode 100644 src/views/demo/permission/front/AuthPageB.vue create mode 100644 src/views/demo/permission/front/Btn.vue create mode 100644 src/views/demo/permission/front/index.vue create mode 100644 src/views/demo/setup/index.vue create mode 100644 src/views/demo/system/account/AccountDetail.vue create mode 100644 src/views/demo/system/account/AccountModal.vue create mode 100644 src/views/demo/system/account/DeptTree.vue create mode 100644 src/views/demo/system/account/account.data.ts create mode 100644 src/views/demo/system/account/index.vue create mode 100644 src/views/demo/system/dept/DeptModal.vue create mode 100644 src/views/demo/system/dept/dept.data.ts create mode 100644 src/views/demo/system/dept/index.vue create mode 100644 src/views/demo/system/menu/MenuDrawer.vue create mode 100644 src/views/demo/system/menu/index.vue create mode 100644 src/views/demo/system/menu/menu.data.ts create mode 100644 src/views/demo/system/password/index.vue create mode 100644 src/views/demo/system/password/pwd.data.ts create mode 100644 src/views/demo/system/role/RoleDrawer.vue create mode 100644 src/views/demo/system/role/index.vue create mode 100644 src/views/demo/system/role/role.data.ts create mode 100644 src/views/demo/table/AuthColumn.vue create mode 100644 src/views/demo/table/Basic.vue create mode 100644 src/views/demo/table/CustomerCell.vue create mode 100644 src/views/demo/table/EditCellTable.vue create mode 100644 src/views/demo/table/EditRowTable.vue create mode 100644 src/views/demo/table/ExpandTable.vue create mode 100644 src/views/demo/table/FetchTable.vue create mode 100644 src/views/demo/table/FixedColumn.vue create mode 100644 src/views/demo/table/FixedHeight.vue create mode 100644 src/views/demo/table/FooterTable.vue create mode 100644 src/views/demo/table/FormTable.vue create mode 100644 src/views/demo/table/MergeHeader.vue create mode 100644 src/views/demo/table/MultipleHeader.vue create mode 100644 src/views/demo/table/RefTable.vue create mode 100644 src/views/demo/table/ResizeParentHeightTable.vue create mode 100644 src/views/demo/table/TreeTable.vue create mode 100644 src/views/demo/table/UseTable.vue create mode 100644 src/views/demo/table/tableData.tsx create mode 100644 src/views/demo/tree/ActionTree.vue create mode 100644 src/views/demo/tree/EditTree.vue create mode 100644 src/views/demo/tree/data.ts create mode 100644 src/views/demo/tree/index.vue create mode 100644 src/views/sys/about/index.vue create mode 100644 src/views/sys/error-log/DetailModal.vue create mode 100644 src/views/sys/error-log/data.tsx create mode 100644 src/views/sys/error-log/index.vue create mode 100644 src/views/sys/exception/Exception.vue create mode 100644 src/views/sys/exception/index.ts create mode 100644 src/views/sys/iframe/FrameBlank.vue create mode 100644 src/views/sys/iframe/index.vue create mode 100644 src/views/sys/lock/LockPage.vue create mode 100644 src/views/sys/lock/index.vue create mode 100644 src/views/sys/lock/useNow.ts create mode 100644 src/views/sys/login/ForgetPasswordForm.vue create mode 100644 src/views/sys/login/Login.vue create mode 100644 src/views/sys/login/LoginForm.vue create mode 100644 src/views/sys/login/LoginFormTitle.vue create mode 100644 src/views/sys/login/MobileForm.vue create mode 100644 src/views/sys/login/QrCodeForm.vue create mode 100644 src/views/sys/login/RegisterForm.vue create mode 100644 src/views/sys/login/SessionTimeoutLogin.vue create mode 100644 src/views/sys/login/useLogin.ts create mode 100644 src/views/sys/redirect/index.vue create mode 100644 stylelint.config.js create mode 100644 tests/server/README.md create mode 100644 tests/server/controller/FileController.ts create mode 100644 tests/server/controller/UserController.ts create mode 100644 tests/server/ecosystem.config.js create mode 100644 tests/server/index.ts create mode 100644 tests/server/nodemon.json create mode 100644 tests/server/package.json create mode 100644 tests/server/routes.ts create mode 100644 tests/server/service/FileService.ts create mode 100644 tests/server/service/UserService.ts create mode 100644 tests/server/tsconfig.json create mode 100644 tests/server/utils.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts create mode 100644 windi.config.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..dccf841d --- /dev/null +++ b/.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/.env b/.env new file mode 100644 index 00000000..fc3262c9 --- /dev/null +++ b/.env @@ -0,0 +1,8 @@ +# 端口号 +VITE_PORT = 3100 + +# 网站标题 +VITE_GLOB_APP_TITLE = Vben Admin + +# 简称,用于配置文件名字 不要出现空格、数字开头等特殊字符 +VITE_GLOB_APP_SHORT_NAME = vue_vben_admin diff --git a/.env.development b/.env.development new file mode 100644 index 00000000..608035d7 --- /dev/null +++ b/.env.development @@ -0,0 +1,24 @@ +# 是否开启mock数据,关闭时需要自行对接后台接口 +VITE_USE_MOCK = true + +# 资源公共路径,需要以 /开头和结尾 +VITE_PUBLIC_PATH = / + +# 本地开发代理,可以解决跨域及多地址代理 +# 如果接口地址匹配到,则会转发到http://localhost:3000,防止本地出现跨域问题 +# 可以有多个,注意多个不能换行,否则代理将会失效 +VITE_PROXY = [["/basic-api","http://localhost:3000"],["/upload","http://localhost:3300/upload"]] +# VITE_PROXY=[["/api","https://vvbin.cn/test"]] + +# 是否删除Console.log +VITE_DROP_CONSOLE = false + +# 接口地址 +# 如果没有跨域问题,直接在这里配置即可 +VITE_GLOB_API_URL = /basic-api + +# 文件上传接口 可选 +VITE_GLOB_UPLOAD_URL = /upload + +# 接口地址前缀,有些系统所有接口地址都有前缀,可以在这里统一加,方便切换 +VITE_GLOB_API_URL_PREFIX = diff --git a/.env.production b/.env.production new file mode 100644 index 00000000..c8195ad0 --- /dev/null +++ b/.env.production @@ -0,0 +1,31 @@ +# 是否开启mock +VITE_USE_MOCK = true + +# 资源公共路径,需要以 / 开头和结尾 +VITE_PUBLIC_PATH = / + +# 是否删除Console.log +VITE_DROP_CONSOLE = true + +# 打包是否输出gz|br文件 +# 可选: gzip | brotli | none +# 也可以有多个, 例如 ‘gzip’|'brotli',这样会同时生成 .gz和.br文件 +VITE_BUILD_COMPRESS = 'gzip' + +# 使用compress时是否删除源文件,默认false +VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false + +# 接口地址 可以由nginx做转发或者直接写实际地址 +VITE_GLOB_API_URL = /basic-api + +# 文件上传地址 可以由nginx做转发或者直接写实际地址 +VITE_GLOB_UPLOAD_URL = /upload + +# 接口地址前缀,有些系统所有接口地址都有前缀,可以在这里统一加,方便切换 +VITE_GLOB_API_URL_PREFIX = + +# 打包是否开启pwa功能 +VITE_USE_PWA = false + +# 是否兼容旧版浏览器。开启后打包时间会慢一倍左右。会多打出旧浏览器兼容包,且会根据浏览器兼容性自动使用相应的版本 +VITE_LEGACY = false diff --git a/.env.test b/.env.test new file mode 100644 index 00000000..42f5bf11 --- /dev/null +++ b/.env.test @@ -0,0 +1,32 @@ +NODE_ENV=production +# 是否开启mock +VITE_USE_MOCK = true + +# 资源公共路径,需要以 / 开头和结尾 +VITE_PUBLIC_PATH = / + +# 是否删除Console.log +VITE_DROP_CONSOLE = true + +# 打包是否输出gz|br文件 +# 可选: gzip | brotli | none +# 也可以有多个, 例如 ‘gzip’|'brotli',这样会同时生成 .gz和.br文件 +VITE_BUILD_COMPRESS = 'none' + +# 使用compress时是否删除源文件,默认false +VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false + +# 接口地址 可以由nginx做转发或者直接写实际地址 +VITE_GLOB_API_URL = /basic-api + +# 文件上传地址 可以由nginx做转发或者直接写实际地址 +VITE_GLOB_UPLOAD_URL = /upload + +# 接口地址前缀,有些系统所有接口地址都有前缀,可以在这里统一加,方便切换 +VITE_GLOB_API_URL_PREFIX = + +# 打包是否开启pwa功能 +VITE_USE_PWA = false + +# 是否兼容旧版浏览器。开启后打包时间会慢一倍左右。会多打出旧浏览器兼容包,且会根据浏览器兼容性自动使用相应的版本 +VITE_LEGACY = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..e65f4ff8 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,14 @@ + +*.sh +node_modules +*.md +*.woff +*.ttf +.vscode +.idea +dist +/public +/docs +.local +/bin +Dockerfile diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..af4a0fdc --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,74 @@ +module.exports = { + root: true, + env: { + browser: true, + node: true, + es6: true + }, + parser: 'vue-eslint-parser', + plugins: ['vue'], + parserOptions: { + parser: '@typescript-eslint/parser', + ecmaVersion: 2020, + sourceType: 'module', + jsxPragma: 'React', + ecmaFeatures: { + jsx: true + } + }, + extends: ['plugin:vue/vue3-recommended', 'prettier', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], + rules: { + 'max-len': ['error', { code: 140, tabWidth: 2, ignoreComments: true }], + '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/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d4e5bd3e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Automatically normalize line endings (to LF) for all text-based files. +* text=auto eol=lf + +# Declare files that will always have CRLF line endings on checkout. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..7bf05d01 --- /dev/null +++ b/.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 + +.history diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000..866381fc --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,6 @@ +ports: + - port: 3344 + onOpen: open-preview +tasks: + - init: pnpm install + command: pnpm run dev diff --git a/.hintrc b/.hintrc new file mode 100644 index 00000000..455c7816 --- /dev/null +++ b/.hintrc @@ -0,0 +1,16 @@ +{ + "extends": [ + "development" + ], + "hints": { + "compat-api/css": [ + "default", + { + "ignore": [ + "-webkit-tap-highlight-color", + "text-size-adjust" + ] + } + ] + } +} \ No newline at end of file diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 00000000..274d2d8b --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,8 @@ +#!/bin/sh + +# shellcheck source=./_/husky.sh +. "$(dirname "$0")/_/husky.sh" + +PATH="/usr/local/bin:$PATH" + +npx --no-install commitlint --edit "$1" diff --git a/.husky/common.sh b/.husky/common.sh new file mode 100644 index 00000000..9d5129bd --- /dev/null +++ b/.husky/common.sh @@ -0,0 +1,9 @@ +#!/bin/sh +command_exists () { + command -v "$1" >/dev/null 2>&1 +} + +# Workaround for Windows 10, Git Bash and Yarn +if command_exists winpty && test -t 1; then + exec < /dev/tty +fi diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..f975aa7b --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,10 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" +. "$(dirname "$0")/common.sh" + +[ -n "$CI" ] && exit 0 + +PATH="/usr/local/bin:$PATH" + +# Format and submit code according to lintstagedrc.js configuration +npm run lint:lint-staged diff --git a/.lintstagedrc.js b/.lintstagedrc.js new file mode 100644 index 00000000..b8b6c4cb --- /dev/null +++ b/.lintstagedrc.js @@ -0,0 +1,8 @@ +// .lintstagedrc.js +module.exports = { + '*.js': ['prettier --config prettier.config.js --write', 'eslint --fix --ext .js'], + '*.ts': ['prettier --config prettier.config.js --write', 'eslint --fix --ext .ts'], + '*.vue': ['prettier --config prettier.config.js --write', 'eslint --fix --ext .vue'], + '*.tsx': ['prettier --config prettier.config.js --write', 'eslint --fix --ext .tsx'], + '*.json': 'prettier --config prettier.config.js --write' +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..f7e39e60 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +/dist/* +.local +.output.js +/node_modules/** + +**/*.svg +**/*.sh + +/public/* diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 00000000..05170761 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,3 @@ +/dist/* +/public/* +public/* diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..f7e6b5f3 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,13 @@ +{ + "recommendations": [ + "vue.volar", + "dbaeumer.vscode-eslint", + "stylelint.vscode-stylelint", + "esbenp.prettier-vscode", + "mrmlnc.vscode-less", + "lokalise.i18n-ally", + "antfu.iconify", + "mikestead.dotenv", + "heybourn.headwind" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..72e95d0d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome", + "url": "http://localhost:3100", + "webRoot": "${workspaceFolder}/src", + "sourceMaps": true + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..252d7050 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,167 @@ +{ + "typescript.tsdk": "./node_modules/typescript/lib", + "volar.tsPlugin": true, + "volar.tsPluginStatus": false, + "npm.packageManager": "pnpm", + "editor.tabSize": 2, + "prettier.printWidth": 140, // 超过最大值换行 + "editor.defaultFormatter": "esbenp.prettier-vscode", + "files.eol": "\n", + "search.exclude": { + "**/node_modules": true, + "**/*.log": true, + "**/*.log*": true, + "**/bower_components": true, + "**/dist": true, + "**/elehukouben": true, + "**/.git": true, + "**/.gitignore": true, + "**/.svn": true, + "**/.DS_Store": true, + "**/.idea": true, + "**/.vscode": false, + "**/yarn.lock": true, + "**/tmp": true, + "out": true, + "dist": true, + "node_modules": true, + "CHANGELOG.md": true, + "examples": true, + "res": true, + "screenshots": true, + "yarn-error.log": true, + "**/.yarn": true + }, + "files.exclude": { + "**/.cache": true, + "**/.editorconfig": true, + "**/.eslintcache": true, + "**/bower_components": true, + "**/.idea": true, + "**/tmp": true, + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true + }, + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/.vscode/**": true, + "**/node_modules/**": true, + "**/tmp/**": true, + "**/bower_components/**": true, + "**/dist/**": true, + "**/yarn.lock": true + }, + "stylelint.enable": true, + "stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"], + "path-intellisense.mappings": { + "@/": "${workspaceRoot}/src" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "rvest.vs-code-prettier-eslint" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "rvest.vs-code-prettier-eslint" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "rvest.vs-code-prettier-eslint" + }, + "[less]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[scss]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "[vue]": { + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true, + "source.fixAll.stylelint": true + } + }, + "i18n-ally.localesPaths": ["src/locales/lang"], + "i18n-ally.keystyle": "nested", + "i18n-ally.sortKeys": true, + "i18n-ally.namespace": true, + "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}", + "i18n-ally.enabledParsers": ["ts"], + "i18n-ally.sourceLanguage": "en", + "i18n-ally.displayLanguage": "zh-CN", + "i18n-ally.enabledFrameworks": ["vue", "react"], + "cSpell.words": [ + "vben", + "windi", + "browserslist", + "tailwindcss", + "esnext", + "antv", + "tinymce", + "qrcode", + "sider", + "pinia", + "sider", + "nprogress", + "INTLIFY", + "stylelint", + "esno", + "vitejs", + "sortablejs", + "mockjs", + "codemirror", + "iconify", + "commitlint", + "vditor", + "echarts", + "cropperjs", + "logicflow", + "vueuse", + "zxcvbn", + "lintstagedrc", + "brotli", + "tailwindcss", + "sider", + "pnpm", + "antd" + ], + "vetur.format.scriptInitialIndent": true, + "vetur.format.styleInitialIndent": true, + "vetur.validation.script": false, + "MicroPython.executeButton": [ + { + "text": "▶", + "tooltip": "运行", + "alignment": "left", + "command": "extension.executeFile", + "priority": 3.5 + } + ], + "MicroPython.syncButton": [ + { + "text": "$(sync)", + "tooltip": "同步", + "alignment": "left", + "command": "extension.execute", + "priority": 4 + } + ], + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/CNAME b/CNAME new file mode 100644 index 00000000..3436928a --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +vben.vvbin.cn diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..ba2fe3b1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-present, Vben + +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/README.md b/README.md new file mode 100644 index 00000000..490048f3 --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +
VbenAdmin Logo

+ +[![license](https://img.shields.io/github/license/xingyu4j/vue-vben-admin.svg)](LICENSE) + +

v1.0.2

+ +## 精简版地址 + +[gitee](https://gitee.com/xingyu4j/vue-vben-admin/tree/thin/) [github](https://github.com/xingyu4j/vue-vben-admin/tree/thin) + +
+ +## 简介 + +- Vue Vben Admin 是一个免费开源的中后台模版。 +- 本项目基于 vben2.8 版本,升级依赖,重构部分组件,重构代码样式,重构 setup 语法糖。 +- 使用了最新的`Vue3`,`Vite4`,`Antdv3`,`TypeScript5`,`Pinia`等主流技术开发,开箱即用的中后台前端解决方案。 + +## 框架 + +| 框架 | 说明 | 版本 | +| --- | --- | --- | +| [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.2.47 | +| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 4.2.0 | +| [ant-design-vue](https://antdv.com/) | ant-design-vue | 3.2.15 | +| [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 的超集 | 5.0.2 | +| [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.0.33 | +| [vueuse](https://vueuse.org/) | 常用工具集 | 9.13.0 | +| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.2.2 | +| [vue-router](https://router.vuejs.org/) | Vue 路由 | 4.1.6 | +| [windicss](https://cn.windicss.org/) | 下一代工具优先的 CSS 框架 | 3.5.6 | +| [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 3.1.0 | + +## 特性 + +- **最新技术栈**:使用 Vue3/vite4/antdv3 等前端前沿技术开发 +- **TypeScript**: 应用程序级 JavaScript 的语言 +- **主题**:可配置的主题 +- **国际化**:内置完善的国际化方案 +- **Mock 数据** 内置 Mock 数据方案 +- **权限** 内置完善的动态路由权限生成方案 +- **组件** 二次封装了多个常用的组件 + +

+ VbenAdmin Logo + VbenAdmin Logo + VbenAdmin Logo +

+ +## 预览地址 + +[预览地址](http://vben.x-surge.com) + +## 准备 + +- [node](http://nodejs.org/) 和 [git](https://git-scm.com/) -项目开发环境 +- [Vite4](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/xingyu4j/vue-vben-admin.git +or +git clone https://gitee.com/xingyu4j/vue-vben-admin +``` + +- 安装依赖 + +```bash +cd vue-vben-admin + +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` 开发中 + +## 浏览器支持 + +本地开发推荐使用`Chrome 80+` 浏览器 + +支持现代浏览器, 不支持 IE + +| [ Edge](http://godban.github.io/browsers-support-badges/)
IE | [ Edge](http://godban.github.io/browsers-support-badges/)
Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | +| :-: | :-: | :-: | :-: | :-: | +| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions | diff --git a/build/config/themeConfig.ts b/build/config/themeConfig.ts new file mode 100644 index 00000000..0d539967 --- /dev/null +++ b/build/config/themeConfig.ts @@ -0,0 +1,69 @@ +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/build/constant.ts b/build/constant.ts new file mode 100644 index 00000000..00736e3f --- /dev/null +++ b/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/build/generate/generateModifyVars.ts b/build/generate/generateModifyVars.ts new file mode 100644 index 00000000..db206309 --- /dev/null +++ b/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/build/generate/icon/index.ts b/build/generate/icon/index.ts new file mode 100644 index 00000000..a3be71b9 --- /dev/null +++ b/build/generate/icon/index.ts @@ -0,0 +1,68 @@ +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) + await 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) + } + } + await fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite')) + console.log(`✨ ${colors.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`) + }) +} + +generateIcon() diff --git a/build/getConfigFileName.ts b/build/getConfigFileName.ts new file mode 100644 index 00000000..4db6b708 --- /dev/null +++ b/build/getConfigFileName.ts @@ -0,0 +1,7 @@ +/** + * 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/build/script/buildConf.ts b/build/script/buildConf.ts new file mode 100644 index 00000000..73df033b --- /dev/null +++ b/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/build/script/postBuild.ts b/build/script/postBuild.ts new file mode 100644 index 00000000..bbe05ed1 --- /dev/null +++ b/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/build/utils.ts b/build/utils.ts new file mode 100644 index 00000000..6550b637 --- /dev/null +++ b/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/build/vite/optimize.ts b/build/vite/optimize.ts new file mode 100644 index 00000000..0e693037 --- /dev/null +++ b/build/vite/optimize.ts @@ -0,0 +1,35 @@ +const include = [ + 'qs', + 'vue', + 'less', + 'axios', + 'pinia', + 'dayjs', + 'qrcode', + 'echarts', + 'intro.js', + 'cropperjs', + 'crypto-js', + 'lodash-es', + 'nprogress', + 'vue-i18n', + 'vue-types', + 'vue-router', + 'sortablejs', + 'echarts/core', + 'echarts/charts', + 'echarts/components', + 'echarts/renderers', + '@vueuse/core', + '@zxcvbn-ts/core', + '@iconify/iconify', + 'ant-design-vue', + 'ant-design-vue/es/style', + 'ant-design-vue/es/locale/zh_CN', + 'ant-design-vue/es/locale/en_US', + 'vite-plugin-windicss' +] + +const exclude = ['@iconify/json'] + +export { include, exclude } diff --git a/build/vite/plugin/compress.ts b/build/vite/plugin/compress.ts new file mode 100644 index 00000000..065f46e8 --- /dev/null +++ b/build/vite/plugin/compress.ts @@ -0,0 +1,32 @@ +/** + * 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/build/vite/plugin/html.ts b/build/vite/plugin/html.ts new file mode 100644 index 00000000..af8ee52d --- /dev/null +++ b/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/build/vite/plugin/index.ts b/build/vite/plugin/index.ts new file mode 100644 index 00000000..f55d0123 --- /dev/null +++ b/build/vite/plugin/index.ts @@ -0,0 +1,76 @@ +import { PluginOption } from 'vite' +import vue from '@vitejs/plugin-vue' +import vueJsx from '@vitejs/plugin-vue-jsx' +import legacy from '@vitejs/plugin-legacy' +import progress from 'vite-plugin-progress' +import windiCSS from 'vite-plugin-windicss' +import purgeIcons from 'vite-plugin-purge-icons' +import VitePluginCertificate from 'vite-plugin-mkcert' +import vueSetupExtend from 'unplugin-vue-setup-extend-plus/vite' +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 { configSvgIconsPlugin } from './svgSprite' +import { isProdFn } from '../../utils' + +export function createVitePlugins(mode: string, viteEnv: ViteEnv, isBuild: boolean) { + const { 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(), + // 打包进度条 + progress(), + // 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 + if (isProdFn(mode)) { + vitePlugins.push(configStyleImportPlugin(isBuild)) + } + + // rollup-plugin-visualizer + vitePlugins.push(configVisualizerConfig()) + + // vite-plugin-vben-theme + vitePlugins.push(configThemePlugin(isBuild)) + + // The following plugins only work in the production environment + if (isBuild) { + // 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/build/vite/plugin/mock.ts b/build/vite/plugin/mock.ts new file mode 100644 index 00000000..f3632b02 --- /dev/null +++ b/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/build/vite/plugin/pwa.ts b/build/vite/plugin/pwa.ts new file mode 100644 index 00000000..a0970af6 --- /dev/null +++ b/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/build/vite/plugin/styleImport.ts b/build/vite/plugin/styleImport.ts new file mode 100644 index 00000000..a253a581 --- /dev/null +++ b/build/vite/plugin/styleImport.ts @@ -0,0 +1,82 @@ +/** + * 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 = { + textarea: 'input', + '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/build/vite/plugin/svgSprite.ts b/build/vite/plugin/svgSprite.ts new file mode 100644 index 00000000..991fc849 --- /dev/null +++ b/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/build/vite/plugin/theme.ts b/build/vite/plugin/theme.ts new file mode 100644 index 00000000..cb3489a0 --- /dev/null +++ b/build/vite/plugin/theme.ts @@ -0,0 +1,83 @@ +/** + * 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 '@kirklin/vite-plugin-vben-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/build/vite/plugin/visualizer.ts b/build/vite/plugin/visualizer.ts new file mode 100644 index 00000000..8118d43d --- /dev/null +++ b/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/build/vite/proxy.ts b/build/vite/proxy.ts new file mode 100644 index 00000000..c12f12c7 --- /dev/null +++ b/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/commitlint.config.js b/commitlint.config.js new file mode 100644 index 00000000..27b7a886 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,91 @@ +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/index.html b/index.html new file mode 100644 index 00000000..32f7cc27 --- /dev/null +++ b/index.html @@ -0,0 +1,158 @@ + + + + + + + + <%= title %> + + + + +
+ +
+
+ +
+ +
+
<%= title %>
+
+
+
+ + + diff --git a/mock/_createProductionServer.ts b/mock/_createProductionServer.ts new file mode 100644 index 00000000..2119e733 --- /dev/null +++ b/mock/_createProductionServer.ts @@ -0,0 +1,34 @@ +import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer' + +// 问题描述 +// 1. `import.meta.globEager` 已被弃用, 需要升级vite版本,有兼容问题 +// 2. `vite-plugin-mock` 插件问题 https://github.com/vbenjs/vite-plugin-mock/issues/56 + +// const modules: Record = import.meta.glob("./**/*.ts", { +// import: "default", +// eager: true, +// }); + +// const mockModules = Object.keys(modules).reduce((pre, key) => { +// if (!key.includes("/_")) { +// pre.push(...(modules as Recordable)[key]); +// } +// return pre; +// }, [] as any[]); + +const modules = import.meta.globEager('./**/*.ts') + +const mockModules: any[] = [] +Object.keys(modules).forEach((key) => { + if (key.includes('/_')) { + return + } + mockModules.push(...(modules as Recordable)[key].default) +}) + +/** + * Used in a production environment. Need to manually import all modules + */ +export function setupProdMockServer() { + createProdMockServer(mockModules) +} diff --git a/mock/_util.ts b/mock/_util.ts new file mode 100644 index 00000000..903985c1 --- /dev/null +++ b/mock/_util.ts @@ -0,0 +1,52 @@ +// 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/mock/demo/account.ts b/mock/demo/account.ts new file mode 100644 index 00000000..0af0978b --- /dev/null +++ b/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/mock/demo/api-cascader.ts b/mock/demo/api-cascader.ts new file mode 100644 index 00000000..a5458c8b --- /dev/null +++ b/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/mock/demo/select-demo.ts b/mock/demo/select-demo.ts new file mode 100644 index 00000000..f733a6e1 --- /dev/null +++ b/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/mock/demo/system.ts b/mock/demo/system.ts new file mode 100644 index 00000000..1522ed37 --- /dev/null +++ b/mock/demo/system.ts @@ -0,0 +1,194 @@ +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/mock/demo/table-demo.ts b/mock/demo/table-demo.ts new file mode 100644 index 00000000..c00185b4 --- /dev/null +++ b/mock/demo/table-demo.ts @@ -0,0 +1,55 @@ +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()', + radio1: `选项${index + 1}`, + radio2: `选项${index + 1}`, + radio3: `选项${index + 1}`, + 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/mock/demo/tree-demo.ts b/mock/demo/tree-demo.ts new file mode 100644 index 00000000..48d1b5ff --- /dev/null +++ b/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/mock/sys/menu.ts b/mock/sys/menu.ts new file mode 100644 index 00000000..01502fcc --- /dev/null +++ b/mock/sys/menu.ts @@ -0,0 +1,270 @@ +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: 'menu', + name: 'MenuManagement', + meta: { + title: 'routes.demo.system.menu', + ignoreKeepAlive: true + }, + component: '/demo/system/menu/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://doc.vvbin.cn/' + } + }, + { + path: 'https://doc.vvbin.cn/', + 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/mock/sys/user.ts b/mock/sys/user.ts new file mode 100644 index 00000000..7ef24764 --- /dev/null +++ b/mock/sys/user.ts @@ -0,0 +1,120 @@ +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: '', + 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: '', + 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/package.json b/package.json new file mode 100644 index 00000000..b2c92f2d --- /dev/null +++ b/package.json @@ -0,0 +1,189 @@ +{ + "name": "vben-admin", + "version": "1.0.2", + "author": { + "name": "xingyu4j", + "email": "xingyu4j@vip.qq.com", + "url": "https://gitee.com/xingyuv" + }, + "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,ts,tsx,css,less,scss,vue,html,md}\"", + "lint:style": "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", + "npm:check": "npx npm-check-updates", + "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": "^7.0.0", + "@ant-design/icons-vue": "^6.1.0", + "@iconify/iconify": "^3.1.0", + "@logicflow/core": "^1.2.1", + "@logicflow/extension": "^1.2.1", + "@vue/runtime-core": "^3.2.47", + "@vueuse/core": "^9.13.0", + "@zxcvbn-ts/core": "^2.2.1", + "ant-design-vue": "^3.2.15", + "axios": "^1.3.4", + "codemirror": "^5.65.3", + "cropperjs": "^1.5.13", + "crypto-js": "^4.1.1", + "dayjs": "^1.11.7", + "echarts": "^5.4.1", + "intro.js": "^6.0.0", + "lodash-es": "^4.17.21", + "mockjs": "^1.1.0", + "nprogress": "^0.2.0", + "path-to-regexp": "^6.2.1", + "pinia": "^2.0.33", + "print-js": "^1.6.0", + "qrcode": "^1.5.1", + "qs": "^6.11.1", + "resize-observer-polyfill": "^1.5.1", + "showdown": "^2.1.0", + "sortablejs": "^1.15.0", + "tinymce": "^5.10.7", + "vditor": "^3.9.1", + "vue": "^3.2.47", + "vue-i18n": "^9.2.2", + "vue-json-pretty": "^2.2.3", + "vue-router": "^4.1.6", + "vue-types": "^5.0.2", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@commitlint/cli": "^17.4.4", + "@commitlint/config-conventional": "^17.4.4", + "@iconify/json": "^2.2.36", + "@kirklin/vite-plugin-vben-theme": "^0.1.1", + "@purge-icons/generated": "^0.9.0", + "@types/codemirror": "^5.60.5", + "@types/crypto-js": "^4.1.1", + "@types/fs-extra": "^11.0.1", + "@types/inquirer": "^9.0.3", + "@types/intro.js": "^5.1.1", + "@types/lodash-es": "^4.17.7", + "@types/mockjs": "^1.0.7", + "@types/node": "^18.15.3", + "@types/nprogress": "^0.2.0", + "@types/qrcode": "^1.5.0", + "@types/qs": "^6.9.7", + "@types/showdown": "^2.0.0", + "@types/sortablejs": "^1.15.1", + "@typescript-eslint/eslint-plugin": "^5.55.0", + "@typescript-eslint/parser": "^5.55.0", + "@vitejs/plugin-legacy": "^4.0.2", + "@vitejs/plugin-vue": "^4.1.0", + "@vitejs/plugin-vue-jsx": "^3.0.1", + "@vue/compiler-sfc": "^3.2.47", + "@vue/test-utils": "^2.3.1", + "autoprefixer": "^10.4.14", + "conventional-changelog-cli": "^2.2.2", + "cross-env": "^7.0.3", + "cz-git": "^1.6.0", + "czg": "^1.6.0", + "dotenv": "^16.0.3", + "eslint": "^8.36.0", + "eslint-config-prettier": "^8.7.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-vue": "^9.9.0", + "esno": "^0.16.3", + "fs-extra": "^11.1.0", + "husky": "^8.0.3", + "inquirer": "^9.1.4", + "less": "^4.1.3", + "lint-staged": "^13.2.0", + "npm-run-all": "^4.1.5", + "picocolors": "^1.0.0", + "postcss": "^8.4.21", + "postcss-html": "^1.5.0", + "postcss-less": "^6.0.0", + "prettier": "^2.8.4", + "rimraf": "^4.4.0", + "rollup": "^3.19.1", + "rollup-plugin-visualizer": "^5.9.0", + "stylelint": "^14.16.1", + "stylelint-config-prettier": "^9.0.5", + "stylelint-config-recommended": "^9.0.0", + "stylelint-config-recommended-vue": "^1.4.0", + "stylelint-config-standard": "^29.0.0", + "stylelint-order": "^6.0.2", + "terser": "^5.16.6", + "ts-node": "^10.9.1", + "typescript": "^5.0.2", + "unplugin-vue-setup-extend-plus": "^0.4.9", + "vite": "^4.2.0", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-html": "^3.2.0", + "vite-plugin-mkcert": "^1.13.3", + "vite-plugin-mock": "^2.9.6", + "vite-plugin-progress": "^0.0.6", + "vite-plugin-purge-icons": "^0.9.2", + "vite-plugin-pwa": "^0.14.4", + "vite-plugin-style-import": "^2.0.0", + "vite-plugin-svg-icons": "^2.0.1", + "vite-plugin-windicss": "^1.8.10", + "vue-eslint-parser": "^9.1.0", + "vue-tsc": "^1.2.0" + }, + "repository": { + "type": "git", + "url": "git+https://gitee.com/xingyuv/vue-vben-admin.git" + }, + "license": "MIT", + "bugs": { + "url": "https://gitee.com/xingyuv/issues" + }, + "homepage": "https://gitee.com/xingyuv", + "engines": { + "node": "> 16.18.0" + }, + "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/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..6dc40bd0 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,9745 @@ +lockfileVersion: 5.4 + +specifiers: + '@ant-design/colors': ^7.0.0 + '@ant-design/icons-vue': ^6.1.0 + '@commitlint/cli': ^17.4.4 + '@commitlint/config-conventional': ^17.4.4 + '@iconify/iconify': ^3.1.0 + '@iconify/json': ^2.2.36 + '@kirklin/vite-plugin-vben-theme': ^0.1.1 + '@logicflow/core': ^1.2.1 + '@logicflow/extension': ^1.2.1 + '@purge-icons/generated': ^0.9.0 + '@types/codemirror': ^5.60.5 + '@types/crypto-js': ^4.1.1 + '@types/fs-extra': ^11.0.1 + '@types/inquirer': ^9.0.3 + '@types/intro.js': ^5.1.1 + '@types/lodash-es': ^4.17.7 + '@types/mockjs': ^1.0.7 + '@types/node': ^18.15.3 + '@types/nprogress': ^0.2.0 + '@types/qrcode': ^1.5.0 + '@types/qs': ^6.9.7 + '@types/showdown': ^2.0.0 + '@types/sortablejs': ^1.15.1 + '@typescript-eslint/eslint-plugin': ^5.55.0 + '@typescript-eslint/parser': ^5.55.0 + '@vitejs/plugin-legacy': ^4.0.2 + '@vitejs/plugin-vue': ^4.1.0 + '@vitejs/plugin-vue-jsx': ^3.0.1 + '@vue/compiler-sfc': ^3.2.47 + '@vue/runtime-core': ^3.2.47 + '@vue/test-utils': ^2.3.1 + '@vueuse/core': ^9.13.0 + '@zxcvbn-ts/core': ^2.2.1 + ant-design-vue: ^3.2.15 + autoprefixer: ^10.4.14 + axios: ^1.3.4 + codemirror: ^5.65.3 + conventional-changelog-cli: ^2.2.2 + cropperjs: ^1.5.13 + cross-env: ^7.0.3 + crypto-js: ^4.1.1 + cz-git: ^1.6.0 + czg: ^1.6.0 + dayjs: ^1.11.7 + dotenv: ^16.0.3 + echarts: ^5.4.1 + eslint: ^8.36.0 + eslint-config-prettier: ^8.7.0 + eslint-plugin-prettier: ^4.2.1 + eslint-plugin-vue: ^9.9.0 + esno: ^0.16.3 + fs-extra: ^11.1.0 + husky: ^8.0.3 + inquirer: ^9.1.4 + intro.js: ^6.0.0 + less: ^4.1.3 + lint-staged: ^13.2.0 + lodash-es: ^4.17.21 + mockjs: ^1.1.0 + npm-run-all: ^4.1.5 + nprogress: ^0.2.0 + path-to-regexp: ^6.2.1 + picocolors: ^1.0.0 + pinia: ^2.0.33 + postcss: ^8.4.21 + postcss-html: ^1.5.0 + postcss-less: ^6.0.0 + prettier: ^2.8.4 + print-js: ^1.6.0 + qrcode: ^1.5.1 + qs: ^6.11.1 + resize-observer-polyfill: ^1.5.1 + rimraf: ^4.4.0 + rollup: ^3.19.1 + rollup-plugin-visualizer: ^5.9.0 + showdown: ^2.1.0 + sortablejs: ^1.15.0 + stylelint: ^14.16.1 + stylelint-config-prettier: ^9.0.5 + stylelint-config-recommended: ^9.0.0 + stylelint-config-recommended-vue: ^1.4.0 + stylelint-config-standard: ^29.0.0 + stylelint-order: ^6.0.2 + terser: ^5.16.6 + tinymce: ^5.10.7 + ts-node: ^10.9.1 + typescript: ^5.0.2 + unplugin-vue-setup-extend-plus: ^0.4.9 + vditor: ^3.9.1 + vite: ^4.2.0 + vite-plugin-compression: ^0.5.1 + vite-plugin-html: ^3.2.0 + vite-plugin-mkcert: ^1.13.3 + vite-plugin-mock: ^2.9.6 + vite-plugin-progress: ^0.0.6 + vite-plugin-purge-icons: ^0.9.2 + vite-plugin-pwa: ^0.14.4 + vite-plugin-style-import: ^2.0.0 + vite-plugin-svg-icons: ^2.0.1 + vite-plugin-windicss: ^1.8.10 + vue: ^3.2.47 + vue-eslint-parser: ^9.1.0 + vue-i18n: ^9.2.2 + vue-json-pretty: ^2.2.3 + vue-router: ^4.1.6 + vue-tsc: ^1.2.0 + vue-types: ^5.0.2 + xlsx: ^0.18.5 + +dependencies: + '@ant-design/colors': 7.0.0 + '@ant-design/icons-vue': 6.1.0_vue@3.2.47 + '@iconify/iconify': 3.1.0 + '@logicflow/core': 1.2.1 + '@logicflow/extension': 1.2.1 + '@vue/runtime-core': 3.2.47 + '@vueuse/core': 9.13.0_vue@3.2.47 + '@zxcvbn-ts/core': 2.2.1 + ant-design-vue: 3.2.15_vue@3.2.47 + axios: 1.3.4 + codemirror: 5.65.12 + cropperjs: 1.5.13 + crypto-js: 4.1.1 + dayjs: 1.11.7 + echarts: 5.4.1 + intro.js: 6.0.0 + lodash-es: 4.17.21 + mockjs: 1.1.0 + nprogress: 0.2.0 + path-to-regexp: 6.2.1 + pinia: 2.0.33_p4srtwdgezcoeovfcc2yxzvaue + print-js: 1.6.0 + qrcode: 1.5.1 + qs: 6.11.1 + resize-observer-polyfill: 1.5.1 + showdown: 2.1.0 + sortablejs: 1.15.0 + tinymce: 5.10.7 + vditor: 3.9.1 + vue: 3.2.47 + vue-i18n: 9.2.2_vue@3.2.47 + vue-json-pretty: 2.2.3_vue@3.2.47 + vue-router: 4.1.6_vue@3.2.47 + vue-types: 5.0.2_vue@3.2.47 + xlsx: 0.18.5 + +devDependencies: + '@commitlint/cli': 17.4.4 + '@commitlint/config-conventional': 17.4.4 + '@iconify/json': 2.2.36 + '@kirklin/vite-plugin-vben-theme': 0.1.1_vite@4.2.0 + '@purge-icons/generated': 0.9.0 + '@types/codemirror': 5.60.7 + '@types/crypto-js': 4.1.1 + '@types/fs-extra': 11.0.1 + '@types/inquirer': 9.0.3 + '@types/intro.js': 5.1.1 + '@types/lodash-es': 4.17.7 + '@types/mockjs': 1.0.7 + '@types/node': 18.15.3 + '@types/nprogress': 0.2.0 + '@types/qrcode': 1.5.0 + '@types/qs': 6.9.7 + '@types/showdown': 2.0.0 + '@types/sortablejs': 1.15.1 + '@typescript-eslint/eslint-plugin': 5.55.0_qsnvknysi52qtaxqdyqyohkcku + '@typescript-eslint/parser': 5.55.0_j4766f7ecgqbon3u7zlxn5zszu + '@vitejs/plugin-legacy': 4.0.2_terser@5.16.6+vite@4.2.0 + '@vitejs/plugin-vue': 4.1.0_vite@4.2.0+vue@3.2.47 + '@vitejs/plugin-vue-jsx': 3.0.1_vite@4.2.0+vue@3.2.47 + '@vue/compiler-sfc': 3.2.47 + '@vue/test-utils': 2.3.1_vue@3.2.47 + autoprefixer: 10.4.14_postcss@8.4.21 + conventional-changelog-cli: 2.2.2 + cross-env: 7.0.3 + cz-git: 1.6.0 + czg: 1.6.0 + dotenv: 16.0.3 + eslint: 8.36.0 + eslint-config-prettier: 8.7.0_eslint@8.36.0 + eslint-plugin-prettier: 4.2.1_eqzx3hpkgx5nnvxls3azrcc7dm + eslint-plugin-vue: 9.9.0_eslint@8.36.0 + esno: 0.16.3 + fs-extra: 11.1.0 + husky: 8.0.3 + inquirer: 9.1.4 + less: 4.1.3 + lint-staged: 13.2.0 + npm-run-all: 4.1.5 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-html: 1.5.0 + postcss-less: 6.0.0_postcss@8.4.21 + prettier: 2.8.4 + rimraf: 4.4.0 + rollup: 3.19.1 + rollup-plugin-visualizer: 5.9.0_rollup@3.19.1 + stylelint: 14.16.1 + stylelint-config-prettier: 9.0.5_stylelint@14.16.1 + stylelint-config-recommended: 9.0.0_stylelint@14.16.1 + stylelint-config-recommended-vue: 1.4.0_kbto3rg3njmczth2rrsgfnlsqa + stylelint-config-standard: 29.0.0_stylelint@14.16.1 + stylelint-order: 6.0.3_stylelint@14.16.1 + terser: 5.16.6 + ts-node: 10.9.1_sxidjv3cojnrggmso45tj7hldi + typescript: 5.0.2 + unplugin-vue-setup-extend-plus: 0.4.9 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + vite-plugin-compression: 0.5.1_vite@4.2.0 + vite-plugin-html: 3.2.0_vite@4.2.0 + vite-plugin-mkcert: 1.13.3_vite@4.2.0 + vite-plugin-mock: 2.9.6_3a7irfe7b7ve75yvq4zwteqfdy + vite-plugin-progress: 0.0.6_vite@4.2.0 + vite-plugin-purge-icons: 0.9.2_vite@4.2.0 + vite-plugin-pwa: 0.14.4_vite@4.2.0 + vite-plugin-style-import: 2.0.0_vite@4.2.0 + vite-plugin-svg-icons: 2.0.1_vite@4.2.0 + vite-plugin-windicss: 1.8.10_vite@4.2.0 + vue-eslint-parser: 9.1.0_eslint@8.36.0 + vue-tsc: 1.2.0_typescript@5.0.2 + +packages: + + /@ampproject/remapping/2.2.0: + resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.1.1 + '@jridgewell/trace-mapping': 0.3.17 + dev: true + + /@ant-design/colors/6.0.0: + resolution: {integrity: sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==} + dependencies: + '@ctrl/tinycolor': 3.6.0 + dev: false + + /@ant-design/colors/7.0.0: + resolution: {integrity: sha512-iVm/9PfGCbC0dSMBrz7oiEXZaaGH7ceU40OJEfKmyuzR9R5CRimJYPlRiFtMQGQcbNMea/ePcoIebi4ASGYXtg==} + dependencies: + '@ctrl/tinycolor': 3.6.0 + dev: false + + /@ant-design/icons-svg/4.2.1: + resolution: {integrity: sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw==} + dev: false + + /@ant-design/icons-vue/6.1.0_vue@3.2.47: + resolution: {integrity: sha512-EX6bYm56V+ZrKN7+3MT/ubDkvJ5rK/O2t380WFRflDcVFgsvl3NLH7Wxeau6R8DbrO5jWR6DSTC3B6gYFp77AA==} + peerDependencies: + vue: '>=3.0.3' + dependencies: + '@ant-design/colors': 6.0.0 + '@ant-design/icons-svg': 4.2.1 + vue: 3.2.47 + dev: false + + /@antfu/utils/0.7.2: + resolution: {integrity: sha512-vy9fM3pIxZmX07dL+VX1aZe7ynZ+YyB0jY+jE6r3hOK6GNY2t6W8rzpFC4tgpbXUYABkFQwgJq2XYXlxbXAI0g==} + dev: true + + /@apideck/better-ajv-errors/0.3.6_ajv@8.12.0: + resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + dependencies: + ajv: 8.12.0 + json-schema: 0.4.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + dev: true + + /@babel/code-frame/7.18.6: + resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/compat-data/7.21.0: + resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core/7.21.0: + resolution: {integrity: sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.21.1 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.0 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helpers': 7.21.0 + '@babel/parser': 7.21.2 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.2 + '@babel/types': 7.21.2 + convert-source-map: 1.9.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator/7.21.1: + resolution: {integrity: sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.17 + jsesc: 2.5.2 + dev: true + + /@babel/helper-annotate-as-pure/7.18.6: + resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-builder-binary-assignment-operator-visitor/7.18.9: + resolution: {integrity: sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-explode-assignable-expression': 7.18.6 + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-compilation-targets/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.0 + '@babel/helper-validator-option': 7.21.0 + browserslist: 4.21.5 + lru-cache: 5.1.1 + semver: 6.3.0 + dev: true + + /@babel/helper-create-class-features-plugin/7.21.0_@babel+core@7.21.0: + resolution: {integrity: sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-member-expression-to-functions': 7.21.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + '@babel/helper-split-export-declaration': 7.18.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-create-regexp-features-plugin/7.21.0_@babel+core@7.21.0: + resolution: {integrity: sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-annotate-as-pure': 7.18.6 + regexpu-core: 5.3.1 + dev: true + + /@babel/helper-define-polyfill-provider/0.3.3_@babel+core@7.21.0: + resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} + peerDependencies: + '@babel/core': ^7.4.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + debug: 4.3.4 + lodash.debounce: 4.0.8 + resolve: 1.22.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-environment-visitor/7.18.9: + resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-explode-assignable-expression/7.18.6: + resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-function-name/7.21.0: + resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-hoist-variables/7.18.6: + resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-member-expression-to-functions/7.21.0: + resolution: {integrity: sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-module-imports/7.18.6: + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-module-transforms/7.21.2: + resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-simple-access': 7.20.2 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.19.1 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.2 + '@babel/types': 7.21.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-optimise-call-expression/7.18.6: + resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-plugin-utils/7.20.2: + resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-remap-async-to-generator/7.18.9_@babel+core@7.21.0: + resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-wrap-function': 7.20.5 + '@babel/types': 7.21.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-replace-supers/7.20.7: + resolution: {integrity: sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-member-expression-to-functions': 7.21.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.2 + '@babel/types': 7.21.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-simple-access/7.20.2: + resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-skip-transparent-expression-wrappers/7.20.0: + resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-split-export-declaration/7.18.6: + resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.2 + dev: true + + /@babel/helper-string-parser/7.19.4: + resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-identifier/7.19.1: + resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-option/7.21.0: + resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-wrap-function/7.20.5: + resolution: {integrity: sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-function-name': 7.21.0 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.2 + '@babel/types': 7.21.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helpers/7.21.0: + resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.2 + '@babel/types': 7.21.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight/7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.19.1 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser/7.21.2: + resolution: {integrity: sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.21.2 + + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.21.0 + dev: true + + /@babel/plugin-proposal-async-generator-functions/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.21.0 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.21.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-class-static-block/7.21.0_@babel+core@7.21.0: + resolution: {integrity: sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.21.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-dynamic-import/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.21.0 + dev: true + + /@babel/plugin-proposal-export-namespace-from/7.18.9_@babel+core@7.21.0: + resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.21.0 + dev: true + + /@babel/plugin-proposal-json-strings/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.21.0 + dev: true + + /@babel/plugin-proposal-logical-assignment-operators/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.21.0 + dev: true + + /@babel/plugin-proposal-nullish-coalescing-operator/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.21.0 + dev: true + + /@babel/plugin-proposal-numeric-separator/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.21.0 + dev: true + + /@babel/plugin-proposal-object-rest-spread/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.0 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.21.0 + '@babel/plugin-transform-parameters': 7.20.7_@babel+core@7.21.0 + dev: true + + /@babel/plugin-proposal-optional-catch-binding/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.21.0 + dev: true + + /@babel/plugin-proposal-optional-chaining/7.21.0_@babel+core@7.21.0: + resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.21.0 + dev: true + + /@babel/plugin-proposal-private-methods/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-private-property-in-object/7.21.0_@babel+core@7.21.0: + resolution: {integrity: sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.21.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-unicode-property-regex/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} + engines: {node: '>=4'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.21.0: + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.21.0: + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.21.0: + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.21.0: + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.21.0: + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-import-assertions/7.20.0_@babel+core@7.21.0: + resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.21.0: + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.21.0: + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.21.0: + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.21.0: + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.21.0: + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.21.0: + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.21.0: + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.21.0: + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.21.0: + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.21.0: + resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-arrow-functions/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-async-to-generator/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.21.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-block-scoped-functions/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-block-scoping/7.21.0_@babel+core@7.21.0: + resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-classes/7.21.0_@babel+core@7.21.0: + resolution: {integrity: sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.0 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-split-export-declaration': 7.18.6 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-computed-properties/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/template': 7.20.7 + dev: true + + /@babel/plugin-transform-destructuring/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-dotall-regex/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-duplicate-keys/7.18.9_@babel+core@7.21.0: + resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-exponentiation-operator/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-for-of/7.21.0_@babel+core@7.21.0: + resolution: {integrity: sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-function-name/7.18.9_@babel+core@7.21.0: + resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.0 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-literals/7.18.9_@babel+core@7.21.0: + resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-member-expression-literals/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-modules-amd/7.20.11_@babel+core@7.21.0: + resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-commonjs/7.21.2_@babel+core@7.21.0: + resolution: {integrity: sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-simple-access': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-systemjs/7.20.11_@babel+core@7.21.0: + resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-validator-identifier': 7.19.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-umd/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-named-capturing-groups-regex/7.20.5_@babel+core@7.21.0: + resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-new-target/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-object-super/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-replace-supers': 7.20.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-parameters/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-property-literals/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-regenerator/7.20.5_@babel+core@7.21.0: + resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + regenerator-transform: 0.15.1 + dev: true + + /@babel/plugin-transform-reserved-words/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-shorthand-properties/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-spread/7.20.7_@babel+core@7.21.0: + resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + dev: true + + /@babel/plugin-transform-sticky-regex/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-template-literals/7.18.9_@babel+core@7.21.0: + resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-typeof-symbol/7.18.9_@babel+core@7.21.0: + resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-typescript/7.21.0_@babel+core@7.21.0: + resolution: {integrity: sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.21.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-unicode-escapes/7.18.10_@babel+core@7.21.0: + resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-unicode-regex/7.18.6_@babel+core@7.21.0: + resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/preset-env/7.20.2_@babel+core@7.21.0: + resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.0 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-validator-option': 7.21.0 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7_@babel+core@7.21.0 + '@babel/plugin-proposal-async-generator-functions': 7.20.7_@babel+core@7.21.0 + '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-proposal-class-static-block': 7.21.0_@babel+core@7.21.0 + '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-proposal-export-namespace-from': 7.18.9_@babel+core@7.21.0 + '@babel/plugin-proposal-json-strings': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7_@babel+core@7.21.0 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.21.0 + '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.21.0 + '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-proposal-private-property-in-object': 7.21.0_@babel+core@7.21.0 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.21.0 + '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.21.0 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.21.0 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.21.0 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.21.0 + '@babel/plugin-syntax-import-assertions': 7.20.0_@babel+core@7.21.0 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.21.0 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.21.0 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.21.0 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.21.0 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.21.0 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.21.0 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.21.0 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.21.0 + '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.21.0 + '@babel/plugin-transform-arrow-functions': 7.20.7_@babel+core@7.21.0 + '@babel/plugin-transform-async-to-generator': 7.20.7_@babel+core@7.21.0 + '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.21.0 + '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.21.0 + '@babel/plugin-transform-computed-properties': 7.20.7_@babel+core@7.21.0 + '@babel/plugin-transform-destructuring': 7.20.7_@babel+core@7.21.0 + '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.21.0 + '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-for-of': 7.21.0_@babel+core@7.21.0 + '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.21.0 + '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.21.0 + '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-modules-amd': 7.20.11_@babel+core@7.21.0 + '@babel/plugin-transform-modules-commonjs': 7.21.2_@babel+core@7.21.0 + '@babel/plugin-transform-modules-systemjs': 7.20.11_@babel+core@7.21.0 + '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5_@babel+core@7.21.0 + '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-parameters': 7.20.7_@babel+core@7.21.0 + '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-regenerator': 7.20.5_@babel+core@7.21.0 + '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-spread': 7.20.7_@babel+core@7.21.0 + '@babel/plugin-transform-sticky-regex': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.21.0 + '@babel/plugin-transform-typeof-symbol': 7.18.9_@babel+core@7.21.0 + '@babel/plugin-transform-unicode-escapes': 7.18.10_@babel+core@7.21.0 + '@babel/plugin-transform-unicode-regex': 7.18.6_@babel+core@7.21.0 + '@babel/preset-modules': 0.1.5_@babel+core@7.21.0 + '@babel/types': 7.21.2 + babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.21.0 + babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.21.0 + babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.21.0 + core-js-compat: 3.28.0 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/preset-modules/0.1.5_@babel+core@7.21.0: + resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.21.0 + '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.21.0 + '@babel/types': 7.21.2 + esutils: 2.0.3 + dev: true + + /@babel/regjsgen/0.8.0: + resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} + dev: true + + /@babel/runtime/7.21.0: + resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.11 + + /@babel/template/7.20.7: + resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/parser': 7.21.2 + '@babel/types': 7.21.2 + dev: true + + /@babel/traverse/7.21.2: + resolution: {integrity: sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.21.1 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.21.2 + '@babel/types': 7.21.2 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types/7.21.2: + resolution: {integrity: sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.19.4 + '@babel/helper-validator-identifier': 7.19.1 + to-fast-properties: 2.0.0 + + /@commitlint/cli/17.4.4: + resolution: {integrity: sha512-HwKlD7CPVMVGTAeFZylVNy14Vm5POVY0WxPkZr7EXLC/os0LH/obs6z4HRvJtH/nHCMYBvUBQhGwnufKfTjd5g==} + engines: {node: '>=v14'} + hasBin: true + dependencies: + '@commitlint/format': 17.4.4 + '@commitlint/lint': 17.4.4 + '@commitlint/load': 17.4.4 + '@commitlint/read': 17.4.4 + '@commitlint/types': 17.4.4 + execa: 5.1.1 + lodash.isfunction: 3.0.9 + resolve-from: 5.0.0 + resolve-global: 1.0.0 + yargs: 17.7.1 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + dev: true + + /@commitlint/config-conventional/17.4.4: + resolution: {integrity: sha512-u6ztvxqzi6NuhrcEDR7a+z0yrh11elY66nRrQIpqsqW6sZmpxYkDLtpRH8jRML+mmxYQ8s4qqF06Q/IQx5aJeQ==} + engines: {node: '>=v14'} + dependencies: + conventional-changelog-conventionalcommits: 5.0.0 + dev: true + + /@commitlint/config-validator/17.4.4: + resolution: {integrity: sha512-bi0+TstqMiqoBAQDvdEP4AFh0GaKyLFlPPEObgI29utoKEYoPQTvF0EYqIwYYLEoJYhj5GfMIhPHJkTJhagfeg==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/types': 17.4.4 + ajv: 8.12.0 + dev: true + + /@commitlint/ensure/17.4.4: + resolution: {integrity: sha512-AHsFCNh8hbhJiuZ2qHv/m59W/GRE9UeOXbkOqxYMNNg9pJ7qELnFcwj5oYpa6vzTSHtPGKf3C2yUFNy1GGHq6g==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/types': 17.4.4 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.upperfirst: 4.3.1 + dev: true + + /@commitlint/execute-rule/17.4.0: + resolution: {integrity: sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==} + engines: {node: '>=v14'} + dev: true + + /@commitlint/format/17.4.4: + resolution: {integrity: sha512-+IS7vpC4Gd/x+uyQPTAt3hXs5NxnkqAZ3aqrHd5Bx/R9skyCAWusNlNbw3InDbAK6j166D9asQM8fnmYIa+CXQ==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/types': 17.4.4 + chalk: 4.1.2 + dev: true + + /@commitlint/is-ignored/17.4.4: + resolution: {integrity: sha512-Y3eo1SFJ2JQDik4rWkBC4tlRIxlXEFrRWxcyrzb1PUT2k3kZ/XGNuCDfk/u0bU2/yS0tOA/mTjFsV+C4qyACHw==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/types': 17.4.4 + semver: 7.3.8 + dev: true + + /@commitlint/lint/17.4.4: + resolution: {integrity: sha512-qgkCRRFjyhbMDWsti/5jRYVJkgYZj4r+ZmweZObnbYqPUl5UKLWMf9a/ZZisOI4JfiPmRktYRZ2JmqlSvg+ccw==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/is-ignored': 17.4.4 + '@commitlint/parse': 17.4.4 + '@commitlint/rules': 17.4.4 + '@commitlint/types': 17.4.4 + dev: true + + /@commitlint/load/17.4.4: + resolution: {integrity: sha512-z6uFIQ7wfKX5FGBe1AkOF4l/ShOQsaa1ml/nLMkbW7R/xF8galGS7Zh0yHvzVp/srtfS0brC+0bUfQfmpMPFVQ==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/config-validator': 17.4.4 + '@commitlint/execute-rule': 17.4.0 + '@commitlint/resolve-extends': 17.4.4 + '@commitlint/types': 17.4.4 + '@types/node': 18.15.3 + chalk: 4.1.2 + cosmiconfig: 8.0.0 + cosmiconfig-typescript-loader: 4.3.0_o5d5d5qrhzipaz5nmkhyrkswza + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + resolve-from: 5.0.0 + ts-node: 10.9.1_cbfmry4sbbh4vatmdrsmatfg5a + typescript: 4.9.5 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + dev: true + + /@commitlint/message/17.4.2: + resolution: {integrity: sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==} + engines: {node: '>=v14'} + dev: true + + /@commitlint/parse/17.4.4: + resolution: {integrity: sha512-EKzz4f49d3/OU0Fplog7nwz/lAfXMaDxtriidyGF9PtR+SRbgv4FhsfF310tKxs6EPj8Y+aWWuX3beN5s+yqGg==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/types': 17.4.4 + conventional-changelog-angular: 5.0.13 + conventional-commits-parser: 3.2.4 + dev: true + + /@commitlint/read/17.4.4: + resolution: {integrity: sha512-B2TvUMJKK+Svzs6eji23WXsRJ8PAD+orI44lVuVNsm5zmI7O8RSGJMvdEZEikiA4Vohfb+HevaPoWZ7PiFZ3zA==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/top-level': 17.4.0 + '@commitlint/types': 17.4.4 + fs-extra: 11.1.0 + git-raw-commits: 2.0.11 + minimist: 1.2.8 + dev: true + + /@commitlint/resolve-extends/17.4.4: + resolution: {integrity: sha512-znXr1S0Rr8adInptHw0JeLgumS11lWbk5xAWFVno+HUFVN45875kUtqjrI6AppmD3JI+4s0uZlqqlkepjJd99A==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/config-validator': 17.4.4 + '@commitlint/types': 17.4.4 + import-fresh: 3.3.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + resolve-global: 1.0.0 + dev: true + + /@commitlint/rules/17.4.4: + resolution: {integrity: sha512-0tgvXnHi/mVcyR8Y8mjTFZIa/FEQXA4uEutXS/imH2v1UNkYDSEMsK/68wiXRpfW1euSgEdwRkvE1z23+yhNrQ==} + engines: {node: '>=v14'} + dependencies: + '@commitlint/ensure': 17.4.4 + '@commitlint/message': 17.4.2 + '@commitlint/to-lines': 17.4.0 + '@commitlint/types': 17.4.4 + execa: 5.1.1 + dev: true + + /@commitlint/to-lines/17.4.0: + resolution: {integrity: sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==} + engines: {node: '>=v14'} + dev: true + + /@commitlint/top-level/17.4.0: + resolution: {integrity: sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==} + engines: {node: '>=v14'} + dependencies: + find-up: 5.0.0 + dev: true + + /@commitlint/types/17.4.4: + resolution: {integrity: sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ==} + engines: {node: '>=v14'} + dependencies: + chalk: 4.1.2 + dev: true + + /@cspotcode/source-map-support/0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@csstools/selector-specificity/2.1.1_wajs5nedgkikc5pcuwett7legi: + resolution: {integrity: sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + postcss-selector-parser: ^6.0.10 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /@ctrl/tinycolor/3.6.0: + resolution: {integrity: sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==} + engines: {node: '>=10'} + dev: false + + /@esbuild-kit/cjs-loader/2.4.2: + resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==} + dependencies: + '@esbuild-kit/core-utils': 3.1.0 + get-tsconfig: 4.4.0 + dev: true + + /@esbuild-kit/core-utils/3.1.0: + resolution: {integrity: sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==} + dependencies: + esbuild: 0.17.10 + source-map-support: 0.5.21 + dev: true + + /@esbuild-kit/esm-loader/2.5.5: + resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==} + dependencies: + '@esbuild-kit/core-utils': 3.1.0 + get-tsconfig: 4.4.0 + dev: true + + /@esbuild/android-arm/0.15.18: + resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm/0.17.10: + resolution: {integrity: sha512-7YEBfZ5lSem9Tqpsz+tjbdsEshlO9j/REJrfv4DXgKTt1+/MHqGwbtlyxQuaSlMeUZLxUKBaX8wdzlTfHkmnLw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64/0.17.10: + resolution: {integrity: sha512-ht1P9CmvrPF5yKDtyC+z43RczVs4rrHpRqrmIuoSvSdn44Fs1n6DGlpZKdK6rM83pFLbVaSUwle8IN+TPmkv7g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64/0.17.10: + resolution: {integrity: sha512-CYzrm+hTiY5QICji64aJ/xKdN70IK8XZ6iiyq0tZkd3tfnwwSWTYH1t3m6zyaaBxkuj40kxgMyj1km/NqdjQZA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64/0.17.10: + resolution: {integrity: sha512-3HaGIowI+nMZlopqyW6+jxYr01KvNaLB5znXfbyyjuo4lE0VZfvFGcguIJapQeQMS4cX/NEispwOekJt3gr5Dg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64/0.17.10: + resolution: {integrity: sha512-J4MJzGchuCRG5n+B4EHpAMoJmBeAE1L3wGYDIN5oWNqX0tEr7VKOzw0ymSwpoeSpdCa030lagGUfnfhS7OvzrQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64/0.17.10: + resolution: {integrity: sha512-ZkX40Z7qCbugeK4U5/gbzna/UQkM9d9LNV+Fro8r7HA7sRof5Rwxc46SsqeMvB5ZaR0b1/ITQ/8Y1NmV2F0fXQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64/0.17.10: + resolution: {integrity: sha512-0m0YX1IWSLG9hWh7tZa3kdAugFbZFFx9XrvfpaCMMvrswSTvUZypp0NFKriUurHpBA3xsHVE9Qb/0u2Bbi/otg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm/0.17.10: + resolution: {integrity: sha512-whRdrrl0X+9D6o5f0sTZtDM9s86Xt4wk1bf7ltx6iQqrIIOH+sre1yjpcCdrVXntQPCNw/G+XqsD4HuxeS+2QA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64/0.17.10: + resolution: {integrity: sha512-g1EZJR1/c+MmCgVwpdZdKi4QAJ8DCLP5uTgLWSAVd9wlqk9GMscaNMEViG3aE1wS+cNMzXXgdWiW/VX4J+5nTA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32/0.17.10: + resolution: {integrity: sha512-1vKYCjfv/bEwxngHERp7huYfJ4jJzldfxyfaF7hc3216xiDA62xbXJfRlradiMhGZbdNLj2WA1YwYFzs9IWNPw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64/0.15.18: + resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64/0.17.10: + resolution: {integrity: sha512-mvwAr75q3Fgc/qz3K6sya3gBmJIYZCgcJ0s7XshpoqIAIBszzfXsqhpRrRdVFAyV1G9VUjj7VopL2HnAS8aHFA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el/0.17.10: + resolution: {integrity: sha512-XilKPgM2u1zR1YuvCsFQWl9Fc35BqSqktooumOY2zj7CSn5czJn279j9TE1JEqSqz88izJo7yE4x3LSf7oxHzg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64/0.17.10: + resolution: {integrity: sha512-kM4Rmh9l670SwjlGkIe7pYWezk8uxKHX4Lnn5jBZYBNlWpKMBCVfpAgAJqp5doLobhzF3l64VZVrmGeZ8+uKmQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64/0.17.10: + resolution: {integrity: sha512-r1m9ZMNJBtOvYYGQVXKy+WvWd0BPvSxMsVq8Hp4GzdMBQvfZRvRr5TtX/1RdN6Va8JMVQGpxqde3O+e8+khNJQ==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x/0.17.10: + resolution: {integrity: sha512-LsY7QvOLPw9WRJ+fU5pNB3qrSfA00u32ND5JVDrn/xG5hIQo3kvTxSlWFRP0NJ0+n6HmhPGG0Q4jtQsb6PFoyg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64/0.17.10: + resolution: {integrity: sha512-zJUfJLebCYzBdIz/Z9vqwFjIA7iSlLCFvVi7glMgnu2MK7XYigwsonXshy9wP9S7szF+nmwrelNaP3WGanstEg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64/0.17.10: + resolution: {integrity: sha512-lOMkailn4Ok9Vbp/q7uJfgicpDTbZFlXlnKT2DqC8uBijmm5oGtXAJy2ZZVo5hX7IOVXikV9LpCMj2U8cTguWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64/0.17.10: + resolution: {integrity: sha512-/VE0Kx6y7eekqZ+ZLU4AjMlB80ov9tEz4H067Y0STwnGOYL8CsNg4J+cCmBznk1tMpxMoUOf0AbWlb1d2Pkbig==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64/0.17.10: + resolution: {integrity: sha512-ERNO0838OUm8HfUjjsEs71cLjLMu/xt6bhOlxcJ0/1MG3hNqCmbWaS+w/8nFLa0DDjbwZQuGKVtCUJliLmbVgg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64/0.17.10: + resolution: {integrity: sha512-fXv+L+Bw2AeK+XJHwDAQ9m3NRlNemG6Z6ijLwJAAVdu4cyoFbBWbEtyZzDeL+rpG2lWI51cXeMt70HA8g2MqIg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32/0.17.10: + resolution: {integrity: sha512-3s+HADrOdCdGOi5lnh5DMQEzgbsFsd4w57L/eLKKjMnN0CN4AIEP0DCP3F3N14xnxh3ruNc32A0Na9zYe1Z/AQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64/0.17.10: + resolution: {integrity: sha512-oP+zFUjYNaMNmjTwlFtWep85hvwUu19cZklB3QsBOcZSs6y7hmH4LNCJ7075bsqzYaNvZFXJlAVaQ2ApITDXtw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils/4.2.0_eslint@8.36.0: + resolution: {integrity: sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.36.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /@eslint-community/regexpp/4.4.0: + resolution: {integrity: sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc/2.0.1: + resolution: {integrity: sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.5.0 + globals: 13.20.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js/8.36.0: + resolution: {integrity: sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array/0.11.8: + resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer/1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema/1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@hutson/parse-repository-url/3.0.2: + resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} + engines: {node: '>=6.9.0'} + dev: true + + /@iconify/iconify/2.1.2: + resolution: {integrity: sha512-QcUzFeEWkE/mW+BVtEGmcWATClcCOIJFiYUD/PiCWuTcdEA297o8D4oN6Ra44WrNOHu1wqNW4J0ioaDIiqaFOQ==} + dependencies: + cross-fetch: 3.1.5 + transitivePeerDependencies: + - encoding + dev: true + + /@iconify/iconify/3.1.0: + resolution: {integrity: sha512-Xyz+N5NSIiHj7G228CvFUcMKBz3BbpUTAuPnK2ariEUM7891F8ysD6Gh8782f3En1U4Qz70F0tzuyF8BhCfTQg==} + dependencies: + '@iconify/types': 2.0.0 + + /@iconify/json/2.2.36: + resolution: {integrity: sha512-M3NzzLjmE5udIO24EgT9MV1LfU5FQDw9nfAfgrV3NySiLMtZFoOvFEdURJihH2SJWCoFhIpRRkgSZKN8qWIhuQ==} + dependencies: + '@iconify/types': 2.0.0 + pathe: 1.1.0 + dev: true + + /@iconify/types/2.0.0: + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + /@intlify/core-base/9.2.2: + resolution: {integrity: sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==} + engines: {node: '>= 14'} + dependencies: + '@intlify/devtools-if': 9.2.2 + '@intlify/message-compiler': 9.2.2 + '@intlify/shared': 9.2.2 + '@intlify/vue-devtools': 9.2.2 + dev: false + + /@intlify/devtools-if/9.2.2: + resolution: {integrity: sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==} + engines: {node: '>= 14'} + dependencies: + '@intlify/shared': 9.2.2 + dev: false + + /@intlify/message-compiler/9.2.2: + resolution: {integrity: sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==} + engines: {node: '>= 14'} + dependencies: + '@intlify/shared': 9.2.2 + source-map: 0.6.1 + dev: false + + /@intlify/shared/9.2.2: + resolution: {integrity: sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==} + engines: {node: '>= 14'} + dev: false + + /@intlify/vue-devtools/9.2.2: + resolution: {integrity: sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==} + engines: {node: '>= 14'} + dependencies: + '@intlify/core-base': 9.2.2 + '@intlify/shared': 9.2.2 + dev: false + + /@jridgewell/gen-mapping/0.1.1: + resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@jridgewell/gen-mapping/0.3.2: + resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/trace-mapping': 0.3.17 + dev: true + + /@jridgewell/resolve-uri/3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array/1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/source-map/0.3.2: + resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==} + dependencies: + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.17 + dev: true + + /@jridgewell/sourcemap-codec/1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@jridgewell/trace-mapping/0.3.17: + resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@jridgewell/trace-mapping/0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@kirklin/vite-plugin-vben-theme/0.1.1_vite@4.2.0: + resolution: {integrity: sha512-3IvbxeJ1hb+RFKqTzgwjhF9/WHwN41jMAfJANa7pNIoRKfVRp9bprTGLZtZ0OiK6jztgNuJS8QZ4jYPj3dn5WA==} + peerDependencies: + vite: '>=3.2.0' + dependencies: + '@types/node': 18.15.3 + '@types/tinycolor2': 1.4.3 + clean-css: 5.3.2 + debug: 4.3.4 + esbuild: 0.15.18 + esbuild-plugin-alias: 0.2.1 + picocolors: 1.0.0 + tinycolor2: 1.6.0 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + transitivePeerDependencies: + - supports-color + dev: true + + /@logicflow/core/1.2.1: + resolution: {integrity: sha512-m6/K7I85jFfCWcrNEQQpchomUWM4ZUjcOhHjsVhejnfpoEx7jke3tSJlh41LyT9hIUEIy7nxnphp5L4YB+InCA==} + dependencies: + '@types/mousetrap': 1.6.11 + mousetrap: 1.6.5 + preact: 10.12.1 + dev: false + + /@logicflow/extension/1.2.1: + resolution: {integrity: sha512-C006Y73sqe0x5ArWzTWc2YglhDNs+Ck/eXJnnaZ89uYqyVDdLgr/6RBjL8JAV41Lg7EQMB1yqmfUW53tCPTRtw==} + dependencies: + '@logicflow/core': 1.2.1 + ids: 1.0.0 + lodash-es: 4.17.21 + preact: 10.12.1 + dev: false + + /@nodelib/fs.scandir/2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat/2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk/1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@octokit/auth-token/3.0.3: + resolution: {integrity: sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==} + engines: {node: '>= 14'} + dependencies: + '@octokit/types': 9.0.0 + dev: true + + /@octokit/core/4.2.0: + resolution: {integrity: sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==} + engines: {node: '>= 14'} + dependencies: + '@octokit/auth-token': 3.0.3 + '@octokit/graphql': 5.0.5 + '@octokit/request': 6.2.3 + '@octokit/request-error': 3.0.3 + '@octokit/types': 9.0.0 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.0 + transitivePeerDependencies: + - encoding + dev: true + + /@octokit/endpoint/7.0.5: + resolution: {integrity: sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA==} + engines: {node: '>= 14'} + dependencies: + '@octokit/types': 9.0.0 + is-plain-object: 5.0.0 + universal-user-agent: 6.0.0 + dev: true + + /@octokit/graphql/5.0.5: + resolution: {integrity: sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==} + engines: {node: '>= 14'} + dependencies: + '@octokit/request': 6.2.3 + '@octokit/types': 9.0.0 + universal-user-agent: 6.0.0 + transitivePeerDependencies: + - encoding + dev: true + + /@octokit/openapi-types/16.0.0: + resolution: {integrity: sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==} + dev: true + + /@octokit/plugin-paginate-rest/6.0.0_@octokit+core@4.2.0: + resolution: {integrity: sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==} + engines: {node: '>= 14'} + peerDependencies: + '@octokit/core': '>=4' + dependencies: + '@octokit/core': 4.2.0 + '@octokit/types': 9.0.0 + dev: true + + /@octokit/plugin-request-log/1.0.4_@octokit+core@4.2.0: + resolution: {integrity: sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==} + peerDependencies: + '@octokit/core': '>=3' + dependencies: + '@octokit/core': 4.2.0 + dev: true + + /@octokit/plugin-rest-endpoint-methods/7.0.1_@octokit+core@4.2.0: + resolution: {integrity: sha512-pnCaLwZBudK5xCdrR823xHGNgqOzRnJ/mpC/76YPpNP7DybdsJtP7mdOwh+wYZxK5jqeQuhu59ogMI4NRlBUvA==} + engines: {node: '>= 14'} + peerDependencies: + '@octokit/core': '>=3' + dependencies: + '@octokit/core': 4.2.0 + '@octokit/types': 9.0.0 + deprecation: 2.3.1 + dev: true + + /@octokit/request-error/3.0.3: + resolution: {integrity: sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==} + engines: {node: '>= 14'} + dependencies: + '@octokit/types': 9.0.0 + deprecation: 2.3.1 + once: 1.4.0 + dev: true + + /@octokit/request/6.2.3: + resolution: {integrity: sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA==} + engines: {node: '>= 14'} + dependencies: + '@octokit/endpoint': 7.0.5 + '@octokit/request-error': 3.0.3 + '@octokit/types': 9.0.0 + is-plain-object: 5.0.0 + node-fetch: 2.6.9 + universal-user-agent: 6.0.0 + transitivePeerDependencies: + - encoding + dev: true + + /@octokit/rest/19.0.7: + resolution: {integrity: sha512-HRtSfjrWmWVNp2uAkEpQnuGMJsu/+dBr47dRc5QVgsCbnIc1+GFEaoKBWkYG+zjrsHpSqcAElMio+n10c0b5JA==} + engines: {node: '>= 14'} + dependencies: + '@octokit/core': 4.2.0 + '@octokit/plugin-paginate-rest': 6.0.0_@octokit+core@4.2.0 + '@octokit/plugin-request-log': 1.0.4_@octokit+core@4.2.0 + '@octokit/plugin-rest-endpoint-methods': 7.0.1_@octokit+core@4.2.0 + transitivePeerDependencies: + - encoding + dev: true + + /@octokit/types/9.0.0: + resolution: {integrity: sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==} + dependencies: + '@octokit/openapi-types': 16.0.0 + dev: true + + /@purge-icons/core/0.9.1: + resolution: {integrity: sha512-sx8/a30MbbqQVEqhuMPE1wJpdVRRbEmwEPZpFzVkcDixzX4p+R2A0WVxqkb0xfHUBAVQwrSE2SeAyniIQLqbLw==} + dependencies: + '@iconify/iconify': 2.1.2 + axios: 0.26.1_debug@4.3.4 + debug: 4.3.4 + fast-glob: 3.2.12 + fs-extra: 10.1.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /@purge-icons/generated/0.9.0: + resolution: {integrity: sha512-s2t+1oVtGDV6KtqfCXtUOhxfeYvOdDF90IVm+nMs/6bUP0HeGZLslguuL/AibpwtfL4FA/oCsIu/RhwapgAdJw==} + dependencies: + '@iconify/iconify': 3.1.0 + dev: true + + /@rollup/plugin-babel/5.3.1_4tnfxcmsyr7y5qv3uwkivwqysm: + resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-module-imports': 7.18.6 + '@rollup/pluginutils': 3.1.0_rollup@2.79.1 + rollup: 2.79.1 + dev: true + + /@rollup/plugin-node-resolve/11.2.1_rollup@2.79.1: + resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} + engines: {node: '>= 10.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.79.1 + '@types/resolve': 1.17.1 + builtin-modules: 3.3.0 + deepmerge: 4.3.0 + is-module: 1.0.0 + resolve: 1.22.1 + rollup: 2.79.1 + dev: true + + /@rollup/plugin-node-resolve/13.3.0_rollup@3.19.1: + resolution: {integrity: sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==} + engines: {node: '>= 10.0.0'} + peerDependencies: + rollup: ^2.42.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@3.19.1 + '@types/resolve': 1.17.1 + deepmerge: 4.3.0 + is-builtin-module: 3.2.1 + is-module: 1.0.0 + resolve: 1.22.1 + rollup: 3.19.1 + dev: true + + /@rollup/plugin-replace/2.4.2_rollup@2.79.1: + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.79.1 + magic-string: 0.25.9 + rollup: 2.79.1 + dev: true + + /@rollup/plugin-replace/5.0.2_rollup@3.19.1: + resolution: {integrity: sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.2_rollup@3.19.1 + magic-string: 0.27.0 + rollup: 3.19.1 + dev: true + + /@rollup/pluginutils/3.1.0_rollup@2.79.1: + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 2.79.1 + dev: true + + /@rollup/pluginutils/3.1.0_rollup@3.19.1: + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 3.19.1 + dev: true + + /@rollup/pluginutils/4.2.1: + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + dev: true + + /@rollup/pluginutils/5.0.2_rollup@3.19.1: + resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.0 + estree-walker: 2.0.2 + picomatch: 2.3.1 + rollup: 3.19.1 + dev: true + + /@simonwep/pickr/1.8.2: + resolution: {integrity: sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==} + dependencies: + core-js: 3.29.1 + nanopop: 2.2.0 + dev: false + + /@surma/rollup-plugin-off-main-thread/2.2.3: + resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} + dependencies: + ejs: 3.1.8 + json5: 2.2.3 + magic-string: 0.25.9 + string.prototype.matchall: 4.0.8 + dev: true + + /@trysound/sax/0.2.0: + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + dev: true + + /@tsconfig/node10/1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12/1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14/1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16/1.0.3: + resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} + dev: true + + /@types/codemirror/5.60.7: + resolution: {integrity: sha512-QXIC+RPzt/1BGSuD6iFn6UMC9TDp+9hkOANYNPVsjjrDdzKphfRkwQDKGp2YaC54Yhz0g6P5uYTCCibZZEiMAA==} + dependencies: + '@types/tern': 0.23.4 + dev: true + + /@types/crypto-js/4.1.1: + resolution: {integrity: sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==} + dev: true + + /@types/estree/0.0.39: + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + dev: true + + /@types/estree/1.0.0: + resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} + dev: true + + /@types/fs-extra/11.0.1: + resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==} + dependencies: + '@types/jsonfile': 6.1.1 + '@types/node': 18.15.3 + dev: true + + /@types/inquirer/9.0.3: + resolution: {integrity: sha512-CzNkWqQftcmk2jaCWdBTf9Sm7xSw4rkI1zpU/Udw3HX5//adEZUIm9STtoRP1qgWj0CWQtJ9UTvqmO2NNjhMJw==} + dependencies: + '@types/through': 0.0.30 + rxjs: 7.8.0 + dev: true + + /@types/intro.js/5.1.1: + resolution: {integrity: sha512-gxrfhzwHeCZI8PoucIVRCe5cX0j29YYB1YLIfPb87HN1HiAhrl0CGMFuYPzo6Gvn5diAPCHF6XW2SR+Lqxexlg==} + dev: true + + /@types/json-schema/7.0.11: + resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + dev: true + + /@types/jsonfile/6.1.1: + resolution: {integrity: sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==} + dependencies: + '@types/node': 18.15.3 + dev: true + + /@types/lodash-es/4.17.7: + resolution: {integrity: sha512-z0ptr6UI10VlU6l5MYhGwS4mC8DZyYer2mCoyysZtSF7p26zOX8UpbrV0YpNYLGS8K4PUFIyEr62IMFFjveSiQ==} + dependencies: + '@types/lodash': 4.14.191 + dev: true + + /@types/lodash/4.14.191: + resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==} + dev: true + + /@types/minimist/1.2.2: + resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} + dev: true + + /@types/mockjs/1.0.7: + resolution: {integrity: sha512-OCxXz6hEaJOVpRwuJMiVY5a6LtJcih+br9gwB/Q8ooOBikvk5FpBQ31OlNimXo3EqKha1Z7PFBni+q9m+8NCWg==} + dev: true + + /@types/mousetrap/1.6.11: + resolution: {integrity: sha512-F0oAily9Q9QQpv9JKxKn0zMKfOo36KHCW7myYsmUyf2t0g+sBTbG3UleTPoguHdE1z3GLFr3p7/wiOio52QFjQ==} + dev: false + + /@types/node/10.17.60: + resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} + dev: true + + /@types/node/18.15.3: + resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==} + dev: true + + /@types/normalize-package-data/2.4.1: + resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} + dev: true + + /@types/nprogress/0.2.0: + resolution: {integrity: sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==} + dev: true + + /@types/parse-json/4.0.0: + resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} + dev: true + + /@types/qrcode/1.5.0: + resolution: {integrity: sha512-x5ilHXRxUPIMfjtM+1vf/GPTRWZ81nqscursm5gMznJeK9M0YnZ1c3bEvRLQ0zSSgedLx1J6MGL231ObQGGhaA==} + dependencies: + '@types/node': 18.15.3 + dev: true + + /@types/qs/6.9.7: + resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} + dev: true + + /@types/resolve/1.17.1: + resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} + dependencies: + '@types/node': 18.15.3 + dev: true + + /@types/semver/7.3.13: + resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} + dev: true + + /@types/showdown/2.0.0: + resolution: {integrity: sha512-70xBJoLv+oXjB5PhtA8vo7erjLDp9/qqI63SRHm4REKrwuPOLs8HhXwlZJBJaB4kC18cCZ1UUZ6Fb/PLFW4TCA==} + dev: true + + /@types/sortablejs/1.15.1: + resolution: {integrity: sha512-g/JwBNToh6oCTAwNS8UGVmjO7NLDKsejVhvE4x1eWiPTC3uCuNsa/TD4ssvX3du+MLiM+SHPNDuijp8y76JzLQ==} + dev: true + + /@types/svgo/2.6.4: + resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==} + dependencies: + '@types/node': 18.15.3 + dev: true + + /@types/tern/0.23.4: + resolution: {integrity: sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==} + dependencies: + '@types/estree': 1.0.0 + dev: true + + /@types/through/0.0.30: + resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==} + dependencies: + '@types/node': 18.15.3 + dev: true + + /@types/tinycolor2/1.4.3: + resolution: {integrity: sha512-Kf1w9NE5HEgGxCRyIcRXR/ZYtDv0V8FVPtYHwLxl0O+maGX0erE77pQlD0gpP+/KByMZ87mOA79SjifhSB3PjQ==} + dev: true + + /@types/trusted-types/2.0.3: + resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==} + dev: true + + /@types/web-bluetooth/0.0.16: + resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} + dev: false + + /@typescript-eslint/eslint-plugin/5.55.0_qsnvknysi52qtaxqdyqyohkcku: + resolution: {integrity: sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.4.0 + '@typescript-eslint/parser': 5.55.0_j4766f7ecgqbon3u7zlxn5zszu + '@typescript-eslint/scope-manager': 5.55.0 + '@typescript-eslint/type-utils': 5.55.0_j4766f7ecgqbon3u7zlxn5zszu + '@typescript-eslint/utils': 5.55.0_j4766f7ecgqbon3u7zlxn5zszu + debug: 4.3.4 + eslint: 8.36.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.3.8 + tsutils: 3.21.0_typescript@5.0.2 + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser/5.55.0_j4766f7ecgqbon3u7zlxn5zszu: + resolution: {integrity: sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.55.0 + '@typescript-eslint/types': 5.55.0 + '@typescript-eslint/typescript-estree': 5.55.0_typescript@5.0.2 + debug: 4.3.4 + eslint: 8.36.0 + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager/5.55.0: + resolution: {integrity: sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.55.0 + '@typescript-eslint/visitor-keys': 5.55.0 + dev: true + + /@typescript-eslint/type-utils/5.55.0_j4766f7ecgqbon3u7zlxn5zszu: + resolution: {integrity: sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.55.0_typescript@5.0.2 + '@typescript-eslint/utils': 5.55.0_j4766f7ecgqbon3u7zlxn5zszu + debug: 4.3.4 + eslint: 8.36.0 + tsutils: 3.21.0_typescript@5.0.2 + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types/5.55.0: + resolution: {integrity: sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree/5.55.0_typescript@5.0.2: + resolution: {integrity: sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.55.0 + '@typescript-eslint/visitor-keys': 5.55.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 + tsutils: 3.21.0_typescript@5.0.2 + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils/5.55.0_j4766f7ecgqbon3u7zlxn5zszu: + resolution: {integrity: sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.2.0_eslint@8.36.0 + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.55.0 + '@typescript-eslint/types': 5.55.0 + '@typescript-eslint/typescript-estree': 5.55.0_typescript@5.0.2 + eslint: 8.36.0 + eslint-scope: 5.1.1 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys/5.55.0: + resolution: {integrity: sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.55.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /@vitejs/plugin-legacy/4.0.2_terser@5.16.6+vite@4.2.0: + resolution: {integrity: sha512-ivnt9sCkgwJTYTWLjuvY6H/HTuiQC1EgzAPkiAvi0yNAssiqOJjyjhG3hAK5LFUUorE0w9kGxn8K0f/74DlbxQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + terser: ^5.4.0 + vite: ^4.0.0 + dependencies: + '@babel/core': 7.21.0 + '@babel/preset-env': 7.20.2_@babel+core@7.21.0 + browserslist: 4.21.5 + core-js: 3.29.1 + magic-string: 0.30.0 + regenerator-runtime: 0.13.11 + systemjs: 6.14.0 + terser: 5.16.6 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + transitivePeerDependencies: + - supports-color + dev: true + + /@vitejs/plugin-vue-jsx/3.0.1_vite@4.2.0+vue@3.2.47: + resolution: {integrity: sha512-+Jb7ggL48FSPS1uhPnJbJwWa9Sr90vQ+d0InW+AhBM22n+cfuYqJZDckBc+W3QSHe1WDvewMZfa4wZOtk5pRgw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 + vue: ^3.0.0 + dependencies: + '@babel/core': 7.21.0 + '@babel/plugin-transform-typescript': 7.21.0_@babel+core@7.21.0 + '@vue/babel-plugin-jsx': 1.1.1_@babel+core@7.21.0 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + vue: 3.2.47 + transitivePeerDependencies: + - supports-color + dev: true + + /@vitejs/plugin-vue/4.1.0_vite@4.2.0+vue@3.2.47: + resolution: {integrity: sha512-++9JOAFdcXI3lyer9UKUV4rfoQ3T1RN8yDqoCLar86s0xQct5yblxAE+yWgRnU5/0FOlVCpTZpYSBV/bGWrSrQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 + vue: ^3.2.25 + dependencies: + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + vue: 3.2.47 + dev: true + + /@volar/language-core/1.3.0-alpha.0: + resolution: {integrity: sha512-W3uMzecHPcbwddPu4SJpUcPakRBK/y/BP+U0U6NiPpUX1tONLC4yCawt+QBJqtgJ+sfD6ztf5PyvPL3hQRqfOA==} + dependencies: + '@volar/source-map': 1.3.0-alpha.0 + dev: true + + /@volar/source-map/1.3.0-alpha.0: + resolution: {integrity: sha512-jSdizxWFvDTvkPYZnO6ew3sBZUnS0abKCbuopkc0JrIlFbznWC/fPH3iPFIMS8/IIkRxq1Jh9VVG60SmtsdaMQ==} + dependencies: + muggle-string: 0.2.2 + dev: true + + /@volar/typescript/1.3.0-alpha.0: + resolution: {integrity: sha512-5UItyW2cdH2mBLu4RrECRNJRgtvvzKrSCn2y3v/D61QwIDkGx4aeil6x8RFuUL5TFtV6QvVHXnsOHxNgd+sCow==} + dependencies: + '@volar/language-core': 1.3.0-alpha.0 + dev: true + + /@volar/vue-language-core/1.2.0: + resolution: {integrity: sha512-w7yEiaITh2WzKe6u8ZdeLKCUz43wdmY/OqAmsB/PGDvvhTcVhCJ6f0W/RprZL1IhqH8wALoWiwEh/Wer7ZviMQ==} + dependencies: + '@volar/language-core': 1.3.0-alpha.0 + '@volar/source-map': 1.3.0-alpha.0 + '@vue/compiler-dom': 3.2.47 + '@vue/compiler-sfc': 3.2.47 + '@vue/reactivity': 3.2.47 + '@vue/shared': 3.2.47 + minimatch: 6.2.0 + muggle-string: 0.2.2 + vue-template-compiler: 2.7.14 + dev: true + + /@volar/vue-typescript/1.2.0: + resolution: {integrity: sha512-zjmRi9y3J1EkG+pfuHp8IbHmibihrKK485cfzsHjiuvJMGrpkWvlO5WVEk8oslMxxeGC5XwBFE9AOlvh378EPA==} + dependencies: + '@volar/typescript': 1.3.0-alpha.0 + '@volar/vue-language-core': 1.2.0 + dev: true + + /@vue/babel-helper-vue-transform-on/1.0.2: + resolution: {integrity: sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==} + dev: true + + /@vue/babel-plugin-jsx/1.1.1_@babel+core@7.21.0: + resolution: {integrity: sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==} + dependencies: + '@babel/helper-module-imports': 7.18.6 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.21.0 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.2 + '@babel/types': 7.21.2 + '@vue/babel-helper-vue-transform-on': 1.0.2 + camelcase: 6.3.0 + html-tags: 3.2.0 + svg-tags: 1.0.0 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@vue/compiler-core/3.2.47: + resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} + dependencies: + '@babel/parser': 7.21.2 + '@vue/shared': 3.2.47 + estree-walker: 2.0.2 + source-map: 0.6.1 + + /@vue/compiler-dom/3.2.47: + resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==} + dependencies: + '@vue/compiler-core': 3.2.47 + '@vue/shared': 3.2.47 + + /@vue/compiler-sfc/3.2.47: + resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} + dependencies: + '@babel/parser': 7.21.2 + '@vue/compiler-core': 3.2.47 + '@vue/compiler-dom': 3.2.47 + '@vue/compiler-ssr': 3.2.47 + '@vue/reactivity-transform': 3.2.47 + '@vue/shared': 3.2.47 + estree-walker: 2.0.2 + magic-string: 0.25.9 + postcss: 8.4.21 + source-map: 0.6.1 + + /@vue/compiler-ssr/3.2.47: + resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==} + dependencies: + '@vue/compiler-dom': 3.2.47 + '@vue/shared': 3.2.47 + + /@vue/devtools-api/6.5.0: + resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==} + dev: false + + /@vue/reactivity-transform/3.2.47: + resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} + dependencies: + '@babel/parser': 7.21.2 + '@vue/compiler-core': 3.2.47 + '@vue/shared': 3.2.47 + estree-walker: 2.0.2 + magic-string: 0.25.9 + + /@vue/reactivity/3.2.47: + resolution: {integrity: sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==} + dependencies: + '@vue/shared': 3.2.47 + + /@vue/runtime-core/3.2.47: + resolution: {integrity: sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==} + dependencies: + '@vue/reactivity': 3.2.47 + '@vue/shared': 3.2.47 + + /@vue/runtime-dom/3.2.47: + resolution: {integrity: sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==} + dependencies: + '@vue/runtime-core': 3.2.47 + '@vue/shared': 3.2.47 + csstype: 2.6.21 + + /@vue/server-renderer/3.2.47_vue@3.2.47: + resolution: {integrity: sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==} + peerDependencies: + vue: 3.2.47 + dependencies: + '@vue/compiler-ssr': 3.2.47 + '@vue/shared': 3.2.47 + vue: 3.2.47 + + /@vue/shared/3.2.47: + resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==} + + /@vue/test-utils/2.3.1_vue@3.2.47: + resolution: {integrity: sha512-tRtHRPEETQSUrqXgAewNZHm5iypxDFxwenfdcvMRm1kbGo4bcqHb1XHHlsaIjoDbLkuE2NYiF8vBQDNYrzlrSA==} + peerDependencies: + vue: ^3.0.1 + dependencies: + js-beautify: 1.14.6 + vue: 3.2.47 + optionalDependencies: + '@vue/compiler-dom': 3.2.47 + '@vue/server-renderer': 3.2.47_vue@3.2.47 + dev: true + + /@vueuse/core/9.13.0_vue@3.2.47: + resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} + dependencies: + '@types/web-bluetooth': 0.0.16 + '@vueuse/metadata': 9.13.0 + '@vueuse/shared': 9.13.0_vue@3.2.47 + vue-demi: 0.13.11_vue@3.2.47 + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + + /@vueuse/metadata/9.13.0: + resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} + dev: false + + /@vueuse/shared/9.13.0_vue@3.2.47: + resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} + dependencies: + vue-demi: 0.13.11_vue@3.2.47 + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + + /@windicss/config/1.8.10: + resolution: {integrity: sha512-O9SsC110b1Ik3YYa4Ck/0TWuCo7YFfA9KDrwD5sAeqscT5COIGK1HszdCT3oh0MJFej2wNrvpfyW9h6yQaW6PA==} + dependencies: + debug: 4.3.4 + jiti: 1.17.1 + windicss: 3.5.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@windicss/plugin-utils/1.8.10: + resolution: {integrity: sha512-Phqk5OW1w+Mv+ry6t7BzAeDq3aMhbI94gR49j9vQCufFfDGCHndhhjtMK0sBv+NPJUsIAIh6qayb1iwBCXUGrw==} + dependencies: + '@antfu/utils': 0.7.2 + '@windicss/config': 1.8.10 + debug: 4.3.4 + fast-glob: 3.2.12 + magic-string: 0.27.0 + micromatch: 4.0.5 + windicss: 3.5.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@zxcvbn-ts/core/2.2.1: + resolution: {integrity: sha512-Cg1JyRpCDIF+Dh3nauqygmmCYxogNVZDxSn+9PgkPD1HZ2QiJe4elruVJrGmYRS7muGmZ1hNJq8ySQdPv6GHaw==} + dependencies: + fastest-levenshtein: 1.0.16 + dev: false + + /JSONStream/1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + dev: true + + /abbrev/1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: true + + /acorn-jsx/5.3.2_acorn@8.8.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.8.2 + dev: true + + /acorn-walk/8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn/8.8.2: + resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /add-stream/1.0.0: + resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} + dev: true + + /adler-32/1.3.1: + resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} + engines: {node: '>=0.8'} + dev: false + + /aggregate-error/3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + dev: true + + /ajv/6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv/8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-escapes/4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-escapes/6.0.0: + resolution: {integrity: sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==} + engines: {node: '>=14.16'} + dependencies: + type-fest: 3.6.0 + dev: true + + /ansi-regex/2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + dev: true + + /ansi-regex/5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + /ansi-regex/6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles/2.2.1: + resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} + engines: {node: '>=0.10.0'} + dev: true + + /ansi-styles/3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles/4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + + /ansi-styles/6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true + + /ant-design-vue/3.2.15_vue@3.2.47: + resolution: {integrity: sha512-sJfE7LWimSdAPe4dzNyQBrmVMnOTNQTkG9oOyr+7W8qIYrX8sYWyC68Nn1uum4KBJUSZUa/BU6dohvTG0urBhA==} + engines: {node: '>=12.22.0'} + peerDependencies: + vue: '>=3.2.0' + dependencies: + '@ant-design/colors': 6.0.0 + '@ant-design/icons-vue': 6.1.0_vue@3.2.47 + '@babel/runtime': 7.21.0 + '@ctrl/tinycolor': 3.6.0 + '@simonwep/pickr': 1.8.2 + array-tree-filter: 2.1.0 + async-validator: 4.2.5 + dayjs: 1.11.7 + dom-align: 1.12.4 + dom-scroll-into-view: 2.0.1 + lodash: 4.17.21 + lodash-es: 4.17.21 + resize-observer-polyfill: 1.5.1 + scroll-into-view-if-needed: 2.2.31 + shallow-equal: 1.2.1 + vue: 3.2.47 + vue-types: 3.0.2_vue@3.2.47 + warning: 4.0.3 + dev: false + + /anymatch/3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /arg/4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /argparse/2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /arr-diff/4.0.0: + resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} + engines: {node: '>=0.10.0'} + dev: true + + /arr-flatten/1.1.0: + resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} + engines: {node: '>=0.10.0'} + dev: true + + /arr-union/3.1.0: + resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} + engines: {node: '>=0.10.0'} + dev: true + + /array-ify/1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + dev: true + + /array-tree-filter/2.1.0: + resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==} + dev: false + + /array-union/2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array-unique/0.3.2: + resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} + engines: {node: '>=0.10.0'} + dev: true + + /arrify/1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + dev: true + + /assign-symbols/1.0.0: + resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} + engines: {node: '>=0.10.0'} + dev: true + + /astral-regex/2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + dev: true + + /async-validator/4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + dev: false + + /async/3.2.4: + resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} + dev: true + + /asynckit/0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + /at-least-node/1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + dev: true + + /atob/2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true + dev: true + + /autoprefixer/10.4.14_postcss@8.4.21: + resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.5 + caniuse-lite: 1.0.30001464 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /available-typed-arrays/1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /axios/0.26.1_debug@4.3.4: + resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} + dependencies: + follow-redirects: 1.15.2_debug@4.3.4 + transitivePeerDependencies: + - debug + dev: true + + /axios/1.3.4: + resolution: {integrity: sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==} + dependencies: + follow-redirects: 1.15.2 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + + /axios/1.3.4_debug@4.3.4: + resolution: {integrity: sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==} + dependencies: + follow-redirects: 1.15.2_debug@4.3.4 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: true + + /babel-plugin-polyfill-corejs2/0.3.3_@babel+core@7.21.0: + resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.21.0 + '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.21.0 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-corejs3/0.6.0_@babel+core@7.21.0: + resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.21.0 + core-js-compat: 3.28.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-regenerator/0.4.1_@babel+core@7.21.0: + resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.0 + '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.21.0 + transitivePeerDependencies: + - supports-color + dev: true + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /balanced-match/2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + dev: true + + /base/0.11.2: + resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} + engines: {node: '>=0.10.0'} + dependencies: + cache-base: 1.0.1 + class-utils: 0.3.6 + component-emitter: 1.3.0 + define-property: 1.0.0 + isobject: 3.0.1 + mixin-deep: 1.3.2 + pascalcase: 0.1.1 + dev: true + + /base64-js/1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: true + + /before-after-hook/2.2.3: + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + dev: true + + /big.js/5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + dev: true + + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /bl/5.1.0: + resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + dependencies: + buffer: 6.0.3 + inherits: 2.0.4 + readable-stream: 3.6.1 + dev: true + + /bluebird/3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + dev: true + + /boolbase/1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: true + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion/2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces/2.3.2: + resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} + engines: {node: '>=0.10.0'} + dependencies: + arr-flatten: 1.1.0 + array-unique: 0.3.2 + extend-shallow: 2.0.1 + fill-range: 4.0.0 + isobject: 3.0.1 + repeat-element: 1.1.4 + snapdragon: 0.8.2 + snapdragon-node: 2.1.1 + split-string: 3.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist/4.21.5: + resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001464 + electron-to-chromium: 1.4.310 + node-releases: 2.0.10 + update-browserslist-db: 1.0.10_browserslist@4.21.5 + dev: true + + /buffer-from/1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /buffer/6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /builtin-modules/3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + + /cache-base/1.0.1: + resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} + engines: {node: '>=0.10.0'} + dependencies: + collection-visit: 1.0.0 + component-emitter: 1.3.0 + get-value: 2.0.6 + has-value: 1.0.0 + isobject: 3.0.1 + set-value: 2.0.1 + to-object-path: 0.3.0 + union-value: 1.0.1 + unset-value: 1.0.0 + dev: true + + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.0 + + /callsites/3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camel-case/4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + dependencies: + pascal-case: 3.1.2 + tslib: 2.5.0 + dev: true + + /camelcase-keys/6.2.2: + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + map-obj: 4.3.0 + quick-lru: 4.0.1 + dev: true + + /camelcase/5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + /camelcase/6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /caniuse-lite/1.0.30001464: + resolution: {integrity: sha512-oww27MtUmusatpRpCGSOneQk2/l5czXANDSFvsc7VuOQ86s3ANhZetpwXNf1zY/zdfP63Xvjz325DAdAoES13g==} + dev: true + + /capital-case/1.0.4: + resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + upper-case-first: 2.0.2 + dev: true + + /cfb/1.2.2: + resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} + engines: {node: '>=0.8'} + dependencies: + adler-32: 1.3.1 + crc-32: 1.2.2 + dev: false + + /chalk/1.1.3: + resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} + engines: {node: '>=0.10.0'} + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + dev: true + + /chalk/2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk/4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chalk/5.2.0: + resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + + /change-case/4.1.2: + resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==} + dependencies: + camel-case: 4.1.2 + capital-case: 1.0.4 + constant-case: 3.0.4 + dot-case: 3.0.4 + header-case: 2.0.4 + no-case: 3.0.4 + param-case: 3.0.4 + pascal-case: 3.1.2 + path-case: 3.0.4 + sentence-case: 3.0.4 + snake-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /chardet/0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true + + /chokidar/3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /class-utils/0.3.6: + resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} + engines: {node: '>=0.10.0'} + dependencies: + arr-union: 3.1.0 + define-property: 0.2.5 + isobject: 3.0.1 + static-extend: 0.1.2 + dev: true + + /clean-css/5.3.2: + resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==} + engines: {node: '>= 10.0'} + dependencies: + source-map: 0.6.1 + dev: true + + /clean-stack/2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + dev: true + + /cli-cursor/3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-cursor/4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + restore-cursor: 4.0.0 + dev: true + + /cli-spinners/2.7.0: + resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==} + engines: {node: '>=6'} + dev: true + + /cli-truncate/2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + dev: true + + /cli-truncate/3.1.0: + resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + slice-ansi: 5.0.0 + string-width: 5.1.2 + dev: true + + /cli-width/4.0.0: + resolution: {integrity: sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==} + engines: {node: '>= 12'} + dev: true + + /cliui/6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + dev: false + + /cliui/7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /cliui/8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /clone/1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + dev: true + + /clone/2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + dev: true + + /codemirror/5.65.12: + resolution: {integrity: sha512-z2jlHBocElRnPYysN2HAuhXbO3DNB0bcSKmNz3hcWR2Js2Dkhc1bEOxG93Z3DeUrnm+qx56XOY5wQmbP5KY0sw==} + dev: false + + /codepage/1.15.0: + resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} + engines: {node: '>=0.8'} + dev: false + + /collection-visit/1.0.0: + resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} + engines: {node: '>=0.10.0'} + dependencies: + map-visit: 1.0.0 + object-visit: 1.0.1 + dev: true + + /color-convert/1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert/2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + + /color-name/1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name/1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + /colord/2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + dev: true + + /colorette/2.0.19: + resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + dev: true + + /combined-stream/1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + + /commander/10.0.0: + resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==} + engines: {node: '>=14'} + + /commander/2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + + /commander/7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + dev: true + + /commander/8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + dev: true + + /commander/9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + dev: false + + /common-tags/1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + dev: true + + /compare-func/2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + dev: true + + /component-emitter/1.3.0: + resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} + dev: true + + /compute-scroll-into-view/1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + dev: false + + /concat-map/0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /config-chain/1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + + /connect-history-api-fallback/1.6.0: + resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==} + engines: {node: '>=0.8'} + dev: true + + /connect/3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /consola/2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + dev: true + + /console/0.7.2: + resolution: {integrity: sha512-+JSDwGunA4MTEgAV/4VBKwUHonP8CzJ/6GIuwPi6acKFqFfHUdSGCm89ZxZ5FfGWdZfkdgAroy5bJ5FSeN/t4g==} + dev: true + + /constant-case/3.0.4: + resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + upper-case: 2.0.2 + dev: true + + /conventional-changelog-angular/5.0.13: + resolution: {integrity: sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==} + engines: {node: '>=10'} + dependencies: + compare-func: 2.0.0 + q: 1.5.1 + dev: true + + /conventional-changelog-atom/2.0.8: + resolution: {integrity: sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-cli/2.2.2: + resolution: {integrity: sha512-8grMV5Jo8S0kP3yoMeJxV2P5R6VJOqK72IiSV9t/4H5r/HiRqEBQ83bYGuz4Yzfdj4bjaAEhZN/FFbsFXr5bOA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + add-stream: 1.0.0 + conventional-changelog: 3.1.25 + lodash: 4.17.21 + meow: 8.1.2 + tempfile: 3.0.0 + dev: true + + /conventional-changelog-codemirror/2.0.8: + resolution: {integrity: sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-conventionalcommits/4.6.3: + resolution: {integrity: sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==} + engines: {node: '>=10'} + dependencies: + compare-func: 2.0.0 + lodash: 4.17.21 + q: 1.5.1 + dev: true + + /conventional-changelog-conventionalcommits/5.0.0: + resolution: {integrity: sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==} + engines: {node: '>=10'} + dependencies: + compare-func: 2.0.0 + lodash: 4.17.21 + q: 1.5.1 + dev: true + + /conventional-changelog-core/4.2.4: + resolution: {integrity: sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==} + engines: {node: '>=10'} + dependencies: + add-stream: 1.0.0 + conventional-changelog-writer: 5.0.1 + conventional-commits-parser: 3.2.4 + dateformat: 3.0.3 + get-pkg-repo: 4.2.1 + git-raw-commits: 2.0.11 + git-remote-origin-url: 2.0.0 + git-semver-tags: 4.1.1 + lodash: 4.17.21 + normalize-package-data: 3.0.3 + q: 1.5.1 + read-pkg: 3.0.0 + read-pkg-up: 3.0.0 + through2: 4.0.2 + dev: true + + /conventional-changelog-ember/2.0.9: + resolution: {integrity: sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-eslint/3.0.9: + resolution: {integrity: sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-express/2.0.6: + resolution: {integrity: sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-jquery/3.0.11: + resolution: {integrity: sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==} + engines: {node: '>=10'} + dependencies: + q: 1.5.1 + dev: true + + /conventional-changelog-jshint/2.0.9: + resolution: {integrity: sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==} + engines: {node: '>=10'} + dependencies: + compare-func: 2.0.0 + q: 1.5.1 + dev: true + + /conventional-changelog-preset-loader/2.3.4: + resolution: {integrity: sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==} + engines: {node: '>=10'} + dev: true + + /conventional-changelog-writer/5.0.1: + resolution: {integrity: sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + conventional-commits-filter: 2.0.7 + dateformat: 3.0.3 + handlebars: 4.7.7 + json-stringify-safe: 5.0.1 + lodash: 4.17.21 + meow: 8.1.2 + semver: 6.3.0 + split: 1.0.1 + through2: 4.0.2 + dev: true + + /conventional-changelog/3.1.25: + resolution: {integrity: sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==} + engines: {node: '>=10'} + dependencies: + conventional-changelog-angular: 5.0.13 + conventional-changelog-atom: 2.0.8 + conventional-changelog-codemirror: 2.0.8 + conventional-changelog-conventionalcommits: 4.6.3 + conventional-changelog-core: 4.2.4 + conventional-changelog-ember: 2.0.9 + conventional-changelog-eslint: 3.0.9 + conventional-changelog-express: 2.0.6 + conventional-changelog-jquery: 3.0.11 + conventional-changelog-jshint: 2.0.9 + conventional-changelog-preset-loader: 2.3.4 + dev: true + + /conventional-commits-filter/2.0.7: + resolution: {integrity: sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==} + engines: {node: '>=10'} + dependencies: + lodash.ismatch: 4.4.0 + modify-values: 1.0.1 + dev: true + + /conventional-commits-parser/3.2.4: + resolution: {integrity: sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==} + engines: {node: '>=10'} + hasBin: true + dependencies: + JSONStream: 1.3.5 + is-text-path: 1.0.1 + lodash: 4.17.21 + meow: 8.1.2 + split2: 3.2.2 + through2: 4.0.2 + dev: true + + /convert-source-map/1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: true + + /copy-anything/2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + dependencies: + is-what: 3.14.1 + dev: true + + /copy-descriptor/0.1.1: + resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} + engines: {node: '>=0.10.0'} + dev: true + + /core-js-compat/3.28.0: + resolution: {integrity: sha512-myzPgE7QodMg4nnd3K1TDoES/nADRStM8Gpz0D6nhkwbmwEnE0ZGJgoWsvQ722FR8D7xS0n0LV556RcEicjTyg==} + dependencies: + browserslist: 4.21.5 + dev: true + + /core-js/3.29.1: + resolution: {integrity: sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==} + requiresBuild: true + + /core-util-is/1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: true + + /cors/2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + dev: true + + /cosmiconfig-typescript-loader/4.3.0_o5d5d5qrhzipaz5nmkhyrkswza: + resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=7' + ts-node: '>=10' + typescript: '>=3' + dependencies: + '@types/node': 18.15.3 + cosmiconfig: 8.0.0 + ts-node: 10.9.1_cbfmry4sbbh4vatmdrsmatfg5a + typescript: 4.9.5 + dev: true + + /cosmiconfig/7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.0 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: true + + /cosmiconfig/8.0.0: + resolution: {integrity: sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==} + engines: {node: '>=14'} + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + dev: true + + /crc-32/1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + dev: false + + /create-require/1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /cropperjs/1.5.13: + resolution: {integrity: sha512-by7jKAo73y5/Do0K6sxdTKHgndY0NMjG2bEdgeJxycbcmHuCiMXqw8sxy5C5Y5WTOTcDGmbT7Sr5CgKOXR06OA==} + dev: false + + /cross-env/7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + + /cross-fetch/3.1.5: + resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} + dependencies: + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + dev: true + + /cross-spawn/6.0.5: + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.1 + shebang-command: 1.2.0 + which: 1.3.1 + dev: true + + /cross-spawn/7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /crypto-js/4.1.1: + resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==} + dev: false + + /crypto-random-string/2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + dev: true + + /css-functions-list/3.1.0: + resolution: {integrity: sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==} + engines: {node: '>=12.22'} + dev: true + + /css-select/4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + dev: true + + /css-tree/1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + dev: true + + /css-what/6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: true + + /cssesc/3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /csso/4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + dependencies: + css-tree: 1.1.3 + dev: true + + /csstype/2.6.21: + resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} + + /cz-git/1.6.0: + resolution: {integrity: sha512-eOGpDlYtY9qcJ+PC9Rg+Dk0aDrk6azkOOU5z52MJzKm11mtIMQIzqCtvsM7fHYschiQ0NCcrg5pNr9JaxtVA1A==} + dev: true + + /czg/1.6.0: + resolution: {integrity: sha512-bFAXzGUrwanQ/VDL+s6zhoXxZbZNHCoAF0R2VAaHoFNpyAFeOrQ66TLP7FydaRsnXdBzRazfN+BUVdKUkyoHGw==} + hasBin: true + dev: true + + /dargs/7.0.0: + resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} + engines: {node: '>=8'} + dev: true + + /dateformat/3.0.3: + resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} + dev: true + + /dayjs/1.11.7: + resolution: {integrity: sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==} + dev: false + + /de-indent/1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + dev: true + + /debug/2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: true + + /debug/3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + optional: true + + /debug/4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /decamelize-keys/1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + dev: true + + /decamelize/1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + /decode-uri-component/0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + dev: true + + /deep-is/0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge/4.3.0: + resolution: {integrity: sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==} + engines: {node: '>=0.10.0'} + dev: true + + /defaults/1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + dependencies: + clone: 1.0.4 + dev: true + + /define-lazy-prop/2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + dev: true + + /define-properties/1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /define-property/0.2.5: + resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} + engines: {node: '>=0.10.0'} + dependencies: + is-descriptor: 0.1.6 + dev: true + + /define-property/1.0.0: + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} + engines: {node: '>=0.10.0'} + dependencies: + is-descriptor: 1.0.2 + dev: true + + /define-property/2.0.2: + resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==} + engines: {node: '>=0.10.0'} + dependencies: + is-descriptor: 1.0.2 + isobject: 3.0.1 + dev: true + + /delayed-stream/1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + /deprecation/2.3.1: + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + dev: true + + /diff-match-patch/1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + dev: false + + /diff/4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /dijkstrajs/1.0.2: + resolution: {integrity: sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==} + dev: false + + /dir-glob/3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine/3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-align/1.12.4: + resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==} + dev: false + + /dom-scroll-into-view/2.0.1: + resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==} + dev: false + + /dom-serializer/0.2.2: + resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} + dependencies: + domelementtype: 2.3.0 + entities: 2.2.0 + dev: true + + /dom-serializer/1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + dev: true + + /dom-serializer/2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.4.0 + dev: true + + /domelementtype/1.3.1: + resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} + dev: true + + /domelementtype/2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: true + + /domhandler/2.4.2: + resolution: {integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==} + dependencies: + domelementtype: 1.3.1 + dev: true + + /domhandler/4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: true + + /domhandler/5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: true + + /domutils/1.7.0: + resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} + dependencies: + dom-serializer: 0.2.2 + domelementtype: 1.3.1 + dev: true + + /domutils/2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + dev: true + + /domutils/3.0.1: + resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==} + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dev: true + + /dot-case/3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /dot-prop/5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dependencies: + is-obj: 2.0.0 + dev: true + + /dotenv-expand/8.0.3: + resolution: {integrity: sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==} + engines: {node: '>=12'} + dev: true + + /dotenv/16.0.3: + resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} + engines: {node: '>=12'} + dev: true + + /eastasianwidth/0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + + /echarts/5.4.1: + resolution: {integrity: sha512-9ltS3M2JB0w2EhcYjCdmtrJ+6haZcW6acBolMGIuf01Hql1yrIV01L1aRj7jsaaIULJslEP9Z3vKlEmnJaWJVQ==} + dependencies: + tslib: 2.3.0 + zrender: 5.4.1 + dev: false + + /editorconfig/0.15.3: + resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==} + hasBin: true + dependencies: + commander: 2.20.3 + lru-cache: 4.1.5 + semver: 5.7.1 + sigmund: 1.0.1 + dev: true + + /ee-first/1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: true + + /ejs/3.1.8: + resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + jake: 10.8.5 + dev: true + + /electron-to-chromium/1.4.310: + resolution: {integrity: sha512-/xlATgfwkm5uDDwLw5nt/MNEf7c1oazLURMZLy39vOioGYyYzLWIDT8fZMJak6qTiAJ7udFTy7JG7ziyjNutiA==} + dev: true + + /emoji-regex/8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + /emoji-regex/9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + + /emojis-list/3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + dev: true + + /encode-utf8/1.0.3: + resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} + dev: false + + /encodeurl/1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: true + + /entities/1.1.2: + resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} + dev: true + + /entities/2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + dev: true + + /entities/4.4.0: + resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} + engines: {node: '>=0.12'} + dev: true + + /errno/0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + requiresBuild: true + dependencies: + prr: 1.0.1 + dev: true + optional: true + + /error-ex/1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract/1.21.1: + resolution: {integrity: sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 + es-to-primitive: 1.2.1 + function-bind: 1.1.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.2.0 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + is-array-buffer: 3.0.1 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.10 + is-weakref: 1.0.2 + object-inspect: 1.12.3 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.4.3 + safe-regex-test: 1.0.0 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.9 + dev: true + + /es-module-lexer/0.9.3: + resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==} + dev: true + + /es-set-tostringtag/2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.0 + has: 1.0.3 + has-tostringtag: 1.0.0 + dev: true + + /es-to-primitive/1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild-android-64/0.15.18: + resolution: {integrity: sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-android-arm64/0.15.18: + resolution: {integrity: sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-64/0.15.18: + resolution: {integrity: sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-arm64/0.15.18: + resolution: {integrity: sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-64/0.15.18: + resolution: {integrity: sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-arm64/0.15.18: + resolution: {integrity: sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-32/0.15.18: + resolution: {integrity: sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-64/0.15.18: + resolution: {integrity: sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm/0.15.18: + resolution: {integrity: sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm64/0.15.18: + resolution: {integrity: sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-mips64le/0.15.18: + resolution: {integrity: sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-ppc64le/0.15.18: + resolution: {integrity: sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-riscv64/0.15.18: + resolution: {integrity: sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-s390x/0.15.18: + resolution: {integrity: sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-netbsd-64/0.15.18: + resolution: {integrity: sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-openbsd-64/0.15.18: + resolution: {integrity: sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-plugin-alias/0.2.1: + resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==} + dev: true + + /esbuild-sunos-64/0.15.18: + resolution: {integrity: sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-32/0.15.18: + resolution: {integrity: sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-64/0.15.18: + resolution: {integrity: sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-arm64/0.15.18: + resolution: {integrity: sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild/0.11.3: + resolution: {integrity: sha512-BzVRHcCtFepjS9WcqRjqoIxLqgpK21a8J4Zi4msSGxDxiXVO1IbcqT1KjhdDDnJxKfe7bvzZrvMEX+bVO0Elcw==} + hasBin: true + requiresBuild: true + dev: true + + /esbuild/0.15.18: + resolution: {integrity: sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.15.18 + '@esbuild/linux-loong64': 0.15.18 + esbuild-android-64: 0.15.18 + esbuild-android-arm64: 0.15.18 + esbuild-darwin-64: 0.15.18 + esbuild-darwin-arm64: 0.15.18 + esbuild-freebsd-64: 0.15.18 + esbuild-freebsd-arm64: 0.15.18 + esbuild-linux-32: 0.15.18 + esbuild-linux-64: 0.15.18 + esbuild-linux-arm: 0.15.18 + esbuild-linux-arm64: 0.15.18 + esbuild-linux-mips64le: 0.15.18 + esbuild-linux-ppc64le: 0.15.18 + esbuild-linux-riscv64: 0.15.18 + esbuild-linux-s390x: 0.15.18 + esbuild-netbsd-64: 0.15.18 + esbuild-openbsd-64: 0.15.18 + esbuild-sunos-64: 0.15.18 + esbuild-windows-32: 0.15.18 + esbuild-windows-64: 0.15.18 + esbuild-windows-arm64: 0.15.18 + dev: true + + /esbuild/0.17.10: + resolution: {integrity: sha512-n7V3v29IuZy5qgxx25TKJrEm0FHghAlS6QweUcyIgh/U0zYmQcvogWROitrTyZId1mHSkuhhuyEXtI9OXioq7A==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.17.10 + '@esbuild/android-arm64': 0.17.10 + '@esbuild/android-x64': 0.17.10 + '@esbuild/darwin-arm64': 0.17.10 + '@esbuild/darwin-x64': 0.17.10 + '@esbuild/freebsd-arm64': 0.17.10 + '@esbuild/freebsd-x64': 0.17.10 + '@esbuild/linux-arm': 0.17.10 + '@esbuild/linux-arm64': 0.17.10 + '@esbuild/linux-ia32': 0.17.10 + '@esbuild/linux-loong64': 0.17.10 + '@esbuild/linux-mips64el': 0.17.10 + '@esbuild/linux-ppc64': 0.17.10 + '@esbuild/linux-riscv64': 0.17.10 + '@esbuild/linux-s390x': 0.17.10 + '@esbuild/linux-x64': 0.17.10 + '@esbuild/netbsd-x64': 0.17.10 + '@esbuild/openbsd-x64': 0.17.10 + '@esbuild/sunos-x64': 0.17.10 + '@esbuild/win32-arm64': 0.17.10 + '@esbuild/win32-ia32': 0.17.10 + '@esbuild/win32-x64': 0.17.10 + dev: true + + /escalade/3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-html/1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: true + + /escape-string-regexp/1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp/4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /escape-string-regexp/5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + dev: true + + /eslint-config-prettier/8.7.0_eslint@8.36.0: + resolution: {integrity: sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.36.0 + dev: true + + /eslint-plugin-prettier/4.2.1_eqzx3hpkgx5nnvxls3azrcc7dm: + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.36.0 + eslint-config-prettier: 8.7.0_eslint@8.36.0 + prettier: 2.8.4 + prettier-linter-helpers: 1.0.0 + dev: true + + /eslint-plugin-vue/9.9.0_eslint@8.36.0: + resolution: {integrity: sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.36.0 + eslint-utils: 3.0.0_eslint@8.36.0 + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.0.11 + semver: 7.3.8 + vue-eslint-parser: 9.1.0_eslint@8.36.0 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-scope/5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope/7.1.1: + resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils/3.0.0_eslint@8.36.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.36.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys/2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys/3.3.0: + resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint/8.36.0: + resolution: {integrity: sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.2.0_eslint@8.36.0 + '@eslint-community/regexpp': 4.4.0 + '@eslint/eslintrc': 2.0.1 + '@eslint/js': 8.36.0 + '@humanwhocodes/config-array': 0.11.8 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-visitor-keys: 3.3.0 + espree: 9.5.0 + esquery: 1.4.2 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.20.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-sdsl: 4.3.0 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /esno/0.16.3: + resolution: {integrity: sha512-6slSBEV1lMKcX13DBifvnDFpNno5WXhw4j/ff7RI0y51BZiDqEe5dNhhjhIQ3iCOQuzsm2MbVzmwqbN78BBhPg==} + hasBin: true + dependencies: + tsx: 3.12.3 + dev: true + + /espree/9.4.1: + resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.8.2 + acorn-jsx: 5.3.2_acorn@8.8.2 + eslint-visitor-keys: 3.3.0 + dev: true + + /espree/9.5.0: + resolution: {integrity: sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.8.2 + acorn-jsx: 5.3.2_acorn@8.8.2 + eslint-visitor-keys: 3.3.0 + dev: true + + /esquery/1.4.2: + resolution: {integrity: sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse/4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse/4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse/5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /estree-walker/1.0.1: + resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + dev: true + + /estree-walker/2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + /esutils/2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /etag/1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: true + + /execa/5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa/7.0.0: + resolution: {integrity: sha512-tQbH0pH/8LHTnwTrsKWideqi6rFB/QNUawEwrn+WHyz7PX1Tuz2u7wfTvbaNBdP5JD5LVWxNo8/A8CHNZ3bV6g==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + + /expand-brackets/2.1.4: + resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} + engines: {node: '>=0.10.0'} + dependencies: + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + posix-character-classes: 0.1.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /extend-shallow/2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + dependencies: + is-extendable: 0.1.1 + dev: true + + /extend-shallow/3.0.2: + resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} + engines: {node: '>=0.10.0'} + dependencies: + assign-symbols: 1.0.0 + is-extendable: 1.0.1 + dev: true + + /external-editor/3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + dev: true + + /extglob/2.0.4: + resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} + engines: {node: '>=0.10.0'} + dependencies: + array-unique: 0.3.2 + define-property: 1.0.0 + expand-brackets: 2.1.4 + extend-shallow: 2.0.1 + fragment-cache: 0.2.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /fast-deep-equal/3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-diff/1.2.0: + resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} + dev: true + + /fast-glob/3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify/2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein/2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastest-levenshtein/1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + /fastq/1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /figures/5.0.0: + resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} + engines: {node: '>=14'} + dependencies: + escape-string-regexp: 5.0.0 + is-unicode-supported: 1.3.0 + dev: true + + /file-entry-cache/6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /filelist/1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + dependencies: + minimatch: 5.1.6 + dev: true + + /fill-range/4.0.0: + resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} + engines: {node: '>=0.10.0'} + dependencies: + extend-shallow: 2.0.1 + is-number: 3.0.0 + repeat-string: 1.6.1 + to-regex-range: 2.1.1 + dev: true + + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /finalhandler/1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /find-up/2.1.0: + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} + engines: {node: '>=4'} + dependencies: + locate-path: 2.0.0 + dev: true + + /find-up/4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + /find-up/5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache/3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.7 + rimraf: 3.0.2 + dev: true + + /flatted/3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /follow-redirects/1.15.2: + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + + /follow-redirects/1.15.2_debug@4.3.4: + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dependencies: + debug: 4.3.4 + dev: true + + /for-each/0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /for-in/1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + dev: true + + /form-data/4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + /frac/1.1.2: + resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} + engines: {node: '>=0.8'} + dev: false + + /fraction.js/4.2.0: + resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + dev: true + + /fragment-cache/0.2.1: + resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} + engines: {node: '>=0.10.0'} + dependencies: + map-cache: 0.2.2 + dev: true + + /fs-extra/10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + + /fs-extra/11.1.0: + resolution: {integrity: sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + + /fs-extra/9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + + /fs.realpath/1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + + /function.prototype.name/1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names/1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /gensync/1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-caller-file/2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + /get-intrinsic/1.2.0: + resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + + /get-own-enumerable-property-symbols/3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + dev: true + + /get-pkg-repo/4.2.1: + resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==} + engines: {node: '>=6.9.0'} + hasBin: true + dependencies: + '@hutson/parse-repository-url': 3.0.2 + hosted-git-info: 4.1.0 + through2: 2.0.5 + yargs: 16.2.0 + dev: true + + /get-stream/6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /get-symbol-description/1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + dev: true + + /get-tsconfig/4.4.0: + resolution: {integrity: sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==} + dev: true + + /get-value/2.0.6: + resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} + engines: {node: '>=0.10.0'} + dev: true + + /git-raw-commits/2.0.11: + resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + dargs: 7.0.0 + lodash: 4.17.21 + meow: 8.1.2 + split2: 3.2.2 + through2: 4.0.2 + dev: true + + /git-remote-origin-url/2.0.0: + resolution: {integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==} + engines: {node: '>=4'} + dependencies: + gitconfiglocal: 1.0.0 + pify: 2.3.0 + dev: true + + /git-semver-tags/4.1.1: + resolution: {integrity: sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + meow: 8.1.2 + semver: 6.3.0 + dev: true + + /gitconfiglocal/1.0.0: + resolution: {integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==} + dependencies: + ini: 1.3.8 + dev: true + + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent/6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob/7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob/8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: true + + /glob/9.2.1: + resolution: {integrity: sha512-Pxxgq3W0HyA3XUvSXcFhRSs+43Jsx0ddxcFrbjxNGkL2Ak5BAUBxLqI5G6ADDeCHLfzzXFhe0b1yYcctGmytMA==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + fs.realpath: 1.0.0 + minimatch: 7.4.2 + minipass: 4.2.4 + path-scurry: 1.6.1 + dev: true + + /global-dirs/0.1.1: + resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==} + engines: {node: '>=4'} + dependencies: + ini: 1.3.8 + dev: true + + /global-modules/2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + dependencies: + global-prefix: 3.0.0 + dev: true + + /global-prefix/3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + dev: true + + /globals/11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals/13.20.0: + resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis/1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + dev: true + + /globby/11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.12 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /globjoin/0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + dev: true + + /gopd/1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.0 + dev: true + + /graceful-fs/4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + dev: true + + /grapheme-splitter/1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /handlebars/4.7.7: + resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} + engines: {node: '>=0.4.7'} + hasBin: true + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.17.4 + dev: true + + /hard-rejection/2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + dev: true + + /has-ansi/2.0.0: + resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} + engines: {node: '>=0.10.0'} + dependencies: + ansi-regex: 2.1.1 + dev: true + + /has-bigints/1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag/1.0.0: + resolution: {integrity: sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==} + engines: {node: '>=0.10.0'} + dev: true + + /has-flag/3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag/4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors/1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.2.0 + dev: true + + /has-proto/1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols/1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + /has-tostringtag/1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has-value/0.3.1: + resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} + engines: {node: '>=0.10.0'} + dependencies: + get-value: 2.0.6 + has-values: 0.1.4 + isobject: 2.1.0 + dev: true + + /has-value/1.0.0: + resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} + engines: {node: '>=0.10.0'} + dependencies: + get-value: 2.0.6 + has-values: 1.0.0 + isobject: 3.0.1 + dev: true + + /has-values/0.1.4: + resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} + engines: {node: '>=0.10.0'} + dev: true + + /has-values/1.0.0: + resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} + engines: {node: '>=0.10.0'} + dependencies: + is-number: 3.0.0 + kind-of: 4.0.0 + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + + /he/1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + + /header-case/2.0.4: + resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} + dependencies: + capital-case: 1.0.4 + tslib: 2.5.0 + dev: true + + /hosted-git-info/2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + + /hosted-git-info/4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + dependencies: + lru-cache: 6.0.0 + dev: true + + /html-minifier-terser/6.1.0: + resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} + engines: {node: '>=12'} + hasBin: true + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.2 + commander: 8.3.0 + he: 1.2.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.16.6 + dev: true + + /html-tags/3.2.0: + resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==} + engines: {node: '>=8'} + dev: true + + /htmlparser2/3.10.1: + resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} + dependencies: + domelementtype: 1.3.1 + domhandler: 2.4.2 + domutils: 1.7.0 + entities: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.1 + dev: true + + /htmlparser2/8.0.1: + resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.0.1 + entities: 4.4.0 + dev: true + + /human-signals/2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /human-signals/4.3.0: + resolution: {integrity: sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ==} + engines: {node: '>=14.18.0'} + dev: true + + /husky/8.0.3: + resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /iconv-lite/0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /iconv-lite/0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + optional: true + + /idb/7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + dev: true + + /ids/1.0.0: + resolution: {integrity: sha512-Zvtq1xUto4LttpstyOlFum8lKx+i1OmRfg+6A9drFS9iSZsDPMHG4Sof/qwNR4kCU7jBeWFPrY2ocHxiz7cCRw==} + dev: false + + /ieee754/1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: true + + /ignore/5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /image-size/0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + dev: true + + /import-fresh/3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-lazy/4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + dev: true + + /imurmurhash/0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string/4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight/1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /ini/1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true + + /inquirer/9.1.4: + resolution: {integrity: sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 6.0.0 + chalk: 5.2.0 + cli-cursor: 4.0.0 + cli-width: 4.0.0 + external-editor: 3.1.0 + figures: 5.0.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 6.1.2 + run-async: 2.4.1 + rxjs: 7.8.0 + string-width: 5.1.2 + strip-ansi: 7.0.1 + through: 2.3.8 + wrap-ansi: 8.1.0 + dev: true + + /internal-slot/1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.0 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /intro.js/6.0.0: + resolution: {integrity: sha512-ZUiR6BoLSvPSlLG0boewnWVgji1fE1gBvP/pyw5pgCKXEDQz1mMeUxarggClPNs71UTq364LwSk9zxz17A9gaQ==} + dev: false + + /is-accessor-descriptor/0.1.6: + resolution: {integrity: sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==} + engines: {node: '>=0.10.0'} + dependencies: + kind-of: 3.2.2 + dev: true + + /is-accessor-descriptor/1.0.0: + resolution: {integrity: sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==} + engines: {node: '>=0.10.0'} + dependencies: + kind-of: 6.0.3 + dev: true + + /is-array-buffer/3.0.1: + resolution: {integrity: sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + is-typed-array: 1.1.10 + dev: true + + /is-arrayish/0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-bigint/1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-boolean-object/1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-buffer/1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + dev: true + + /is-builtin-module/3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + dependencies: + builtin-modules: 3.3.0 + dev: true + + /is-callable/1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module/2.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + dependencies: + has: 1.0.3 + dev: true + + /is-data-descriptor/0.1.4: + resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} + engines: {node: '>=0.10.0'} + dependencies: + kind-of: 3.2.2 + dev: true + + /is-data-descriptor/1.0.0: + resolution: {integrity: sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==} + engines: {node: '>=0.10.0'} + dependencies: + kind-of: 6.0.3 + dev: true + + /is-date-object/1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-descriptor/0.1.6: + resolution: {integrity: sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==} + engines: {node: '>=0.10.0'} + dependencies: + is-accessor-descriptor: 0.1.6 + is-data-descriptor: 0.1.4 + kind-of: 5.1.0 + dev: true + + /is-descriptor/1.0.2: + resolution: {integrity: sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==} + engines: {node: '>=0.10.0'} + dependencies: + is-accessor-descriptor: 1.0.0 + is-data-descriptor: 1.0.0 + kind-of: 6.0.3 + dev: true + + /is-docker/2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + + /is-extendable/0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + dev: true + + /is-extendable/1.0.1: + resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} + engines: {node: '>=0.10.0'} + dependencies: + is-plain-object: 2.0.4 + dev: true + + /is-extglob/2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point/3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + /is-fullwidth-code-point/4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + dev: true + + /is-glob/4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-interactive/2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + dev: true + + /is-module/1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + dev: true + + /is-negative-zero/2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object/1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number/3.0.0: + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} + engines: {node: '>=0.10.0'} + dependencies: + kind-of: 3.2.2 + dev: true + + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-obj/1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + dev: true + + /is-obj/2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + dev: true + + /is-path-inside/3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-plain-obj/1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + dev: true + + /is-plain-object/2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + dependencies: + isobject: 3.0.1 + dev: true + + /is-plain-object/3.0.1: + resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==} + engines: {node: '>=0.10.0'} + dev: false + + /is-plain-object/5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + /is-regex/1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-regexp/1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + dev: true + + /is-shared-array-buffer/1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-stream/2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-stream/3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-string/1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol/1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-text-path/1.0.1: + resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} + engines: {node: '>=0.10.0'} + dependencies: + text-extensions: 1.9.0 + dev: true + + /is-typed-array/1.1.10: + resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /is-unicode-supported/1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + dev: true + + /is-weakref/1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-what/3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + dev: true + + /is-windows/1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + dev: true + + /is-wsl/2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + + /isarray/1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: true + + /isexe/2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /isobject/2.1.0: + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} + engines: {node: '>=0.10.0'} + dependencies: + isarray: 1.0.0 + dev: true + + /isobject/3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + dev: true + + /jake/10.8.5: + resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + async: 3.2.4 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + dev: true + + /jest-worker/26.6.2: + resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 18.15.3 + merge-stream: 2.0.0 + supports-color: 7.2.0 + dev: true + + /jiti/1.17.1: + resolution: {integrity: sha512-NZIITw8uZQFuzQimqjUxIrIcEdxYDFIe/0xYfIlVXTkiBjjyBEvgasj5bb0/cHtPRD/NziPbT312sFrkI5ALpw==} + hasBin: true + dev: true + + /js-base64/2.6.4: + resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} + dev: true + + /js-beautify/1.14.6: + resolution: {integrity: sha512-GfofQY5zDp+cuHc+gsEXKPpNw2KbPddreEo35O6jT6i0RVK6LhsoYBhq5TvK4/n74wnA0QbK8gGd+jUZwTMKJw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + config-chain: 1.1.13 + editorconfig: 0.15.3 + glob: 8.1.0 + nopt: 6.0.0 + dev: true + + /js-sdsl/4.3.0: + resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} + dev: true + + /js-tokens/4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-tokens/8.0.1: + resolution: {integrity: sha512-3AGrZT6tuMm1ZWWn9mLXh7XMfi2YtiLNPALCVxBCiUVq0LD1OQMxV/AdS/s7rLJU5o9i/jBZw/N4vXXL5dm29A==} + dev: true + + /js-yaml/4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc/0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + dev: true + + /jsesc/2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-parse-better-errors/1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + dev: true + + /json-parse-even-better-errors/2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse/0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse/1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /json-schema/0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + dev: true + + /json-stable-stringify-without-jsonify/1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json-stringify-safe/5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + dev: true + + /json5/1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /json5/2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonfile/6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.0 + optionalDependencies: + graceful-fs: 4.2.10 + dev: true + + /jsonparse/1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + dev: true + + /jsonpointer/5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + dev: true + + /kind-of/3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + dependencies: + is-buffer: 1.1.6 + dev: true + + /kind-of/4.0.0: + resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} + engines: {node: '>=0.10.0'} + dependencies: + is-buffer: 1.1.6 + dev: true + + /kind-of/5.1.0: + resolution: {integrity: sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==} + engines: {node: '>=0.10.0'} + dev: true + + /kind-of/6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: true + + /known-css-properties/0.26.0: + resolution: {integrity: sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==} + dev: true + + /kolorist/1.7.0: + resolution: {integrity: sha512-ymToLHqL02udwVdbkowNpzjFd6UzozMtshPQKVi5k1EjKRqKqBrOnE9QbLEb0/pV76SAiIT13hdL8R6suc+f3g==} + dev: true + + /less/4.1.3: + resolution: {integrity: sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==} + engines: {node: '>=6'} + hasBin: true + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.5.0 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.10 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.2.0 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + + /leven/3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: true + + /levn/0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig/2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: true + + /lines-and-columns/1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /lint-staged/13.2.0: + resolution: {integrity: sha512-GbyK5iWinax5Dfw5obm2g2ccUiZXNGtAS4mCbJ0Lv4rq6iEtfBSjOYdcbOtAIFtM114t0vdpViDDetjVTSd8Vw==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + dependencies: + chalk: 5.2.0 + cli-truncate: 3.1.0 + commander: 10.0.0 + debug: 4.3.4 + execa: 7.0.0 + lilconfig: 2.1.0 + listr2: 5.0.7 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-inspect: 1.12.3 + pidtree: 0.6.0 + string-argv: 0.3.1 + yaml: 2.2.1 + transitivePeerDependencies: + - enquirer + - supports-color + dev: true + + /listr2/5.0.7: + resolution: {integrity: sha512-MD+qXHPmtivrHIDRwPYdfNkrzqDiuaKU/rfBcec3WMyMF3xylQj3jMq344OtvQxz7zaCFViRAeqlr2AFhPvXHw==} + engines: {node: ^14.13.1 || >=16.0.0} + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + dependencies: + cli-truncate: 2.1.0 + colorette: 2.0.19 + log-update: 4.0.0 + p-map: 4.0.0 + rfdc: 1.3.0 + rxjs: 7.8.0 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + + /load-json-file/4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + dependencies: + graceful-fs: 4.2.10 + parse-json: 4.0.0 + pify: 3.0.0 + strip-bom: 3.0.0 + dev: true + + /loader-utils/1.4.2: + resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==} + engines: {node: '>=4.0.0'} + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 1.0.2 + dev: true + + /locate-path/2.0.0: + resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} + engines: {node: '>=4'} + dependencies: + p-locate: 2.0.0 + path-exists: 3.0.0 + dev: true + + /locate-path/5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + + /locate-path/6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash-es/4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: false + + /lodash.camelcase/4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + dev: true + + /lodash.debounce/4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + dev: true + + /lodash.isfunction/3.0.9: + resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} + dev: true + + /lodash.ismatch/4.4.0: + resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} + dev: true + + /lodash.isplainobject/4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: true + + /lodash.kebabcase/4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + dev: true + + /lodash.merge/4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.mergewith/4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + dev: true + + /lodash.snakecase/4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + dev: true + + /lodash.sortby/4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + dev: true + + /lodash.startcase/4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + dev: true + + /lodash.truncate/4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + dev: true + + /lodash.uniq/4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + dev: true + + /lodash.upperfirst/4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + dev: true + + /lodash/4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + /log-symbols/5.1.0: + resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} + engines: {node: '>=12'} + dependencies: + chalk: 5.2.0 + is-unicode-supported: 1.3.0 + dev: true + + /log-update/4.0.0: + resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} + engines: {node: '>=10'} + dependencies: + ansi-escapes: 4.3.2 + cli-cursor: 3.1.0 + slice-ansi: 4.0.0 + wrap-ansi: 6.2.0 + dev: true + + /loose-envify/1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: false + + /lower-case/2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + dependencies: + tslib: 2.5.0 + dev: true + + /lru-cache/4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + dev: true + + /lru-cache/5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /lru-cache/7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: true + + /magic-string/0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + dependencies: + sourcemap-codec: 1.4.8 + + /magic-string/0.26.7: + resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==} + engines: {node: '>=12'} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /magic-string/0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /magic-string/0.30.0: + resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /make-dir/2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + pify: 4.0.1 + semver: 5.7.1 + dev: true + optional: true + + /make-error/1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /map-cache/0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + dev: true + + /map-obj/1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + dev: true + + /map-obj/4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + dev: true + + /map-visit/1.0.0: + resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} + engines: {node: '>=0.10.0'} + dependencies: + object-visit: 1.0.1 + dev: true + + /mathml-tag-names/2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + dev: true + + /mdn-data/2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + dev: true + + /memorystream/0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + dev: true + + /meow/8.1.2: + resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} + engines: {node: '>=10'} + dependencies: + '@types/minimist': 1.2.2 + camelcase-keys: 6.2.2 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.18.1 + yargs-parser: 20.2.9 + dev: true + + /meow/9.0.0: + resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} + engines: {node: '>=10'} + dependencies: + '@types/minimist': 1.2.2 + camelcase-keys: 6.2.2 + decamelize: 1.2.0 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.18.1 + yargs-parser: 20.2.9 + dev: true + + /merge-options/1.0.1: + resolution: {integrity: sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==} + engines: {node: '>=4'} + dependencies: + is-plain-obj: 1.1.0 + dev: true + + /merge-stream/2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch/3.1.0: + resolution: {integrity: sha512-3StSelAE+hnRvMs8IdVW7Uhk8CVed5tp+kLLGlBP6WiRAXS21GPGu/Nat4WNPXj2Eoc24B02SaeoyozPMfj0/g==} + engines: {node: '>=0.10.0'} + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + braces: 2.3.2 + define-property: 1.0.0 + extend-shallow: 2.0.1 + extglob: 2.0.4 + fragment-cache: 0.2.1 + kind-of: 5.1.0 + nanomatch: 1.2.13 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /micromatch/4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime-db/1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + /mime-types/2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + + /mime/1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + requiresBuild: true + dev: true + optional: true + + /mimic-fn/2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn/4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /min-indent/1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch/3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch/5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimatch/6.2.0: + resolution: {integrity: sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimatch/7.4.2: + resolution: {integrity: sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist-options/4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + dev: true + + /minimist/1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /minipass/4.2.4: + resolution: {integrity: sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==} + engines: {node: '>=8'} + dev: true + + /mixin-deep/1.3.2: + resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} + engines: {node: '>=0.10.0'} + dependencies: + for-in: 1.0.2 + is-extendable: 1.0.1 + dev: true + + /mockjs/1.1.0: + resolution: {integrity: sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==} + hasBin: true + dependencies: + commander: 10.0.0 + + /modify-values/1.0.1: + resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} + engines: {node: '>=0.10.0'} + dev: true + + /mousetrap/1.6.5: + resolution: {integrity: sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==} + dev: false + + /ms/2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: true + + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms/2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + optional: true + + /muggle-string/0.2.2: + resolution: {integrity: sha512-YVE1mIJ4VpUMqZObFndk9CJu6DBJR/GB13p3tXuNbwD4XExaI5EOuRl6BHeIDxIqXZVxSfAC+y6U1Z/IxCfKUg==} + dev: true + + /mute-stream/0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: true + + /nanoid/3.3.4: + resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + /nanomatch/1.2.13: + resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} + engines: {node: '>=0.10.0'} + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + fragment-cache: 0.2.1 + is-windows: 1.0.2 + kind-of: 6.0.3 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /nanopop/2.2.0: + resolution: {integrity: sha512-E9JaHcxh3ere8/BEZHAcnuD10RluTSPyTToBvoFWS9/7DcCx6gyKjbn7M7Bx7E1veCxCuY1iO6h4+gdAf1j73Q==} + dev: false + + /natural-compare-lite/1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare/1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /needle/3.2.0: + resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==} + engines: {node: '>= 4.4.x'} + hasBin: true + requiresBuild: true + dependencies: + debug: 3.2.7 + iconv-lite: 0.6.3 + sax: 1.2.4 + transitivePeerDependencies: + - supports-color + dev: true + optional: true + + /neo-async/2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + dev: true + + /nice-try/1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + dev: true + + /no-case/3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + dependencies: + lower-case: 2.0.2 + tslib: 2.5.0 + dev: true + + /node-fetch/2.6.7: + resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: true + + /node-fetch/2.6.9: + resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: true + + /node-html-parser/5.4.2: + resolution: {integrity: sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==} + dependencies: + css-select: 4.3.0 + he: 1.2.0 + dev: true + + /node-releases/2.0.10: + resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} + dev: true + + /nopt/6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: true + + /normalize-package-data/2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.1 + semver: 5.7.1 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-package-data/3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.11.0 + semver: 7.3.8 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-range/0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-all/4.1.5: + resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} + engines: {node: '>= 4'} + hasBin: true + dependencies: + ansi-styles: 3.2.1 + chalk: 2.4.2 + cross-spawn: 6.0.5 + memorystream: 0.3.1 + minimatch: 3.1.2 + pidtree: 0.3.1 + read-pkg: 3.0.0 + shell-quote: 1.8.0 + string.prototype.padend: 3.1.4 + dev: true + + /npm-run-path/4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /npm-run-path/5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /nprogress/0.2.0: + resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} + dev: false + + /nth-check/2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: true + + /object-assign/4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: true + + /object-copy/0.1.0: + resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} + engines: {node: '>=0.10.0'} + dependencies: + copy-descriptor: 0.1.1 + define-property: 0.2.5 + kind-of: 3.2.2 + dev: true + + /object-inspect/1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + + /object-keys/1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object-visit/1.0.1: + resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} + engines: {node: '>=0.10.0'} + dependencies: + isobject: 3.0.1 + dev: true + + /object.assign/4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.pick/1.3.0: + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} + engines: {node: '>=0.10.0'} + dependencies: + isobject: 3.0.1 + dev: true + + /on-finished/2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: true + + /once/1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime/5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /onetime/6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /open/8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: true + + /optionator/0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.3 + dev: true + + /ora/6.1.2: + resolution: {integrity: sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + bl: 5.1.0 + chalk: 5.2.0 + cli-cursor: 4.0.0 + cli-spinners: 2.7.0 + is-interactive: 2.0.0 + is-unicode-supported: 1.3.0 + log-symbols: 5.1.0 + strip-ansi: 7.0.1 + wcwidth: 1.0.1 + dev: true + + /os-tmpdir/1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: true + + /p-limit/1.3.0: + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} + dependencies: + p-try: 1.0.0 + dev: true + + /p-limit/2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + + /p-limit/3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate/2.0.0: + resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} + engines: {node: '>=4'} + dependencies: + p-limit: 1.3.0 + dev: true + + /p-locate/4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + + /p-locate/5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-map/4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + dependencies: + aggregate-error: 3.1.0 + dev: true + + /p-try/1.0.0: + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} + engines: {node: '>=4'} + dev: true + + /p-try/2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + /param-case/3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + dependencies: + dot-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /parent-module/1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json/4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + dependencies: + error-ex: 1.3.2 + json-parse-better-errors: 1.0.2 + dev: true + + /parse-json/5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.18.6 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /parse-node-version/1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + dev: true + + /parseurl/1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: true + + /pascal-case/3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /pascalcase/0.1.1: + resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} + engines: {node: '>=0.10.0'} + dev: true + + /path-case/3.0.4: + resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} + dependencies: + dot-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /path-exists/3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + dev: true + + /path-exists/4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + /path-is-absolute/1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key/2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + dev: true + + /path-key/3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key/4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-scurry/1.6.1: + resolution: {integrity: sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA==} + engines: {node: '>=14'} + dependencies: + lru-cache: 7.18.3 + minipass: 4.2.4 + dev: true + + /path-to-regexp/6.2.1: + resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} + + /path-type/3.0.0: + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} + dependencies: + pify: 3.0.0 + dev: true + + /path-type/4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pathe/0.2.0: + resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} + dev: true + + /pathe/1.1.0: + resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} + dev: true + + /picocolors/1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + /picomatch/2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pidtree/0.3.1: + resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + + /pidtree/0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + + /pify/2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: true + + /pify/3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + dev: true + + /pify/4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + dev: true + optional: true + + /pinia/2.0.33_p4srtwdgezcoeovfcc2yxzvaue: + resolution: {integrity: sha512-HOj1yVV2itw6rNIrR2f7+MirGNxhORjrULL8GWgRwXsGSvEqIQ+SE0MYt6cwtpegzCda3i+rVTZM+AM7CG+kRg==} + peerDependencies: + '@vue/composition-api': ^1.4.0 + typescript: '>=4.4.4' + vue: ^2.6.14 || ^3.2.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + typescript: + optional: true + dependencies: + '@vue/devtools-api': 6.5.0 + typescript: 5.0.2 + vue: 3.2.47 + vue-demi: 0.13.11_vue@3.2.47 + dev: false + + /pngjs/5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + dev: false + + /posix-character-classes/0.1.1: + resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} + engines: {node: '>=0.10.0'} + dev: true + + /postcss-html/1.5.0: + resolution: {integrity: sha512-kCMRWJRHKicpA166kc2lAVUGxDZL324bkj/pVOb6RhjB0Z5Krl7mN0AsVkBhVIRZZirY0lyQXG38HCVaoKVNoA==} + engines: {node: ^12 || >=14} + dependencies: + htmlparser2: 8.0.1 + js-tokens: 8.0.1 + postcss: 8.4.21 + postcss-safe-parser: 6.0.0_postcss@8.4.21 + dev: true + + /postcss-less/6.0.0_postcss@8.4.21: + resolution: {integrity: sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==} + engines: {node: '>=12'} + peerDependencies: + postcss: ^8.3.5 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-media-query-parser/0.2.3: + resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} + dev: true + + /postcss-prefix-selector/1.16.0_postcss@5.2.18: + resolution: {integrity: sha512-rdVMIi7Q4B0XbXqNUEI+Z4E+pueiu/CS5E6vRCQommzdQ/sgsS4dK42U7GX8oJR+TJOtT+Qv3GkNo6iijUMp3Q==} + peerDependencies: + postcss: '>4 <9' + dependencies: + postcss: 5.2.18 + dev: true + + /postcss-resolve-nested-selector/0.1.1: + resolution: {integrity: sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==} + dev: true + + /postcss-safe-parser/6.0.0_postcss@8.4.21: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-selector-parser/6.0.11: + resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-sorting/8.0.2_postcss@8.4.21: + resolution: {integrity: sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==} + peerDependencies: + postcss: ^8.4.20 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-value-parser/4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + + /postcss/5.2.18: + resolution: {integrity: sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==} + engines: {node: '>=0.12'} + dependencies: + chalk: 1.1.3 + js-base64: 2.6.4 + source-map: 0.5.7 + supports-color: 3.2.3 + dev: true + + /postcss/8.4.21: + resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + + /posthtml-parser/0.2.1: + resolution: {integrity: sha512-nPC53YMqJnc/+1x4fRYFfm81KV2V+G9NZY+hTohpYg64Ay7NemWWcV4UWuy/SgMupqQ3kJ88M/iRfZmSnxT+pw==} + dependencies: + htmlparser2: 3.10.1 + isobject: 2.1.0 + dev: true + + /posthtml-rename-id/1.0.12: + resolution: {integrity: sha512-UKXf9OF/no8WZo9edRzvuMenb6AD5hDLzIepJW+a4oJT+T/Lx7vfMYWT4aWlGNQh0WMhnUx1ipN9OkZ9q+ddEw==} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /posthtml-render/1.4.0: + resolution: {integrity: sha512-W1779iVHGfq0Fvh2PROhCe2QhB8mEErgqzo1wpIt36tCgChafP+hbXIhLDOM8ePJrZcFs0vkNEtdibEWVqChqw==} + engines: {node: '>=10'} + dev: true + + /posthtml-svg-mode/1.0.3: + resolution: {integrity: sha512-hEqw9NHZ9YgJ2/0G7CECOeuLQKZi8HjWLkBaSVtOWjygQ9ZD8P7tqeowYs7WrFdKsWEKG7o+IlsPY8jrr0CJpQ==} + dependencies: + merge-options: 1.0.1 + posthtml: 0.9.2 + posthtml-parser: 0.2.1 + posthtml-render: 1.4.0 + dev: true + + /posthtml/0.9.2: + resolution: {integrity: sha512-spBB5sgC4cv2YcW03f/IAUN1pgDJWNWD8FzkyY4mArLUMJW+KlQhlmUdKAHQuPfb00Jl5xIfImeOsf6YL8QK7Q==} + engines: {node: '>=0.10.0'} + dependencies: + posthtml-parser: 0.2.1 + posthtml-render: 1.4.0 + dev: true + + /preact/10.12.1: + resolution: {integrity: sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==} + dev: false + + /prelude-ls/1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-linter-helpers/1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.2.0 + dev: true + + /prettier/2.8.4: + resolution: {integrity: sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /pretty-bytes/5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + dev: true + + /pretty-bytes/6.1.0: + resolution: {integrity: sha512-Rk753HI8f4uivXi4ZCIYdhmG1V+WKzvRMg/X+M42a6t7D07RcmopXJMDNk6N++7Bl75URRGsb40ruvg7Hcp2wQ==} + engines: {node: ^14.13.1 || >=16.0.0} + dev: true + + /print-js/1.6.0: + resolution: {integrity: sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg==} + dev: false + + /process-nextick-args/2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true + + /progress/2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: true + + /proto-list/1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true + + /proxy-from-env/1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + /prr/1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + dev: true + optional: true + + /pseudomap/1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + dev: true + + /punycode/2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /q/1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + dev: true + + /qrcode/1.5.1: + resolution: {integrity: sha512-nS8NJ1Z3md8uTjKtP+SGGhfqmTCs5flU/xR623oI0JX+Wepz9R8UrRVCTBTJm3qGw3rH6jJ6MUHjkDx15cxSSg==} + engines: {node: '>=10.13.0'} + hasBin: true + dependencies: + dijkstrajs: 1.0.2 + encode-utf8: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + dev: false + + /qs/6.11.1: + resolution: {integrity: sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: false + + /query-string/4.3.4: + resolution: {integrity: sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==} + engines: {node: '>=0.10.0'} + dependencies: + object-assign: 4.1.1 + strict-uri-encode: 1.1.0 + dev: true + + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /quick-lru/4.0.1: + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} + dev: true + + /randombytes/2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /rd/2.0.1: + resolution: {integrity: sha512-/XdKU4UazUZTXFmI0dpABt8jSXPWcEyaGdk340KdHnsEOdkTctlX23aAK7ChQDn39YGNlAJr1M5uvaKt4QnpNw==} + dependencies: + '@types/node': 10.17.60 + dev: true + + /read-pkg-up/3.0.0: + resolution: {integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==} + engines: {node: '>=4'} + dependencies: + find-up: 2.1.0 + read-pkg: 3.0.0 + dev: true + + /read-pkg-up/7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + dev: true + + /read-pkg/3.0.0: + resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} + engines: {node: '>=4'} + dependencies: + load-json-file: 4.0.0 + normalize-package-data: 2.5.0 + path-type: 3.0.0 + dev: true + + /read-pkg/5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + dependencies: + '@types/normalize-package-data': 2.4.1 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + dev: true + + /readable-stream/2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: true + + /readable-stream/3.6.1: + resolution: {integrity: sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: true + + /readdirp/3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /redent/3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + dev: true + + /regenerate-unicode-properties/10.1.0: + resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} + engines: {node: '>=4'} + dependencies: + regenerate: 1.4.2 + dev: true + + /regenerate/1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + dev: true + + /regenerator-runtime/0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + + /regenerator-transform/0.15.1: + resolution: {integrity: sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==} + dependencies: + '@babel/runtime': 7.21.0 + dev: true + + /regex-not/1.0.2: + resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} + engines: {node: '>=0.10.0'} + dependencies: + extend-shallow: 3.0.2 + safe-regex: 1.1.0 + dev: true + + /regexp.prototype.flags/1.4.3: + resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + functions-have-names: 1.2.3 + dev: true + + /regexpu-core/5.3.1: + resolution: {integrity: sha512-nCOzW2V/X15XpLsK2rlgdwrysrBq+AauCn+omItIz4R1pIcmeot5zvjdmOBRLzEH/CkC6IxMJVmxDe3QcMuNVQ==} + engines: {node: '>=4'} + dependencies: + '@babel/regjsgen': 0.8.0 + regenerate: 1.4.2 + regenerate-unicode-properties: 10.1.0 + regjsparser: 0.9.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.1.0 + dev: true + + /regjsparser/0.9.1: + resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + + /relateurl/0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + dev: true + + /repeat-element/1.1.4: + resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} + engines: {node: '>=0.10.0'} + dev: true + + /repeat-string/1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + dev: true + + /require-directory/2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + /require-from-string/2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /require-main-filename/2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + dev: false + + /resize-observer-polyfill/1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + dev: false + + /resolve-from/4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-from/5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + + /resolve-global/1.0.0: + resolution: {integrity: sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==} + engines: {node: '>=8'} + dependencies: + global-dirs: 0.1.1 + dev: true + + /resolve-url/0.2.1: + resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} + deprecated: https://github.com/lydell/resolve-url#deprecated + dev: true + + /resolve/1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /restore-cursor/3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + + /restore-cursor/4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + + /ret/0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + dev: true + + /reusify/1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rfdc/1.3.0: + resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + dev: true + + /rimraf/3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rimraf/4.4.0: + resolution: {integrity: sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 9.2.1 + dev: true + + /rollup-plugin-purge-icons/0.9.1: + resolution: {integrity: sha512-hRDKBsPUz47UMdBufki2feTmBF2ClEJlYqL7N6vpVAHSLd7V2BJUaNKOF7YYbLMofVVF+9hm44YSkYO6k9hUgg==} + engines: {node: '>= 12'} + dependencies: + '@purge-icons/core': 0.9.1 + '@purge-icons/generated': 0.9.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /rollup-plugin-terser/7.0.2_rollup@2.79.1: + resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser + peerDependencies: + rollup: ^2.0.0 + dependencies: + '@babel/code-frame': 7.18.6 + jest-worker: 26.6.2 + rollup: 2.79.1 + serialize-javascript: 4.0.0 + terser: 5.16.6 + dev: true + + /rollup-plugin-visualizer/5.9.0_rollup@3.19.1: + resolution: {integrity: sha512-bbDOv47+Bw4C/cgs0czZqfm8L82xOZssk4ayZjG40y9zbXclNk7YikrZTDao6p7+HDiGxrN0b65SgZiVm9k1Cg==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + rollup: 2.x || 3.x + peerDependenciesMeta: + rollup: + optional: true + dependencies: + open: 8.4.2 + picomatch: 2.3.1 + rollup: 3.19.1 + source-map: 0.7.4 + yargs: 17.7.1 + dev: true + + /rollup/2.79.1: + resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /rollup/3.19.1: + resolution: {integrity: sha512-lAbrdN7neYCg/8WaoWn/ckzCtz+jr70GFfYdlf50OF7387HTg+wiuiqJRFYawwSPpqfqDNYqK7smY/ks2iAudg==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /run-async/2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + dev: true + + /run-parallel/1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /rxjs/7.8.0: + resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==} + dependencies: + tslib: 2.5.0 + dev: true + + /safe-buffer/5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /safe-regex-test/1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + is-regex: 1.1.4 + dev: true + + /safe-regex/1.1.0: + resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} + dependencies: + ret: 0.1.15 + dev: true + + /safer-buffer/2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true + + /sax/1.2.4: + resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + dev: true + optional: true + + /scroll-into-view-if-needed/2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + dependencies: + compute-scroll-into-view: 1.0.20 + dev: false + + /semver/5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: true + + /semver/6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: true + + /semver/7.3.8: + resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /sentence-case/3.0.4: + resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} + dependencies: + no-case: 3.0.4 + tslib: 2.5.0 + upper-case-first: 2.0.2 + dev: true + + /serialize-javascript/4.0.0: + resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + dependencies: + randombytes: 2.1.0 + dev: true + + /set-blocking/2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: false + + /set-value/2.0.1: + resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} + engines: {node: '>=0.10.0'} + dependencies: + extend-shallow: 2.0.1 + is-extendable: 0.1.1 + is-plain-object: 2.0.4 + split-string: 3.1.0 + dev: true + + /shallow-equal/1.2.1: + resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} + dev: false + + /shebang-command/1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + dependencies: + shebang-regex: 1.0.0 + dev: true + + /shebang-command/2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex/1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + dev: true + + /shebang-regex/3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /shell-quote/1.8.0: + resolution: {integrity: sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==} + dev: true + + /showdown/2.1.0: + resolution: {integrity: sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==} + hasBin: true + dependencies: + commander: 9.5.0 + dev: false + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + object-inspect: 1.12.3 + + /sigmund/1.0.1: + resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==} + dev: true + + /signal-exit/3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /slash/3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slice-ansi/3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /slice-ansi/4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /slice-ansi/5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + dev: true + + /snake-case/3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + dependencies: + dot-case: 3.0.4 + tslib: 2.5.0 + dev: true + + /snapdragon-node/2.1.1: + resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==} + engines: {node: '>=0.10.0'} + dependencies: + define-property: 1.0.0 + isobject: 3.0.1 + snapdragon-util: 3.0.1 + dev: true + + /snapdragon-util/3.0.1: + resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==} + engines: {node: '>=0.10.0'} + dependencies: + kind-of: 3.2.2 + dev: true + + /snapdragon/0.8.2: + resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} + engines: {node: '>=0.10.0'} + dependencies: + base: 0.11.2 + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + map-cache: 0.2.2 + source-map: 0.5.7 + source-map-resolve: 0.5.3 + use: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /sortablejs/1.15.0: + resolution: {integrity: sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==} + dev: false + + /source-map-js/1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + + /source-map-resolve/0.5.3: + resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated + dependencies: + atob: 2.1.2 + decode-uri-component: 0.2.2 + resolve-url: 0.2.1 + source-map-url: 0.4.1 + urix: 0.1.0 + dev: true + + /source-map-support/0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map-url/0.4.1: + resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} + deprecated: See https://github.com/lydell/source-map-url#deprecated + dev: true + + /source-map/0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map/0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + /source-map/0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + + /source-map/0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + dependencies: + whatwg-url: 7.1.0 + dev: true + + /sourcemap-codec/1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + + /spdx-correct/3.1.1: + resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.12 + dev: true + + /spdx-exceptions/2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse/3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.12 + dev: true + + /spdx-license-ids/3.0.12: + resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==} + dev: true + + /split-string/3.1.0: + resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} + engines: {node: '>=0.10.0'} + dependencies: + extend-shallow: 3.0.2 + dev: true + + /split/1.0.1: + resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} + dependencies: + through: 2.3.8 + dev: true + + /split2/3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + dependencies: + readable-stream: 3.6.1 + dev: true + + /ssf/0.11.2: + resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} + engines: {node: '>=0.8'} + dependencies: + frac: 1.1.2 + dev: false + + /stable/0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + dev: true + + /static-extend/0.1.2: + resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} + engines: {node: '>=0.10.0'} + dependencies: + define-property: 0.2.5 + object-copy: 0.1.0 + dev: true + + /statuses/1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + dev: true + + /strict-uri-encode/1.1.0: + resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==} + engines: {node: '>=0.10.0'} + dev: true + + /string-argv/0.3.1: + resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} + engines: {node: '>=0.6.19'} + dev: true + + /string-width/4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + /string-width/5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.0.1 + dev: true + + /string.prototype.matchall/4.0.8: + resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + get-intrinsic: 1.2.0 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + regexp.prototype.flags: 1.4.3 + side-channel: 1.0.4 + dev: true + + /string.prototype.padend/3.1.4: + resolution: {integrity: sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + dev: true + + /string.prototype.trimend/1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + dev: true + + /string.prototype.trimstart/1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.1 + dev: true + + /string_decoder/1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /string_decoder/1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /stringify-object/3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + dev: true + + /strip-ansi/3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + dependencies: + ansi-regex: 2.1.1 + dev: true + + /strip-ansi/6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + + /strip-ansi/7.0.1: + resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-bom/3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-comments/2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + dev: true + + /strip-final-newline/2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-final-newline/3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-indent/3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments/3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /style-search/0.1.0: + resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} + dev: true + + /stylelint-config-html/1.1.0_kbto3rg3njmczth2rrsgfnlsqa: + resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + dependencies: + postcss-html: 1.5.0 + stylelint: 14.16.1 + dev: true + + /stylelint-config-prettier/9.0.5_stylelint@14.16.1: + resolution: {integrity: sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==} + engines: {node: '>= 12'} + hasBin: true + peerDependencies: + stylelint: '>= 11.x < 15' + dependencies: + stylelint: 14.16.1 + dev: true + + /stylelint-config-recommended-vue/1.4.0_kbto3rg3njmczth2rrsgfnlsqa: + resolution: {integrity: sha512-DVJqyX2KvMCn9U0+keL12r7xlsH26K4Vg8NrIZuq5MoF7g82DpMp326Om4E0Q+Il1o+bTHuUyejf2XAI0iD04Q==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + dependencies: + postcss-html: 1.5.0 + semver: 7.3.8 + stylelint: 14.16.1 + stylelint-config-html: 1.1.0_kbto3rg3njmczth2rrsgfnlsqa + stylelint-config-recommended: 9.0.0_stylelint@14.16.1 + dev: true + + /stylelint-config-recommended/9.0.0_stylelint@14.16.1: + resolution: {integrity: sha512-9YQSrJq4NvvRuTbzDsWX3rrFOzOlYBmZP+o513BJN/yfEmGSr0AxdvrWs0P/ilSpVV/wisamAHu5XSk8Rcf4CQ==} + peerDependencies: + stylelint: ^14.10.0 + dependencies: + stylelint: 14.16.1 + dev: true + + /stylelint-config-standard/29.0.0_stylelint@14.16.1: + resolution: {integrity: sha512-uy8tZLbfq6ZrXy4JKu3W+7lYLgRQBxYTUUB88vPgQ+ZzAxdrvcaSUW9hOMNLYBnwH+9Kkj19M2DHdZ4gKwI7tg==} + peerDependencies: + stylelint: ^14.14.0 + dependencies: + stylelint: 14.16.1 + stylelint-config-recommended: 9.0.0_stylelint@14.16.1 + dev: true + + /stylelint-order/6.0.3_stylelint@14.16.1: + resolution: {integrity: sha512-1j1lOb4EU/6w49qZeT2SQVJXm0Ht+Qnq9GMfUa3pMwoyojIWfuA+JUDmoR97Bht1RLn4ei0xtLGy87M7d29B1w==} + peerDependencies: + stylelint: ^14.0.0 || ^15.0.0 + dependencies: + postcss: 8.4.21 + postcss-sorting: 8.0.2_postcss@8.4.21 + stylelint: 14.16.1 + dev: true + + /stylelint/14.16.1: + resolution: {integrity: sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + dependencies: + '@csstools/selector-specificity': 2.1.1_wajs5nedgkikc5pcuwett7legi + balanced-match: 2.0.0 + colord: 2.9.3 + cosmiconfig: 7.1.0 + css-functions-list: 3.1.0 + debug: 4.3.4 + fast-glob: 3.2.12 + fastest-levenshtein: 1.0.16 + file-entry-cache: 6.0.1 + global-modules: 2.0.0 + globby: 11.1.0 + globjoin: 0.1.4 + html-tags: 3.2.0 + ignore: 5.2.4 + import-lazy: 4.0.0 + imurmurhash: 0.1.4 + is-plain-object: 5.0.0 + known-css-properties: 0.26.0 + mathml-tag-names: 2.1.3 + meow: 9.0.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-media-query-parser: 0.2.3 + postcss-resolve-nested-selector: 0.1.1 + postcss-safe-parser: 6.0.0_postcss@8.4.21 + postcss-selector-parser: 6.0.11 + postcss-value-parser: 4.2.0 + resolve-from: 5.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + style-search: 0.1.0 + supports-hyperlinks: 2.3.0 + svg-tags: 1.0.0 + table: 6.8.1 + v8-compile-cache: 2.3.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /supports-color/2.0.0: + resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} + engines: {node: '>=0.8.0'} + dev: true + + /supports-color/3.2.3: + resolution: {integrity: sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==} + engines: {node: '>=0.8.0'} + dependencies: + has-flag: 1.0.0 + dev: true + + /supports-color/5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color/7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-hyperlinks/2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + dev: true + + /supports-preserve-symlinks-flag/1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /svg-baker/1.7.0: + resolution: {integrity: sha512-nibslMbkXOIkqKVrfcncwha45f97fGuAOn1G99YwnwTj8kF9YiM6XexPcUso97NxOm6GsP0SIvYVIosBis1xLg==} + dependencies: + bluebird: 3.7.2 + clone: 2.1.2 + he: 1.2.0 + image-size: 0.5.5 + loader-utils: 1.4.2 + merge-options: 1.0.1 + micromatch: 3.1.0 + postcss: 5.2.18 + postcss-prefix-selector: 1.16.0_postcss@5.2.18 + posthtml-rename-id: 1.0.12 + posthtml-svg-mode: 1.0.3 + query-string: 4.3.4 + traverse: 0.6.7 + transitivePeerDependencies: + - supports-color + dev: true + + /svg-tags/1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + dev: true + + /svgo/2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.0.0 + stable: 0.1.8 + dev: true + + /systemjs/6.14.0: + resolution: {integrity: sha512-OMf+kFCYG9fLQerUyw/QVIPfZ+lo579R+usrDzSrZAkvMl6B0tHtc4rUP7DFaPCr7Sy6p5DYD4V9OCF1Sp6+vA==} + dev: true + + /table/6.8.1: + resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} + engines: {node: '>=10.0.0'} + dependencies: + ajv: 8.12.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /temp-dir/2.0.0: + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} + dev: true + + /tempfile/3.0.0: + resolution: {integrity: sha512-uNFCg478XovRi85iD42egu+eSFUmmka750Jy7L5tfHI5hQKKtbPnxaSaXAbBqCDYrw3wx4tXjKwci4/QmsZJxw==} + engines: {node: '>=8'} + dependencies: + temp-dir: 2.0.0 + uuid: 3.4.0 + dev: true + + /tempy/0.6.0: + resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} + engines: {node: '>=10'} + dependencies: + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + dev: true + + /terser/5.16.6: + resolution: {integrity: sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.2 + acorn: 8.8.2 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + + /text-extensions/1.9.0: + resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} + engines: {node: '>=0.10'} + dev: true + + /text-table/0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /through/2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: true + + /through2/2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + dependencies: + readable-stream: 2.3.8 + xtend: 4.0.2 + dev: true + + /through2/4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + dependencies: + readable-stream: 3.6.1 + dev: true + + /tinycolor2/1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + dev: true + + /tinymce/5.10.7: + resolution: {integrity: sha512-9UUjaO0R7FxcFo0oxnd1lMs7H+D0Eh+dDVo5hKbVe1a+VB0nit97vOqlinj+YwgoBDt6/DSCUoWqAYlLI8BLYA==} + dev: false + + /tmp/0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + dependencies: + os-tmpdir: 1.0.2 + dev: true + + /to-fast-properties/2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + /to-object-path/0.3.0: + resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} + engines: {node: '>=0.10.0'} + dependencies: + kind-of: 3.2.2 + dev: true + + /to-regex-range/2.1.1: + resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} + engines: {node: '>=0.10.0'} + dependencies: + is-number: 3.0.0 + repeat-string: 1.6.1 + dev: true + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /to-regex/3.0.2: + resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} + engines: {node: '>=0.10.0'} + dependencies: + define-property: 2.0.2 + extend-shallow: 3.0.2 + regex-not: 1.0.2 + safe-regex: 1.1.0 + dev: true + + /tr46/0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: true + + /tr46/1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + dependencies: + punycode: 2.3.0 + dev: true + + /traverse/0.6.7: + resolution: {integrity: sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==} + dev: true + + /trim-newlines/3.0.1: + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} + dev: true + + /ts-node/10.9.1_cbfmry4sbbh4vatmdrsmatfg5a: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + '@types/node': 18.15.3 + acorn: 8.8.2 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.9.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /ts-node/10.9.1_sxidjv3cojnrggmso45tj7hldi: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + '@types/node': 18.15.3 + acorn: 8.8.2 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.0.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tslib/1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib/2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + dev: false + + /tslib/2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + dev: true + + /tsutils/3.21.0_typescript@5.0.2: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.0.2 + dev: true + + /tsx/3.12.3: + resolution: {integrity: sha512-Wc5BFH1xccYTXaQob+lEcimkcb/Pq+0en2s+ruiX0VEIC80nV7/0s7XRahx8NnsoCnpCVUPz8wrqVSPi760LkA==} + hasBin: true + dependencies: + '@esbuild-kit/cjs-loader': 2.4.2 + '@esbuild-kit/core-utils': 3.1.0 + '@esbuild-kit/esm-loader': 2.5.5 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /type-check/0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest/0.16.0: + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} + dev: true + + /type-fest/0.18.1: + resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} + engines: {node: '>=10'} + dev: true + + /type-fest/0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest/0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /type-fest/0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + dev: true + + /type-fest/0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + dev: true + + /type-fest/3.6.0: + resolution: {integrity: sha512-RqTRtKTzvPpNdDUp1dVkKQRunlPITk4mXeqFlAZoJsS+fLRn8AdPK0TcQDumGayhU7fjlBfiBjsq3pe3rIfXZQ==} + engines: {node: '>=14.16'} + dev: true + + /typed-array-length/1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.10 + dev: true + + /typescript/4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /typescript/5.0.2: + resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==} + engines: {node: '>=12.20'} + hasBin: true + + /uglify-js/3.17.4: + resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} + engines: {node: '>=0.8.0'} + hasBin: true + requiresBuild: true + dev: true + optional: true + + /unbox-primitive/1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /unicode-canonical-property-names-ecmascript/2.0.0: + resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + engines: {node: '>=4'} + dev: true + + /unicode-match-property-ecmascript/2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.0 + unicode-property-aliases-ecmascript: 2.1.0 + dev: true + + /unicode-match-property-value-ecmascript/2.1.0: + resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} + engines: {node: '>=4'} + dev: true + + /unicode-property-aliases-ecmascript/2.1.0: + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} + dev: true + + /union-value/1.0.1: + resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} + engines: {node: '>=0.10.0'} + dependencies: + arr-union: 3.1.0 + get-value: 2.0.6 + is-extendable: 0.1.1 + set-value: 2.0.1 + dev: true + + /unique-string/2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + dependencies: + crypto-random-string: 2.0.0 + dev: true + + /universal-user-agent/6.0.0: + resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==} + dev: true + + /universalify/2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + dev: true + + /unpipe/1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: true + + /unplugin-vue-setup-extend-plus/0.4.9: + resolution: {integrity: sha512-4a4CvMkoZRuUHLQq1kkcuZ7HG85xM6MPMa+E1pEf5/PX6ovaCXwZ20qVOIQUIGkh3sHVp0VBhJaibHzC8hPtoA==} + dependencies: + '@vue/compiler-sfc': 3.2.47 + magic-string: 0.26.7 + unplugin: 1.1.0 + dev: true + + /unplugin/1.1.0: + resolution: {integrity: sha512-I8obQ8Rs/hnkxokRV6g8JKOQFgYNnTd9DL58vcSt5IJ9AkK8wbrtsnzD5hi4BJlvcY536JzfEXj9L6h7j559/A==} + dependencies: + acorn: 8.8.2 + chokidar: 3.5.3 + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.5.0 + dev: true + + /unset-value/1.0.0: + resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} + engines: {node: '>=0.10.0'} + dependencies: + has-value: 0.3.1 + isobject: 3.0.1 + dev: true + + /upath/1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + dev: true + + /update-browserslist-db/1.0.10_browserslist@4.21.5: + resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.5 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /upper-case-first/2.0.2: + resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} + dependencies: + tslib: 2.5.0 + dev: true + + /upper-case/2.0.2: + resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} + dependencies: + tslib: 2.5.0 + dev: true + + /uri-js/4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /urix/0.1.0: + resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} + deprecated: Please see https://github.com/lydell/urix#deprecated + dev: true + + /use/3.1.1: + resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} + engines: {node: '>=0.10.0'} + dev: true + + /util-deprecate/1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /utils-merge/1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + dev: true + + /uuid/3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + hasBin: true + dev: true + + /v8-compile-cache-lib/3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /v8-compile-cache/2.3.0: + resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} + dev: true + + /validate-npm-package-license/3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.1.1 + spdx-expression-parse: 3.0.1 + dev: true + + /vary/1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: true + + /vditor/3.9.1: + resolution: {integrity: sha512-pu9+4duq/RPftiGas9QCxy8FwwFx2jPEZd2/xKIsI8BMUzwKL5M65LH0cAkmrSUwZSXACLBWGM3RYahK5n1CUA==} + dependencies: + diff-match-patch: 1.0.5 + dev: false + + /vite-plugin-compression/0.5.1_vite@4.2.0: + resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==} + peerDependencies: + vite: '>=2.0.0' + dependencies: + chalk: 4.1.2 + debug: 4.3.4 + fs-extra: 10.1.0 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + transitivePeerDependencies: + - supports-color + dev: true + + /vite-plugin-html/3.2.0_vite@4.2.0: + resolution: {integrity: sha512-2VLCeDiHmV/BqqNn5h2V+4280KRgQzCFN47cst3WiNK848klESPQnzuC3okH5XHtgwHH/6s1Ho/YV6yIO0pgoQ==} + peerDependencies: + vite: '>=2.0.0' + dependencies: + '@rollup/pluginutils': 4.2.1 + colorette: 2.0.19 + connect-history-api-fallback: 1.6.0 + consola: 2.15.3 + dotenv: 16.0.3 + dotenv-expand: 8.0.3 + ejs: 3.1.8 + fast-glob: 3.2.12 + fs-extra: 10.1.0 + html-minifier-terser: 6.1.0 + node-html-parser: 5.4.2 + pathe: 0.2.0 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + dev: true + + /vite-plugin-mkcert/1.13.3_vite@4.2.0: + resolution: {integrity: sha512-cuAbOb6bHyTMhha9YimL6ZB/WoCcFy8d3uaf5n5Ovgybix1yvEu3pxARy2HjC5LJIS1qq6fFXG0+oj6/XMUapA==} + engines: {node: '>=v16.7.0'} + peerDependencies: + vite: '>=3' + dependencies: + '@octokit/rest': 19.0.7 + axios: 1.3.4_debug@4.3.4 + debug: 4.3.4 + picocolors: 1.0.0 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /vite-plugin-mock/2.9.6_3a7irfe7b7ve75yvq4zwteqfdy: + resolution: {integrity: sha512-/Rm59oPppe/ncbkSrUuAxIQihlI2YcBmnbR4ST1RA2VzM1C0tEQc1KlbQvnUGhXECAGTaQN2JyasiwXP6EtKgg==} + engines: {node: '>=12.0.0'} + peerDependencies: + mockjs: '>=1.1.0' + vite: '>=2.0.0' + dependencies: + '@rollup/plugin-node-resolve': 13.3.0_rollup@3.19.1 + '@types/mockjs': 1.0.7 + chalk: 4.1.2 + chokidar: 3.5.3 + connect: 3.7.0 + debug: 4.3.4 + esbuild: 0.11.3 + fast-glob: 3.2.12 + mockjs: 1.1.0 + path-to-regexp: 6.2.1 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + transitivePeerDependencies: + - rollup + - supports-color + dev: true + + /vite-plugin-progress/0.0.6_vite@4.2.0: + resolution: {integrity: sha512-pIK2TVEY4XFGrz10CQDdEufBBCDaV0geRHfXV3abGTBr+OF9O0Zmd3ZDrHJXDv4Rl3qAQP4BTCuPYQ3XqstmqA==} + engines: {node: '>=14', pnpm: '>=7.0.0'} + peerDependencies: + vite: '>2.0.0-0' + dependencies: + picocolors: 1.0.0 + progress: 2.0.3 + rd: 2.0.1 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + dev: true + + /vite-plugin-purge-icons/0.9.2_vite@4.2.0: + resolution: {integrity: sha512-vxJEMyNyckqLr/4HPsW9P6BMLUvOVMvjjFz3jLl4Wke1KWE8ITJUxIUwodxaOmEp9L2lxVk5an3TYeycZCfqFw==} + engines: {node: '>= 12'} + peerDependencies: + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 + dependencies: + '@purge-icons/core': 0.9.1 + '@purge-icons/generated': 0.9.0 + rollup-plugin-purge-icons: 0.9.1 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /vite-plugin-pwa/0.14.4_vite@4.2.0: + resolution: {integrity: sha512-M7Ct0so8OlouMkTWgXnl8W1xU95glITSKIe7qswZf1tniAstO2idElGCnsrTJ5NPNSx1XqfTCOUj8j94S6FD7Q==} + peerDependencies: + vite: ^3.1.0 || ^4.0.0 + dependencies: + '@rollup/plugin-replace': 5.0.2_rollup@3.19.1 + debug: 4.3.4 + fast-glob: 3.2.12 + pretty-bytes: 6.1.0 + rollup: 3.19.1 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + workbox-build: 6.5.4 + workbox-window: 6.5.4 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + dev: true + + /vite-plugin-style-import/2.0.0_vite@4.2.0: + resolution: {integrity: sha512-qtoHQae5dSUQPo/rYz/8p190VU5y19rtBaeV7ryLa/AYAU/e9CG89NrN/3+k7MR8mJy/GPIu91iJ3zk9foUOSA==} + peerDependencies: + vite: '>=2.0.0' + dependencies: + '@rollup/pluginutils': 4.2.1 + change-case: 4.1.2 + console: 0.7.2 + es-module-lexer: 0.9.3 + fs-extra: 10.1.0 + magic-string: 0.25.9 + pathe: 0.2.0 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + dev: true + + /vite-plugin-svg-icons/2.0.1_vite@4.2.0: + resolution: {integrity: sha512-6ktD+DhV6Rz3VtedYvBKKVA2eXF+sAQVaKkKLDSqGUfnhqXl3bj5PPkVTl3VexfTuZy66PmINi8Q6eFnVfRUmA==} + peerDependencies: + vite: '>=2.0.0' + dependencies: + '@types/svgo': 2.6.4 + cors: 2.8.5 + debug: 4.3.4 + etag: 1.8.1 + fs-extra: 10.1.0 + pathe: 0.2.0 + svg-baker: 1.7.0 + svgo: 2.8.0 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + transitivePeerDependencies: + - supports-color + dev: true + + /vite-plugin-windicss/1.8.10_vite@4.2.0: + resolution: {integrity: sha512-scywsuzo46lcTBohspmF0WiwhWEte6p+OUVrX4yr7VMRvLHMHVfLtJReyD5pppjijG7YOwVsZn7XBWWZtF658Q==} + peerDependencies: + vite: ^2.0.1 || ^3.0.0 || ^4.0.0 + dependencies: + '@windicss/plugin-utils': 1.8.10 + debug: 4.3.4 + kolorist: 1.7.0 + vite: 4.2.0_kfn5zdpk76mco3hnivyqwkouli + windicss: 3.5.6 + transitivePeerDependencies: + - supports-color + dev: true + + /vite/4.2.0_kfn5zdpk76mco3hnivyqwkouli: + resolution: {integrity: sha512-AbDTyzzwuKoRtMIRLGNxhLRuv1FpRgdIw+1y6AQG73Q5+vtecmvzKo/yk8X/vrHDpETRTx01ABijqUHIzBXi0g==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.15.3 + esbuild: 0.17.10 + less: 4.1.3 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 3.19.1 + terser: 5.16.6 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vue-demi/0.13.11_vue@3.2.47: + resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.2.47 + dev: false + + /vue-eslint-parser/9.1.0_eslint@8.36.0: + resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + debug: 4.3.4 + eslint: 8.36.0 + eslint-scope: 7.1.1 + eslint-visitor-keys: 3.3.0 + espree: 9.4.1 + esquery: 1.4.2 + lodash: 4.17.21 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + dev: true + + /vue-i18n/9.2.2_vue@3.2.47: + resolution: {integrity: sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==} + engines: {node: '>= 14'} + peerDependencies: + vue: ^3.0.0 + dependencies: + '@intlify/core-base': 9.2.2 + '@intlify/shared': 9.2.2 + '@intlify/vue-devtools': 9.2.2 + '@vue/devtools-api': 6.5.0 + vue: 3.2.47 + dev: false + + /vue-json-pretty/2.2.3_vue@3.2.47: + resolution: {integrity: sha512-tJo+4eFclQBt3gJ6EELXNdvo50wDKTZYthwmLpy9YS7UDldeJln5ff+IpdmUglfk+FqLVOX/re0+Ni/EOUPZgw==} + engines: {node: '>= 10.0.0', npm: '>= 5.0.0'} + peerDependencies: + vue: '>=3.0.0' + dependencies: + vue: 3.2.47 + dev: false + + /vue-router/4.1.6_vue@3.2.47: + resolution: {integrity: sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==} + peerDependencies: + vue: ^3.2.0 + dependencies: + '@vue/devtools-api': 6.5.0 + vue: 3.2.47 + dev: false + + /vue-template-compiler/2.7.14: + resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==} + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + dev: true + + /vue-tsc/1.2.0_typescript@5.0.2: + resolution: {integrity: sha512-rIlzqdrhyPYyLG9zxsVRa+JEseeS9s8F2BbVVVWRRsTZvJO2BbhLEb2HW3MY+DFma0378tnIqs+vfTzbcQtRFw==} + hasBin: true + peerDependencies: + typescript: '*' + dependencies: + '@volar/vue-language-core': 1.2.0 + '@volar/vue-typescript': 1.2.0 + typescript: 5.0.2 + dev: true + + /vue-types/3.0.2_vue@3.2.47: + resolution: {integrity: sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==} + engines: {node: '>=10.15.0'} + peerDependencies: + vue: ^3.0.0 + dependencies: + is-plain-object: 3.0.1 + vue: 3.2.47 + dev: false + + /vue-types/5.0.2_vue@3.2.47: + resolution: {integrity: sha512-+/5hnQ65XOYqPs+tEUF8GGTJX95UFVH0wPQo71IJJYh5TKMfik2tGKTLkZ42JqAczANA9hGu5FrZmPgxn20nnA==} + engines: {node: '>=14.0.0'} + peerDependencies: + vue: ^2.0.0 || ^3.0.0 + peerDependenciesMeta: + vue: + optional: true + dependencies: + is-plain-object: 5.0.0 + vue: 3.2.47 + dev: false + + /vue/3.2.47: + resolution: {integrity: sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==} + dependencies: + '@vue/compiler-dom': 3.2.47 + '@vue/compiler-sfc': 3.2.47 + '@vue/runtime-dom': 3.2.47 + '@vue/server-renderer': 3.2.47_vue@3.2.47 + '@vue/shared': 3.2.47 + + /warning/4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /wcwidth/1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + dependencies: + defaults: 1.0.4 + dev: true + + /webidl-conversions/3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: true + + /webidl-conversions/4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + dev: true + + /webpack-sources/3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + dev: true + + /webpack-virtual-modules/0.5.0: + resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} + dev: true + + /whatwg-url/5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: true + + /whatwg-url/7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + dev: true + + /which-boxed-primitive/1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-module/2.0.0: + resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} + dev: false + + /which-typed-array/1.1.9: + resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + is-typed-array: 1.1.10 + dev: true + + /which/1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /which/2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /windicss/3.5.6: + resolution: {integrity: sha512-P1mzPEjgFMZLX0ZqfFht4fhV/FX8DTG7ERG1fBLiWvd34pTLVReS5CVsewKn9PApSgXnVfPWwvq+qUsRwpnwFA==} + engines: {node: '>= 12'} + hasBin: true + dev: true + + /wmf/1.0.2: + resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} + engines: {node: '>=0.8'} + dev: false + + /word-wrap/1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + dev: true + + /word/0.3.0: + resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} + engines: {node: '>=0.8'} + dev: false + + /wordwrap/1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + dev: true + + /workbox-background-sync/6.5.4: + resolution: {integrity: sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==} + dependencies: + idb: 7.1.1 + workbox-core: 6.5.4 + dev: true + + /workbox-broadcast-update/6.5.4: + resolution: {integrity: sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-build/6.5.4: + resolution: {integrity: sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==} + engines: {node: '>=10.0.0'} + dependencies: + '@apideck/better-ajv-errors': 0.3.6_ajv@8.12.0 + '@babel/core': 7.21.0 + '@babel/preset-env': 7.20.2_@babel+core@7.21.0 + '@babel/runtime': 7.21.0 + '@rollup/plugin-babel': 5.3.1_4tnfxcmsyr7y5qv3uwkivwqysm + '@rollup/plugin-node-resolve': 11.2.1_rollup@2.79.1 + '@rollup/plugin-replace': 2.4.2_rollup@2.79.1 + '@surma/rollup-plugin-off-main-thread': 2.2.3 + ajv: 8.12.0 + common-tags: 1.8.2 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 7.2.3 + lodash: 4.17.21 + pretty-bytes: 5.6.0 + rollup: 2.79.1 + rollup-plugin-terser: 7.0.2_rollup@2.79.1 + source-map: 0.8.0-beta.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 6.5.4 + workbox-broadcast-update: 6.5.4 + workbox-cacheable-response: 6.5.4 + workbox-core: 6.5.4 + workbox-expiration: 6.5.4 + workbox-google-analytics: 6.5.4 + workbox-navigation-preload: 6.5.4 + workbox-precaching: 6.5.4 + workbox-range-requests: 6.5.4 + workbox-recipes: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + workbox-streams: 6.5.4 + workbox-sw: 6.5.4 + workbox-window: 6.5.4 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + dev: true + + /workbox-cacheable-response/6.5.4: + resolution: {integrity: sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-core/6.5.4: + resolution: {integrity: sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==} + dev: true + + /workbox-expiration/6.5.4: + resolution: {integrity: sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==} + dependencies: + idb: 7.1.1 + workbox-core: 6.5.4 + dev: true + + /workbox-google-analytics/6.5.4: + resolution: {integrity: sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==} + dependencies: + workbox-background-sync: 6.5.4 + workbox-core: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + dev: true + + /workbox-navigation-preload/6.5.4: + resolution: {integrity: sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-precaching/6.5.4: + resolution: {integrity: sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==} + dependencies: + workbox-core: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + dev: true + + /workbox-range-requests/6.5.4: + resolution: {integrity: sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-recipes/6.5.4: + resolution: {integrity: sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==} + dependencies: + workbox-cacheable-response: 6.5.4 + workbox-core: 6.5.4 + workbox-expiration: 6.5.4 + workbox-precaching: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + dev: true + + /workbox-routing/6.5.4: + resolution: {integrity: sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-strategies/6.5.4: + resolution: {integrity: sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==} + dependencies: + workbox-core: 6.5.4 + dev: true + + /workbox-streams/6.5.4: + resolution: {integrity: sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==} + dependencies: + workbox-core: 6.5.4 + workbox-routing: 6.5.4 + dev: true + + /workbox-sw/6.5.4: + resolution: {integrity: sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==} + dev: true + + /workbox-window/6.5.4: + resolution: {integrity: sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==} + dependencies: + '@types/trusted-types': 2.0.3 + workbox-core: 6.5.4 + dev: true + + /wrap-ansi/6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + /wrap-ansi/7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi/8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.0.1 + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /write-file-atomic/4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + + /xlsx/0.18.5: + resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} + engines: {node: '>=0.8'} + hasBin: true + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 + dev: false + + /xml-name-validator/4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + dev: true + + /xtend/4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + + /y18n/4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + dev: false + + /y18n/5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist/2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + dev: true + + /yallist/3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml/1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + + /yaml/2.2.1: + resolution: {integrity: sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==} + engines: {node: '>= 14'} + dev: true + + /yargs-parser/18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: false + + /yargs-parser/20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true + + /yargs-parser/21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs/15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.0 + y18n: 4.0.3 + yargs-parser: 18.1.3 + dev: false + + /yargs/16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + dev: true + + /yargs/17.7.1: + resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yn/3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue/0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /zrender/5.4.1: + resolution: {integrity: sha512-M4Z05BHWtajY2241EmMPHglDQAJ1UyHQcYsxDNzD9XLSkPDqMq4bB28v9Pb4mvHnVQ0GxyTklZ/69xCFP6RXBA==} + dependencies: + tslib: 2.3.0 + dev: false diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..961986e2 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + autoprefixer: {} + } +} diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 00000000..19151123 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,37 @@ +module.exports = { + // 一行代码的最大字符数,默认是80 + printWidth: 140, + // tab宽度为2空格 + tabWidth: 2, + // 使用tab缩进,默认false + useTabs: false, + // 结尾是否添加分号, 默认true + semi: false, + // vue script和style标签中是否缩进,开启可能会破坏编辑器的代码折叠 + vueIndentScriptAndStyle: false, + // 使用单引号, 默认false(在jsx中配置无效, 默认都是双引号) + singleQuote: true, + // object对象中key值是否加引号 as-needed只有在需求要的情况下加引号,consistent是有一个需要引号就统一加,preserve是保留用户输入的引号 + quoteProps: 'as-needed', + // object对象里面的key和value值和括号间的空格 + bracketSpacing: true, + // 行尾逗号,默认none,可选 none|es5|all + // es5 包括es5中的数组、对象 + // all 包括函数对象等所有可选 + trailingComma: 'none', + // 在jsx文件中的引号需要单独设置 默认false + jsxSingleQuote: false, + // 箭头函数单个参数的情况是否省略括号,默认always是总是带括号 + // avoid 能省略括号的时候就省略 例如x => x + // always 总是有括号 + arrowParens: 'always', + insertPragma: false, + requirePragma: false, + proseWrap: 'never', + htmlWhitespaceSensitivity: 'strict', + // endOfLine: "" 行尾换行符,默认是lf + endOfLine: 'auto', + // range是format执行的范围,可以选执行一个文件的一部分,默认的设置是整个文件 + rangeStart: 0, + rangeEnd: Infinity +} diff --git a/public/favicon.ico b/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/public/resource/img/logo.png b/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/public/resource/img/pwa-192x192.png b/public/resource/img/pwa-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..00fb815215e1fcb171d1c62fca6c54018663cb72 GIT binary patch literal 12205 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 s+6jerali:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:1298}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #b4d7ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #b4d7ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #b4d7ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#b4d7ff}.mce-content-body .mce-edit-focus{outline:3px solid #b4d7ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid rgba(180,215,255,.7);bottom:-1px;content:'';left:-1px;mix-blend-mode:multiply;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#b4d7ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'} diff --git a/public/resource/tinymce/skins/ui/oxide-dark/content.min.css b/public/resource/tinymce/skins/ui/oxide-dark/content.min.css new file mode 100644 index 00000000..e27b8a02 --- /dev/null +++ b/public/resource/tinymce/skins/ui/oxide-dark/content.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%236d737b%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#f8f8f2;background:0 0;text-shadow:0 1px rgba(0,0,0,.3);font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background:#282a36}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#6272a4}.token.punctuation{color:#f8f8f2}.namespace{opacity:.7}.token.constant,.token.deleted,.token.property,.token.symbol,.token.tag{color:#ff79c6}.token.boolean,.token.number{color:#bd93f9}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#50fa7b}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url,.token.variable{color:#f8f8f2}.token.atrule,.token.attr-value,.token.class-name,.token.function{color:#f1fa8c}.token.keyword{color:#8be9fd}.token.important,.token.regex{color:#ffb86c}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:1298}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #4099ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #4099ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #4099ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #4099ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #4099ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#4099ff}.mce-content-body .mce-edit-focus{outline:3px solid #4099ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid transparent;bottom:-1px;content:'';left:-1px;mix-blend-mode:lighten;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#4099ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'}body{font-family:sans-serif}table{border-collapse:collapse} diff --git a/public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css b/public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css new file mode 100644 index 00000000..35f7dc08 --- /dev/null +++ b/public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse} diff --git a/public/resource/tinymce/skins/ui/oxide-dark/skin.min.css b/public/resource/tinymce/skins/ui/oxide-dark/skin.min.css new file mode 100644 index 00000000..e71f6f01 --- /dev/null +++ b/public/resource/tinymce/skins/ui/oxide-dark/skin.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tox{box-shadow:none;box-sizing:content-box;color:#2a3746;cursor:auto;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:normal;-webkit-tap-highlight-color:transparent;text-decoration:none;text-shadow:none;text-transform:none;vertical-align:initial;white-space:normal}.tox :not(svg):not(rect){box-sizing:inherit;color:inherit;cursor:inherit;direction:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;line-height:inherit;-webkit-tap-highlight-color:inherit;text-align:inherit;text-decoration:inherit;text-shadow:inherit;text-transform:inherit;vertical-align:inherit;white-space:inherit}.tox :not(svg):not(rect){background:0 0;border:0;box-shadow:none;float:none;height:auto;margin:0;max-width:none;outline:0;padding:0;position:static;width:auto}.tox:not([dir=rtl]){direction:ltr;text-align:left}.tox[dir=rtl]{direction:rtl;text-align:right}.tox-tinymce{border:1px solid #000;border-radius:0;box-shadow:none;box-sizing:border-box;display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;overflow:hidden;position:relative;visibility:inherit!important}.tox-tinymce-inline{border:none;box-shadow:none}.tox-tinymce-inline .tox-editor-header{background-color:transparent;border:1px solid #000;border-radius:0;box-shadow:none}.tox-tinymce-aux{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;z-index:1300}.tox-tinymce :focus,.tox-tinymce-aux :focus{outline:0}button::-moz-focus-inner{border:0}.tox[dir=rtl] .tox-icon--flip svg{transform:rotateY(180deg)}.tox .accessibility-issue__header{align-items:center;display:flex;margin-bottom:4px}.tox .accessibility-issue__description{align-items:stretch;border:1px solid #000;border-radius:3px;display:flex;justify-content:space-between}.tox .accessibility-issue__description>div{padding-bottom:4px}.tox .accessibility-issue__description>div>div{align-items:center;display:flex;margin-bottom:4px}.tox .accessibility-issue__description>:last-child:not(:only-child){border-color:#000;border-style:solid}.tox .accessibility-issue__repair{margin-top:16px}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description{background-color:rgba(32,122,183,.5);border-color:#207ab7;color:#fff}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description>:last-child{border-color:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--info a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description{background-color:rgba(255,165,0,.5);border-color:rgba(255,165,0,.8);color:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description>:last-child{border-color:rgba(255,165,0,.8)}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description{background-color:rgba(204,0,0,.5);border-color:rgba(204,0,0,.8);color:#fff}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description>:last-child{border-color:rgba(204,0,0,.8)}.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--error a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description{background-color:rgba(120,171,70,.5);border-color:rgba(120,171,70,.8);color:#fff}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description>:last-child{border-color:rgba(120,171,70,.8)}.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--success a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue__header h1,.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2{margin-top:0}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-left:4px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-left:auto}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description{padding:4px 4px 4px 8px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description>:last-child{border-left-width:1px;padding-left:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-right:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-right:auto}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description{padding:4px 8px 4px 4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description>:last-child{border-right-width:1px;padding-right:4px}.tox .tox-anchorbar{display:flex;flex:0 0 auto}.tox .tox-bar{display:flex;flex:0 0 auto}.tox .tox-button{background-color:#207ab7;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#207ab7;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;line-height:24px;margin:0;outline:0;padding:4px 16px;text-align:center;text-decoration:none;text-transform:none;white-space:nowrap}.tox .tox-button[disabled]{background-color:#207ab7;background-image:none;border-color:#207ab7;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-button:focus:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:hover:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:active:not(:disabled){background-color:#185d8c;background-image:none;border-color:#185d8c;box-shadow:none;color:#fff}.tox .tox-button--secondary{background-color:#3d546f;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#3d546f;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;color:#fff;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;outline:0;padding:4px 16px;text-decoration:none;text-transform:none}.tox .tox-button--secondary[disabled]{background-color:#3d546f;background-image:none;border-color:#3d546f;box-shadow:none;color:rgba(255,255,255,.5)}.tox .tox-button--secondary:focus:not(:disabled){background-color:#34485f;background-image:none;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--secondary:hover:not(:disabled){background-color:#34485f;background-image:none;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--secondary:active:not(:disabled){background-color:#2b3b4e;background-image:none;border-color:#2b3b4e;box-shadow:none;color:#fff}.tox .tox-button--icon,.tox .tox-button.tox-button--icon,.tox .tox-button.tox-button--secondary.tox-button--icon{padding:4px}.tox .tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg{display:block;fill:currentColor}.tox .tox-button-link{background:0;border:none;box-sizing:border-box;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;white-space:nowrap}.tox .tox-button-link--sm{font-size:14px}.tox .tox-button--naked{background-color:transparent;border-color:transparent;box-shadow:unset;color:#fff}.tox .tox-button--naked[disabled]{background-color:#3d546f;border-color:#3d546f;box-shadow:none;color:rgba(255,255,255,.5)}.tox .tox-button--naked:hover:not(:disabled){background-color:#34485f;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--naked:focus:not(:disabled){background-color:#34485f;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--naked:active:not(:disabled){background-color:#2b3b4e;border-color:#2b3b4e;box-shadow:none;color:#fff}.tox .tox-button--naked .tox-icon svg{fill:currentColor}.tox .tox-button--naked.tox-button--icon:hover:not(:disabled){color:#fff}.tox .tox-checkbox{align-items:center;border-radius:3px;cursor:pointer;display:flex;height:36px;min-width:36px}.tox .tox-checkbox__input{height:1px;overflow:hidden;position:absolute;top:auto;width:1px}.tox .tox-checkbox__icons{align-items:center;border-radius:3px;box-shadow:0 0 0 2px transparent;box-sizing:content-box;display:flex;height:24px;justify-content:center;padding:calc(4px - 1px);width:24px}.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:block;fill:rgba(255,255,255,.2)}.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:none;fill:#207ab7}.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg{display:none;fill:#207ab7}.tox .tox-checkbox--disabled{color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg{fill:rgba(255,255,255,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{fill:rgba(255,255,255,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{fill:rgba(255,255,255,.5)}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__checked svg{display:block}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:block}.tox input.tox-checkbox__input:focus+.tox-checkbox__icons{border-radius:3px;box-shadow:inset 0 0 0 1px #207ab7;padding:calc(4px - 1px)}.tox:not([dir=rtl]) .tox-checkbox__label{margin-left:4px}.tox:not([dir=rtl]) .tox-checkbox__input{left:-10000px}.tox:not([dir=rtl]) .tox-bar .tox-checkbox{margin-left:4px}.tox[dir=rtl] .tox-checkbox__label{margin-right:4px}.tox[dir=rtl] .tox-checkbox__input{right:-10000px}.tox[dir=rtl] .tox-bar .tox-checkbox{margin-right:4px}.tox .tox-collection--toolbar .tox-collection__group{display:flex;padding:0}.tox .tox-collection--grid .tox-collection__group{display:flex;flex-wrap:wrap;max-height:208px;overflow-x:hidden;overflow-y:auto;padding:0}.tox .tox-collection--list .tox-collection__group{border-bottom-width:0;border-color:#1a1a1a;border-left-width:0;border-right-width:0;border-style:solid;border-top-width:1px;padding:4px 0}.tox .tox-collection--list .tox-collection__group:first-child{border-top-width:0}.tox .tox-collection__group-heading{background-color:#333;color:#fff;cursor:default;font-size:12px;font-style:normal;font-weight:400;margin-bottom:4px;margin-top:-4px;padding:4px 8px;text-transform:none;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection__item{align-items:center;color:#fff;cursor:pointer;display:flex;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection--list .tox-collection__item{padding:4px 8px}.tox .tox-collection--toolbar .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--grid .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--list .tox-collection__item--enabled{background-color:#2b3b4e;color:#fff}.tox .tox-collection--list .tox-collection__item--active{background-color:#4a5562}.tox .tox-collection--toolbar .tox-collection__item--enabled{background-color:#757d87;color:#fff}.tox .tox-collection--toolbar .tox-collection__item--active{background-color:#4a5562}.tox .tox-collection--grid .tox-collection__item--enabled{background-color:#757d87;color:#fff}.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled){background-color:#4a5562;color:#fff}.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#fff}.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#fff}.tox .tox-collection__item-checkmark,.tox .tox-collection__item-icon{align-items:center;display:flex;height:24px;justify-content:center;width:24px}.tox .tox-collection__item-checkmark svg,.tox .tox-collection__item-icon svg{fill:currentColor}.tox .tox-collection--toolbar-lg .tox-collection__item-icon{height:48px;width:48px}.tox .tox-collection__item-label{color:currentColor;display:inline-block;flex:1;-ms-flex-preferred-size:auto;font-size:14px;font-style:normal;font-weight:400;line-height:24px;text-transform:none;word-break:break-all}.tox .tox-collection__item-accessory{color:rgba(255,255,255,.5);display:inline-block;font-size:14px;height:24px;line-height:24px;text-transform:none}.tox .tox-collection__item-caret{align-items:center;display:flex;min-height:24px}.tox .tox-collection__item-caret::after{content:'';font-size:0;min-height:inherit}.tox .tox-collection__item-caret svg{fill:#fff}.tox .tox-collection__item--state-disabled{background-color:transparent;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-collection__item--state-disabled .tox-collection__item-caret svg{fill:rgba(255,255,255,.5)}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg{display:none}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory+.tox-collection__item-checkmark{display:none}.tox .tox-collection--horizontal{background-color:#2b3b4e;border:1px solid #1a1a1a;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:nowrap;margin-bottom:0;overflow-x:auto;padding:0}.tox .tox-collection--horizontal .tox-collection__group{align-items:center;display:flex;flex-wrap:nowrap;margin:0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item{height:34px;margin:2px 0 3px 0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item-label{white-space:nowrap}.tox .tox-collection--horizontal .tox-collection__item-caret{margin-left:4px}.tox .tox-collection__item-container{display:flex}.tox .tox-collection__item-container--row{align-items:center;flex:1 1 auto;flex-direction:row}.tox .tox-collection__item-container--row.tox-collection__item-container--align-left{margin-right:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--align-right{justify-content:flex-end;margin-left:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top{align-items:flex-start;margin-bottom:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle{align-items:center}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom{align-items:flex-end;margin-top:auto}.tox .tox-collection__item-container--column{-ms-grid-row-align:center;align-self:center;flex:1 1 auto;flex-direction:column}.tox .tox-collection__item-container--column.tox-collection__item-container--align-left{align-items:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--align-right{align-items:flex-end}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top{align-self:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle{-ms-grid-row-align:center;align-self:center}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom{align-self:flex-end}.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-right:1px solid #000}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>:not(:first-child){margin-left:8px}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-left:4px}.tox:not([dir=rtl]) .tox-collection__item-accessory{margin-left:16px;text-align:right}.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret{margin-left:16px}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-left:1px solid #000}.tox[dir=rtl] .tox-collection--list .tox-collection__item>:not(:first-child){margin-right:8px}.tox[dir=rtl] .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-right:4px}.tox[dir=rtl] .tox-collection__item-accessory{margin-right:16px;text-align:left}.tox[dir=rtl] .tox-collection .tox-collection__item-caret{margin-right:16px;transform:rotateY(180deg)}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret{margin-right:4px}.tox .tox-color-picker-container{display:flex;flex-direction:row;height:225px;margin:0}.tox .tox-sv-palette{box-sizing:border-box;display:flex;height:100%}.tox .tox-sv-palette-spectrum{height:100%}.tox .tox-sv-palette,.tox .tox-sv-palette-spectrum{width:225px}.tox .tox-sv-palette-thumb{background:0 0;border:1px solid #000;border-radius:50%;box-sizing:content-box;height:12px;position:absolute;width:12px}.tox .tox-sv-palette-inner-thumb{border:1px solid #fff;border-radius:50%;height:10px;position:absolute;width:10px}.tox .tox-hue-slider{box-sizing:border-box;height:100%;width:25px}.tox .tox-hue-slider-spectrum{background:linear-gradient(to bottom,red,#ff0080,#f0f,#8000ff,#00f,#0080ff,#0ff,#00ff80,#0f0,#80ff00,#ff0,#ff8000,red);height:100%;width:100%}.tox .tox-hue-slider,.tox .tox-hue-slider-spectrum{width:20px}.tox .tox-hue-slider-thumb{background:#fff;border:1px solid #000;box-sizing:content-box;height:4px;width:100%}.tox .tox-rgb-form{display:flex;flex-direction:column;justify-content:space-between}.tox .tox-rgb-form div{align-items:center;display:flex;justify-content:space-between;margin-bottom:5px;width:inherit}.tox .tox-rgb-form input{width:6em}.tox .tox-rgb-form input.tox-invalid{border:1px solid red!important}.tox .tox-rgb-form .tox-rgba-preview{border:1px solid #000;flex-grow:2;margin-bottom:0}.tox:not([dir=rtl]) .tox-sv-palette{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider-thumb{margin-left:-1px}.tox:not([dir=rtl]) .tox-rgb-form label{margin-right:.5em}.tox[dir=rtl] .tox-sv-palette{margin-left:15px}.tox[dir=rtl] .tox-hue-slider{margin-left:15px}.tox[dir=rtl] .tox-hue-slider-thumb{margin-right:-1px}.tox[dir=rtl] .tox-rgb-form label{margin-left:.5em}.tox .tox-toolbar .tox-swatches,.tox .tox-toolbar__overflow .tox-swatches,.tox .tox-toolbar__primary .tox-swatches{margin:2px 0 3px 4px}.tox .tox-collection--list .tox-collection__group .tox-swatches-menu{border:0;margin:-4px 0}.tox .tox-swatches__row{display:flex}.tox .tox-swatch{height:30px;transition:transform .15s,box-shadow .15s;width:30px}.tox .tox-swatch:focus,.tox .tox-swatch:hover{box-shadow:0 0 0 1px rgba(127,127,127,.3) inset;transform:scale(.8)}.tox .tox-swatch--remove{align-items:center;display:flex;justify-content:center}.tox .tox-swatch--remove svg path{stroke:#e74c3c}.tox .tox-swatches__picker-btn{align-items:center;background-color:transparent;border:0;cursor:pointer;display:flex;height:30px;justify-content:center;outline:0;padding:0;width:30px}.tox .tox-swatches__picker-btn svg{height:24px;width:24px}.tox .tox-swatches__picker-btn:hover{background:#4a5562}.tox:not([dir=rtl]) .tox-swatches__picker-btn{margin-left:auto}.tox[dir=rtl] .tox-swatches__picker-btn{margin-right:auto}.tox .tox-comment-thread{background:#2b3b4e;position:relative}.tox .tox-comment-thread>:not(:first-child){margin-top:8px}.tox .tox-comment{background:#2b3b4e;border:1px solid #000;border-radius:3px;box-shadow:0 4px 8px 0 rgba(42,55,70,.1);padding:8px 8px 16px 8px;position:relative}.tox .tox-comment__header{align-items:center;color:#fff;display:flex;justify-content:space-between}.tox .tox-comment__date{color:rgba(255,255,255,.5);font-size:12px}.tox .tox-comment__body{color:#fff;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;margin-top:8px;position:relative;text-transform:initial}.tox .tox-comment__body textarea{resize:none;white-space:normal;width:100%}.tox .tox-comment__expander{padding-top:8px}.tox .tox-comment__expander p{color:rgba(255,255,255,.5);font-size:14px;font-style:normal}.tox .tox-comment__body p{margin:0}.tox .tox-comment__buttonspacing{padding-top:16px;text-align:center}.tox .tox-comment-thread__overlay::after{background:#2b3b4e;bottom:0;content:"";display:flex;left:0;opacity:.9;position:absolute;right:0;top:0;z-index:5}.tox .tox-comment__reply{display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end;margin-top:8px}.tox .tox-comment__reply>:first-child{margin-bottom:8px;width:100%}.tox .tox-comment__edit{display:flex;flex-wrap:wrap;justify-content:flex-end;margin-top:16px}.tox .tox-comment__gradient::after{background:linear-gradient(rgba(43,59,78,0),#2b3b4e);bottom:0;content:"";display:block;height:5em;margin-top:-40px;position:absolute;width:100%}.tox .tox-comment__overlay{background:#2b3b4e;bottom:0;display:flex;flex-direction:column;flex-grow:1;left:0;opacity:.9;position:absolute;right:0;text-align:center;top:0;z-index:5}.tox .tox-comment__loading-text{align-items:center;color:#fff;display:flex;flex-direction:column;position:relative}.tox .tox-comment__loading-text>div{padding-bottom:16px}.tox .tox-comment__overlaytext{bottom:0;flex-direction:column;font-size:14px;left:0;padding:1em;position:absolute;right:0;top:0;z-index:10}.tox .tox-comment__overlaytext p{background-color:#2b3b4e;box-shadow:0 0 8px 8px #2b3b4e;color:#fff;text-align:center}.tox .tox-comment__overlaytext div:nth-of-type(2){font-size:.8em}.tox .tox-comment__busy-spinner{align-items:center;background-color:#2b3b4e;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:20}.tox .tox-comment__scroll{display:flex;flex-direction:column;flex-shrink:1;overflow:auto}.tox .tox-conversations{margin:8px}.tox:not([dir=rtl]) .tox-comment__edit{margin-left:8px}.tox:not([dir=rtl]) .tox-comment__buttonspacing>:last-child,.tox:not([dir=rtl]) .tox-comment__edit>:last-child,.tox:not([dir=rtl]) .tox-comment__reply>:last-child{margin-left:8px}.tox[dir=rtl] .tox-comment__edit{margin-right:8px}.tox[dir=rtl] .tox-comment__buttonspacing>:last-child,.tox[dir=rtl] .tox-comment__edit>:last-child,.tox[dir=rtl] .tox-comment__reply>:last-child{margin-right:8px}.tox .tox-user{align-items:center;display:flex}.tox .tox-user__avatar svg{fill:rgba(255,255,255,.5)}.tox .tox-user__name{color:rgba(255,255,255,.5);font-size:12px;font-style:normal;font-weight:700;text-transform:uppercase}.tox:not([dir=rtl]) .tox-user__avatar svg{margin-right:8px}.tox:not([dir=rtl]) .tox-user__avatar+.tox-user__name{margin-left:8px}.tox[dir=rtl] .tox-user__avatar svg{margin-left:8px}.tox[dir=rtl] .tox-user__avatar+.tox-user__name{margin-right:8px}.tox .tox-dialog-wrap{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:fixed;right:0;top:0;z-index:1100}.tox .tox-dialog-wrap__backdrop{background-color:rgba(34,47,62,.75);bottom:0;left:0;position:absolute;right:0;top:0;z-index:1}.tox .tox-dialog-wrap__backdrop--opaque{background-color:#222f3e}.tox .tox-dialog{background-color:#2b3b4e;border-color:#000;border-radius:3px;border-style:solid;border-width:1px;box-shadow:0 16px 16px -10px rgba(42,55,70,.15),0 0 40px 1px rgba(42,55,70,.15);display:flex;flex-direction:column;max-height:100%;max-width:480px;overflow:hidden;position:relative;width:95vw;z-index:2}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog{align-self:flex-start;margin:8px auto;width:calc(100vw - 16px)}}.tox .tox-dialog-inline{z-index:1100}.tox .tox-dialog__header{align-items:center;background-color:#2b3b4e;border-bottom:none;color:#fff;display:flex;font-size:16px;justify-content:space-between;padding:8px 16px 0 16px;position:relative}.tox .tox-dialog__header .tox-button{z-index:1}.tox .tox-dialog__draghandle{cursor:grab;height:100%;left:0;position:absolute;top:0;width:100%}.tox .tox-dialog__draghandle:active{cursor:grabbing}.tox .tox-dialog__dismiss{margin-left:auto}.tox .tox-dialog__title{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1.3;margin:0;text-transform:none}.tox .tox-dialog__body{color:#fff;display:flex;flex:1;-ms-flex-preferred-size:auto;font-size:16px;font-style:normal;font-weight:400;line-height:1.3;min-width:0;text-align:left;text-transform:none}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body{flex-direction:column}}.tox .tox-dialog__body-nav{align-items:flex-start;display:flex;flex-direction:column;padding:16px 16px}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body-nav{flex-direction:row;-webkit-overflow-scrolling:touch;overflow-x:auto;padding-bottom:0}}.tox .tox-dialog__body-nav-item{border-bottom:2px solid transparent;color:rgba(255,255,255,.5);display:inline-block;font-size:14px;line-height:1.3;margin-bottom:8px;text-decoration:none;white-space:nowrap}.tox .tox-dialog__body-nav-item:focus{background-color:rgba(32,122,183,.1)}.tox .tox-dialog__body-nav-item--active{border-bottom:2px solid #207ab7;color:#207ab7}.tox .tox-dialog__body-content{box-sizing:border-box;display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto;max-height:650px;overflow:auto;-webkit-overflow-scrolling:touch;padding:16px 16px}.tox .tox-dialog__body-content>*{margin-bottom:0;margin-top:16px}.tox .tox-dialog__body-content>:first-child{margin-top:0}.tox .tox-dialog__body-content>:last-child{margin-bottom:0}.tox .tox-dialog__body-content>:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog__body-content a{color:#207ab7;cursor:pointer;text-decoration:none}.tox .tox-dialog__body-content a:focus,.tox .tox-dialog__body-content a:hover{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content a:active{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content svg{fill:#fff}.tox .tox-dialog__body-content ul{display:block;list-style-type:disc;margin-bottom:16px;-webkit-margin-end:0;margin-inline-end:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem}.tox .tox-dialog__body-content .tox-form__group h1{color:#fff;font-size:20px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group h2{color:#fff;font-size:16px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group p{margin-bottom:16px}.tox .tox-dialog__body-content .tox-form__group h1:first-child,.tox .tox-dialog__body-content .tox-form__group h2:first-child,.tox .tox-dialog__body-content .tox-form__group p:first-child{margin-top:0}.tox .tox-dialog__body-content .tox-form__group h1:last-child,.tox .tox-dialog__body-content .tox-form__group h2:last-child,.tox .tox-dialog__body-content .tox-form__group p:last-child{margin-bottom:0}.tox .tox-dialog__body-content .tox-form__group h1:only-child,.tox .tox-dialog__body-content .tox-form__group h2:only-child,.tox .tox-dialog__body-content .tox-form__group p:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog--width-lg{height:650px;max-width:1200px}.tox .tox-dialog--width-md{max-width:800px}.tox .tox-dialog--width-md .tox-dialog__body-content{overflow:auto}.tox .tox-dialog__body-content--centered{text-align:center}.tox .tox-dialog__footer{align-items:center;background-color:#2b3b4e;border-top:1px solid #000;display:flex;justify-content:space-between;padding:8px 16px}.tox .tox-dialog__footer-end,.tox .tox-dialog__footer-start{display:flex}.tox .tox-dialog__busy-spinner{align-items:center;background-color:rgba(34,47,62,.75);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:3}.tox .tox-dialog__table{border-collapse:collapse;width:100%}.tox .tox-dialog__table thead th{font-weight:700;padding-bottom:8px}.tox .tox-dialog__table tbody tr{border-bottom:1px solid #000}.tox .tox-dialog__table tbody tr:last-child{border-bottom:none}.tox .tox-dialog__table td{padding-bottom:8px;padding-top:8px}.tox .tox-dialog__popups{position:absolute;width:100%;z-index:1100}.tox .tox-dialog__body-iframe{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox .tox-dialog-dock-fadeout{opacity:0;visibility:hidden}.tox .tox-dialog-dock-fadein{opacity:1;visibility:visible}.tox .tox-dialog-dock-transition{transition:visibility 0s linear .3s,opacity .3s ease}.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein{transition-delay:0s}.tox.tox-platform-ie .tox-dialog-wrap{position:-ms-device-fixed}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav{margin-right:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child){margin-left:8px}}.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end>*,.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start>*{margin-left:8px}.tox[dir=rtl] .tox-dialog__body{text-align:right}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav{margin-left:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child){margin-right:8px}}.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end>*,.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start>*{margin-right:8px}body.tox-dialog__disable-scroll{overflow:hidden}.tox .tox-dropzone-container{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dropzone{align-items:center;background:#fff;border:2px dashed #000;box-sizing:border-box;display:flex;flex-direction:column;flex-grow:1;justify-content:center;min-height:100px;padding:10px}.tox .tox-dropzone p{color:rgba(255,255,255,.5);margin:0 0 16px 0}.tox .tox-edit-area{display:flex;flex:1;-ms-flex-preferred-size:auto;overflow:hidden;position:relative}.tox .tox-edit-area__iframe{background-color:#fff;border:0;box-sizing:border-box;flex:1;-ms-flex-preferred-size:auto;height:100%;position:absolute;width:100%}.tox.tox-inline-edit-area{border:1px dotted #000}.tox .tox-editor-container{display:flex;flex:1 1 auto;flex-direction:column;overflow:hidden}.tox .tox-editor-header{z-index:1}.tox:not(.tox-tinymce-inline) .tox-editor-header{box-shadow:none;transition:box-shadow .5s}.tox.tox-tinymce--toolbar-bottom .tox-editor-header,.tox.tox-tinymce-inline .tox-editor-header{margin-bottom:-1px}.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header{background-color:transparent;box-shadow:0 4px 4px -3px rgba(0,0,0,.25)}.tox-editor-dock-fadeout{opacity:0;visibility:hidden}.tox-editor-dock-fadein{opacity:1;visibility:visible}.tox-editor-dock-transition{transition:visibility 0s linear .25s,opacity .25s ease}.tox-editor-dock-transition.tox-editor-dock-fadein{transition-delay:0s}.tox .tox-control-wrap{flex:1;position:relative}.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid{display:none}.tox .tox-control-wrap svg{display:block}.tox .tox-control-wrap__status-icon-wrap{position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-control-wrap__status-icon-invalid svg{fill:#c00}.tox .tox-control-wrap__status-icon-unknown svg{fill:orange}.tox .tox-control-wrap__status-icon-valid svg{fill:green}.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield{padding-right:32px}.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap{right:4px}.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield{padding-left:32px}.tox[dir=rtl] .tox-control-wrap__status-icon-wrap{left:4px}.tox .tox-autocompleter{max-width:25em}.tox .tox-autocompleter .tox-menu{max-width:25em}.tox .tox-autocompleter .tox-autocompleter-highlight{font-weight:700}.tox .tox-color-input{display:flex;position:relative;z-index:1}.tox .tox-color-input .tox-textfield{z-index:-1}.tox .tox-color-input span{border-color:rgba(42,55,70,.2);border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;height:24px;position:absolute;top:6px;width:24px}.tox .tox-color-input span:focus:not([aria-disabled=true]),.tox .tox-color-input span:hover:not([aria-disabled=true]){border-color:#207ab7;cursor:pointer}.tox .tox-color-input span::before{background-image:linear-gradient(45deg,rgba(255,255,255,.25) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.25) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.25) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.25) 75%);background-position:0 0,0 6px,6px -6px,-6px 0;background-size:12px 12px;border:1px solid #2b3b4e;border-radius:3px;box-sizing:border-box;content:'';height:24px;left:-1px;position:absolute;top:-1px;width:24px;z-index:-1}.tox .tox-color-input span[aria-disabled=true]{cursor:not-allowed}.tox:not([dir=rtl]) .tox-color-input .tox-textfield{padding-left:36px}.tox:not([dir=rtl]) .tox-color-input span{left:6px}.tox[dir=rtl] .tox-color-input .tox-textfield{padding-right:36px}.tox[dir=rtl] .tox-color-input span{right:6px}.tox .tox-label,.tox .tox-toolbar-label{color:rgba(255,255,255,.5);display:block;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;padding:0 8px 0 0;text-transform:none;white-space:nowrap}.tox .tox-toolbar-label{padding:0 8px}.tox[dir=rtl] .tox-label{padding:0 0 0 8px}.tox .tox-form{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group{box-sizing:border-box;margin-bottom:4px}.tox .tox-form-group--maximize{flex:1}.tox .tox-form__group--error{color:#c00}.tox .tox-form__group--collection{display:flex}.tox .tox-form__grid{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between}.tox .tox-form__grid--2col>.tox-form__group{width:calc(50% - (8px / 2))}.tox .tox-form__grid--3col>.tox-form__group{width:calc(100% / 3 - (8px / 2))}.tox .tox-form__grid--4col>.tox-form__group{width:calc(25% - (8px / 2))}.tox .tox-form__controls-h-stack{align-items:center;display:flex}.tox .tox-form__group--inline{align-items:center;display:flex}.tox .tox-form__group--stretched{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-textarea{flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox:not([dir=rtl]) .tox-form__controls-h-stack>:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-form__controls-h-stack>:not(:first-child){margin-right:4px}.tox .tox-lock.tox-locked .tox-lock-icon__unlock,.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock{display:none}.tox .tox-listboxfield .tox-listbox--select,.tox .tox-textarea,.tox .tox-textfield,.tox .tox-toolbar-textfield{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#2b3b4e;border-color:#000;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-textarea[disabled],.tox .tox-textfield[disabled]{background-color:#222f3e;color:rgba(255,255,255,.85);cursor:not-allowed}.tox .tox-listboxfield .tox-listbox--select:focus,.tox .tox-textarea:focus,.tox .tox-textfield:focus{background-color:#2b3b4e;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-toolbar-textfield{border-width:0;margin-bottom:3px;margin-top:2px;max-width:250px}.tox .tox-naked-btn{background-color:transparent;border:0;border-color:transparent;box-shadow:unset;color:#207ab7;cursor:pointer;display:block;margin:0;padding:0}.tox .tox-naked-btn svg{display:block;fill:#fff}.tox:not([dir=rtl]) .tox-toolbar-textfield+*{margin-left:4px}.tox[dir=rtl] .tox-toolbar-textfield+*{margin-right:4px}.tox .tox-listboxfield{cursor:pointer;position:relative}.tox .tox-listboxfield .tox-listbox--select[disabled]{background-color:#19232e;color:rgba(255,255,255,.85);cursor:not-allowed}.tox .tox-listbox__select-label{cursor:default;flex:1;margin:0 4px}.tox .tox-listbox__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-listbox__select-chevron svg{fill:#fff}.tox .tox-listboxfield .tox-listbox--select{align-items:center;display:flex}.tox:not([dir=rtl]) .tox-listboxfield svg{right:8px}.tox[dir=rtl] .tox-listboxfield svg{left:8px}.tox .tox-selectfield{cursor:pointer;position:relative}.tox .tox-selectfield select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#2b3b4e;border-color:#000;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-selectfield select[disabled]{background-color:#19232e;color:rgba(255,255,255,.85);cursor:not-allowed}.tox .tox-selectfield select::-ms-expand{display:none}.tox .tox-selectfield select:focus{background-color:#2b3b4e;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-selectfield svg{pointer-events:none;position:absolute;top:50%;transform:translateY(-50%)}.tox:not([dir=rtl]) .tox-selectfield select[size="0"],.tox:not([dir=rtl]) .tox-selectfield select[size="1"]{padding-right:24px}.tox:not([dir=rtl]) .tox-selectfield svg{right:8px}.tox[dir=rtl] .tox-selectfield select[size="0"],.tox[dir=rtl] .tox-selectfield select[size="1"]{padding-left:24px}.tox[dir=rtl] .tox-selectfield svg{left:8px}.tox .tox-textarea{-webkit-appearance:textarea;-moz-appearance:textarea;appearance:textarea;white-space:pre-wrap}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}.tox .tox-help__more-link{list-style:none;margin-top:1em}.tox .tox-image-tools{width:100%}.tox .tox-image-tools__toolbar{align-items:center;display:flex;justify-content:center}.tox .tox-image-tools__image{background-color:#666;height:380px;overflow:auto;position:relative;width:100%}.tox .tox-image-tools__image,.tox .tox-image-tools__image+.tox-image-tools__toolbar{margin-top:8px}.tox .tox-image-tools__image-bg{background:url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==)}.tox .tox-image-tools__toolbar>.tox-spacer{flex:1;-ms-flex-preferred-size:auto}.tox .tox-croprect-block{background:#000;opacity:.5;position:absolute;zoom:1}.tox .tox-croprect-handle{border:2px solid #fff;height:20px;left:0;position:absolute;top:0;width:20px}.tox .tox-croprect-handle-move{border:0;cursor:move;position:absolute}.tox .tox-croprect-handle-nw{border-width:2px 0 0 2px;cursor:nw-resize;left:100px;margin:-2px 0 0 -2px;top:100px}.tox .tox-croprect-handle-ne{border-width:2px 2px 0 0;cursor:ne-resize;left:200px;margin:-2px 0 0 -20px;top:100px}.tox .tox-croprect-handle-sw{border-width:0 0 2px 2px;cursor:sw-resize;left:100px;margin:-20px 2px 0 -2px;top:200px}.tox .tox-croprect-handle-se{border-width:0 2px 2px 0;cursor:se-resize;left:200px;margin:-20px 0 0 -20px;top:200px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-left:8px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-left:32px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-left:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-right:8px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-right:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-right:32px}.tox .tox-insert-table-picker{display:flex;flex-wrap:wrap;width:170px}.tox .tox-insert-table-picker>div{border-color:#000;border-style:solid;border-width:0 1px 1px 0;box-sizing:border-box;height:17px;width:17px}.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker{margin:-4px 0}.tox .tox-insert-table-picker .tox-insert-table-picker__selected{background-color:rgba(32,122,183,.5);border-color:rgba(32,122,183,.5)}.tox .tox-insert-table-picker__label{color:#fff;display:block;font-size:14px;padding:4px;text-align:center;width:100%}.tox:not([dir=rtl]) .tox-insert-table-picker>div:nth-child(10n){border-right:0}.tox[dir=rtl] .tox-insert-table-picker>div:nth-child(10n+1){border-right:0}.tox .tox-menu{background-color:#2b3b4e;border:1px solid #000;border-radius:3px;box-shadow:0 4px 8px 0 rgba(42,55,70,.1);display:inline-block;overflow:hidden;vertical-align:top;z-index:1150}.tox .tox-menu.tox-collection.tox-collection--list{padding:0}.tox .tox-menu.tox-collection.tox-collection--toolbar{padding:4px}.tox .tox-menu.tox-collection.tox-collection--grid{padding:4px}.tox .tox-menu__label blockquote,.tox .tox-menu__label code,.tox .tox-menu__label h1,.tox .tox-menu__label h2,.tox .tox-menu__label h3,.tox .tox-menu__label h4,.tox .tox-menu__label h5,.tox .tox-menu__label h6,.tox .tox-menu__label p{margin:0}.tox .tox-menubar{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E") left 0 top 0 #222f3e;background-color:#222f3e;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 4px 0 4px}.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar{border-top:1px solid #000}.tox .tox-mbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#fff;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0 4px;text-transform:none;width:auto}.tox .tox-mbtn[disabled]{background-color:transparent;border:0;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-mbtn:focus:not(:disabled){background:#4a5562;border:0;box-shadow:none;color:#fff}.tox .tox-mbtn--active{background:#757d87;border:0;box-shadow:none;color:#fff}.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active){background:#4a5562;border:0;box-shadow:none;color:#fff}.tox .tox-mbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-mbtn[disabled] .tox-mbtn__select-label{cursor:not-allowed}.tox .tox-mbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px;display:none}.tox .tox-notification{border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;display:-ms-grid;display:grid;font-size:14px;font-weight:400;-ms-grid-columns:minmax(40px,1fr) auto minmax(40px,1fr);grid-template-columns:minmax(40px,1fr) auto minmax(40px,1fr);margin-top:4px;opacity:0;padding:4px;transition:transform .1s ease-in,opacity 150ms ease-in}.tox .tox-notification p{font-size:14px;font-weight:400}.tox .tox-notification a{cursor:pointer;text-decoration:underline}.tox .tox-notification--in{opacity:1}.tox .tox-notification--success{background-color:#e4eeda;border-color:#d7e6c8;color:#fff}.tox .tox-notification--success p{color:#fff}.tox .tox-notification--success a{color:#547831}.tox .tox-notification--success svg{fill:#fff}.tox .tox-notification--error{background-color:#f8dede;border-color:#f2bfbf;color:#fff}.tox .tox-notification--error p{color:#fff}.tox .tox-notification--error a{color:#c00}.tox .tox-notification--error svg{fill:#fff}.tox .tox-notification--warn,.tox .tox-notification--warning{background-color:#fffaea;border-color:#ffe89d;color:#fff}.tox .tox-notification--warn p,.tox .tox-notification--warning p{color:#fff}.tox .tox-notification--warn a,.tox .tox-notification--warning a{color:#fff}.tox .tox-notification--warn svg,.tox .tox-notification--warning svg{fill:#fff}.tox .tox-notification--info{background-color:#d9edf7;border-color:#779ecb;color:#fff}.tox .tox-notification--info p{color:#fff}.tox .tox-notification--info a{color:#fff}.tox .tox-notification--info svg{fill:#fff}.tox .tox-notification__body{-ms-grid-row-align:center;align-self:center;color:#fff;font-size:14px;-ms-grid-column-span:1;grid-column-end:3;-ms-grid-column:2;grid-column-start:2;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;text-align:center;white-space:normal;word-break:break-all;word-break:break-word}.tox .tox-notification__body>*{margin:0}.tox .tox-notification__body>*+*{margin-top:1rem}.tox .tox-notification__icon{-ms-grid-row-align:center;align-self:center;-ms-grid-column-span:1;grid-column-end:2;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification__icon svg{display:block}.tox .tox-notification__dismiss{-ms-grid-row-align:start;align-self:start;-ms-grid-column-span:1;grid-column-end:4;-ms-grid-column:3;grid-column-start:3;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification .tox-progress-bar{-ms-grid-column-span:3;grid-column-end:4;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:3;-ms-grid-row:2;grid-row-start:2;-ms-grid-column-align:center;justify-self:center}.tox .tox-pop{display:inline-block;position:relative}.tox .tox-pop--resizing{transition:width .1s ease}.tox .tox-pop--resizing .tox-toolbar,.tox .tox-pop--resizing .tox-toolbar__group{flex-wrap:nowrap}.tox .tox-pop--transition{transition:.15s ease;transition-property:left,right,top,bottom}.tox .tox-pop--transition::after,.tox .tox-pop--transition::before{transition:all .15s,visibility 0s,opacity 75ms ease 75ms}.tox .tox-pop__dialog{background-color:#222f3e;border:1px solid #000;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);min-width:0;overflow:hidden}.tox .tox-pop__dialog>:not(.tox-toolbar){margin:4px 4px 4px 8px}.tox .tox-pop__dialog .tox-toolbar{background-color:transparent;margin-bottom:-1px}.tox .tox-pop::after,.tox .tox-pop::before{border-style:solid;content:'';display:block;height:0;opacity:1;position:absolute;width:0}.tox .tox-pop.tox-pop--inset::after,.tox .tox-pop.tox-pop--inset::before{opacity:0;transition:all 0s .15s,visibility 0s,opacity 75ms ease}.tox .tox-pop.tox-pop--bottom::after,.tox .tox-pop.tox-pop--bottom::before{left:50%;top:100%}.tox .tox-pop.tox-pop--bottom::after{border-color:#222f3e transparent transparent transparent;border-width:8px;margin-left:-8px;margin-top:-1px}.tox .tox-pop.tox-pop--bottom::before{border-color:#000 transparent transparent transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--top::after,.tox .tox-pop.tox-pop--top::before{left:50%;top:0;transform:translateY(-100%)}.tox .tox-pop.tox-pop--top::after{border-color:transparent transparent #222f3e transparent;border-width:8px;margin-left:-8px;margin-top:1px}.tox .tox-pop.tox-pop--top::before{border-color:transparent transparent #000 transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--left::after,.tox .tox-pop.tox-pop--left::before{left:0;top:calc(50% - 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--left::after{border-color:transparent #222f3e transparent transparent;border-width:8px;margin-left:-15px}.tox .tox-pop.tox-pop--left::before{border-color:transparent #000 transparent transparent;border-width:10px;margin-left:-19px}.tox .tox-pop.tox-pop--right::after,.tox .tox-pop.tox-pop--right::before{left:100%;top:calc(50% + 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--right::after{border-color:transparent transparent transparent #222f3e;border-width:8px;margin-left:-1px}.tox .tox-pop.tox-pop--right::before{border-color:transparent transparent transparent #000;border-width:10px;margin-left:-1px}.tox .tox-pop.tox-pop--align-left::after,.tox .tox-pop.tox-pop--align-left::before{left:20px}.tox .tox-pop.tox-pop--align-right::after,.tox .tox-pop.tox-pop--align-right::before{left:calc(100% - 20px)}.tox .tox-sidebar-wrap{display:flex;flex-direction:row;flex-grow:1;-ms-flex-preferred-size:0;min-height:0}.tox .tox-sidebar{background-color:#222f3e;display:flex;flex-direction:row;justify-content:flex-end}.tox .tox-sidebar__slider{display:flex;overflow:hidden}.tox .tox-sidebar__pane-container{display:flex}.tox .tox-sidebar__pane{display:flex}.tox .tox-sidebar--sliding-closed{opacity:0}.tox .tox-sidebar--sliding-open{opacity:1}.tox .tox-sidebar--sliding-growing,.tox .tox-sidebar--sliding-shrinking{transition:width .5s ease,opacity .5s ease}.tox .tox-selector{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;display:inline-block;height:10px;position:absolute;width:10px}.tox.tox-platform-touch .tox-selector{height:12px;width:12px}.tox .tox-slider{align-items:center;display:flex;flex:1;-ms-flex-preferred-size:auto;height:24px;justify-content:center;position:relative}.tox .tox-slider__rail{background-color:transparent;border:1px solid #000;border-radius:3px;height:10px;min-width:120px;width:100%}.tox .tox-slider__handle{background-color:#207ab7;border:2px solid #185d8c;border-radius:3px;box-shadow:none;height:24px;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:14px}.tox .tox-source-code{overflow:auto}.tox .tox-spinner{display:flex}.tox .tox-spinner>div{animation:tam-bouncing-dots 1.5s ease-in-out 0s infinite both;background-color:rgba(255,255,255,.5);border-radius:100%;height:8px;width:8px}.tox .tox-spinner>div:nth-child(1){animation-delay:-.32s}.tox .tox-spinner>div:nth-child(2){animation-delay:-.16s}@keyframes tam-bouncing-dots{0%,100%,80%{transform:scale(0)}40%{transform:scale(1)}}.tox:not([dir=rtl]) .tox-spinner>div:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-spinner>div:not(:first-child){margin-right:4px}.tox .tox-statusbar{align-items:center;background-color:#222f3e;border-top:1px solid #000;color:#fff;display:flex;flex:0 0 auto;font-size:12px;font-weight:400;height:18px;overflow:hidden;padding:0 8px;position:relative;text-transform:uppercase}.tox .tox-statusbar__text-container{display:flex;flex:1 1 auto;justify-content:flex-end;overflow:hidden}.tox .tox-statusbar__path{display:flex;flex:1 1 auto;margin-right:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tox .tox-statusbar__path>*{display:inline;white-space:nowrap}.tox .tox-statusbar__wordcount{flex:0 0 auto;margin-left:1ch}.tox .tox-statusbar a,.tox .tox-statusbar__path-item,.tox .tox-statusbar__wordcount{color:#fff;text-decoration:none}.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]){cursor:pointer;text-decoration:underline}.tox .tox-statusbar__resize-handle{align-items:flex-end;align-self:stretch;cursor:nwse-resize;display:flex;flex:0 0 auto;justify-content:flex-end;margin-left:auto;margin-right:-8px;padding-left:1ch}.tox .tox-statusbar__resize-handle svg{display:block;fill:#fff}.tox .tox-statusbar__resize-handle:focus svg{background-color:#4a5562;border-radius:1px;box-shadow:0 0 0 2px #4a5562}.tox:not([dir=rtl]) .tox-statusbar__path>*{margin-right:4px}.tox:not([dir=rtl]) .tox-statusbar__branding{margin-left:1ch}.tox[dir=rtl] .tox-statusbar{flex-direction:row-reverse}.tox[dir=rtl] .tox-statusbar__path>*{margin-left:4px}.tox .tox-throbber{z-index:1299}.tox .tox-throbber__busy-spinner{align-items:center;background-color:rgba(34,47,62,.6);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}.tox .tox-tbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#fff;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0;text-transform:none;width:34px}.tox .tox-tbtn svg{display:block;fill:#fff}.tox .tox-tbtn.tox-tbtn-more{padding-left:5px;padding-right:5px;width:inherit}.tox .tox-tbtn:focus{background:#4a5562;border:0;box-shadow:none}.tox .tox-tbtn:hover{background:#4a5562;border:0;box-shadow:none;color:#fff}.tox .tox-tbtn:hover svg{fill:#fff}.tox .tox-tbtn:active{background:#757d87;border:0;box-shadow:none;color:#fff}.tox .tox-tbtn:active svg{fill:#fff}.tox .tox-tbtn--disabled,.tox .tox-tbtn--disabled:hover,.tox .tox-tbtn:disabled,.tox .tox-tbtn:disabled:hover{background:0 0;border:0;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-tbtn--disabled svg,.tox .tox-tbtn--disabled:hover svg,.tox .tox-tbtn:disabled svg,.tox .tox-tbtn:disabled:hover svg{fill:rgba(255,255,255,.5)}.tox .tox-tbtn--enabled,.tox .tox-tbtn--enabled:hover{background:#757d87;border:0;box-shadow:none;color:#fff}.tox .tox-tbtn--enabled:hover>*,.tox .tox-tbtn--enabled>*{transform:none}.tox .tox-tbtn--enabled svg,.tox .tox-tbtn--enabled:hover svg{fill:#fff}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled){color:#fff}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg{fill:#fff}.tox .tox-tbtn:active>*{transform:none}.tox .tox-tbtn--md{height:51px;width:51px}.tox .tox-tbtn--lg{flex-direction:column;height:68px;width:68px}.tox .tox-tbtn--return{-ms-grid-row-align:stretch;align-self:stretch;height:unset;width:16px}.tox .tox-tbtn--labeled{padding:0 4px;width:unset}.tox .tox-tbtn__vlabel{display:block;font-size:10px;font-weight:400;letter-spacing:-.025em;margin-bottom:4px;white-space:nowrap}.tox .tox-tbtn--select{margin:2px 0 3px 0;padding:0 4px;width:auto}.tox .tox-tbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-tbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-tbtn__select-chevron svg{fill:rgba(255,255,255,.5)}.tox .tox-tbtn--bespoke .tox-tbtn__select-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:7em}.tox .tox-split-button{border:0;border-radius:3px;box-sizing:border-box;display:flex;margin:2px 0 3px 0;overflow:hidden}.tox .tox-split-button:hover{box-shadow:0 0 0 1px #4a5562 inset}.tox .tox-split-button:focus{background:#4a5562;box-shadow:none;color:#fff}.tox .tox-split-button>*{border-radius:0}.tox .tox-split-button__chevron{width:16px}.tox .tox-split-button__chevron svg{fill:rgba(255,255,255,.5)}.tox .tox-split-button .tox-tbtn{margin:0}.tox.tox-platform-touch .tox-split-button .tox-tbtn:first-child{width:30px}.tox.tox-platform-touch .tox-split-button__chevron{width:20px}.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus,.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,.tox .tox-split-button.tox-tbtn--disabled:focus,.tox .tox-split-button.tox-tbtn--disabled:hover{background:0 0;box-shadow:none;color:rgba(255,255,255,.5)}.tox .tox-toolbar-overlord{background-color:#222f3e}.tox .tox-toolbar,.tox .tox-toolbar__overflow,.tox .tox-toolbar__primary{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E") left 0 top 0 #222f3e;background-color:#222f3e;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 0}.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed{height:0;opacity:0;padding-bottom:0;padding-top:0;visibility:hidden}.tox .tox-toolbar__overflow--growing{transition:height .3s ease,opacity .2s linear .1s}.tox .tox-toolbar__overflow--shrinking{transition:opacity .3s ease,height .2s linear .1s,visibility 0s linear .3s}.tox .tox-menubar+.tox-toolbar,.tox .tox-menubar+.tox-toolbar-overlord .tox-toolbar__primary{border-top:1px solid #000;margin-top:-1px}.tox .tox-toolbar--scrolling{flex-wrap:nowrap;overflow-x:auto}.tox .tox-pop .tox-toolbar{border-width:0}.tox .tox-toolbar--no-divider{background-image:none}.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary,.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child{border-top:1px solid #000}.tox.tox-tinymce-aux .tox-toolbar__overflow{background-color:#222f3e;border:1px solid #000;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15)}.tox .tox-toolbar__group{align-items:center;display:flex;flex-wrap:wrap;margin:0 0;padding:0 4px 0 4px}.tox .tox-toolbar__group--pull-right{margin-left:auto}.tox .tox-toolbar--scrolling .tox-toolbar__group{flex-shrink:0;flex-wrap:nowrap}.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type){border-right:1px solid #000}.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type){border-left:1px solid #000}.tox .tox-tooltip{display:inline-block;padding:8px;position:relative}.tox .tox-tooltip__body{background-color:#3d546f;border-radius:3px;box-shadow:0 2px 4px rgba(42,55,70,.3);color:rgba(255,255,255,.75);font-size:14px;font-style:normal;font-weight:400;padding:4px 8px;text-transform:none}.tox .tox-tooltip__arrow{position:absolute}.tox .tox-tooltip--down .tox-tooltip__arrow{border-left:8px solid transparent;border-right:8px solid transparent;border-top:8px solid #3d546f;bottom:0;left:50%;position:absolute;transform:translateX(-50%)}.tox .tox-tooltip--up .tox-tooltip__arrow{border-bottom:8px solid #3d546f;border-left:8px solid transparent;border-right:8px solid transparent;left:50%;position:absolute;top:0;transform:translateX(-50%)}.tox .tox-tooltip--right .tox-tooltip__arrow{border-bottom:8px solid transparent;border-left:8px solid #3d546f;border-top:8px solid transparent;position:absolute;right:0;top:50%;transform:translateY(-50%)}.tox .tox-tooltip--left .tox-tooltip__arrow{border-bottom:8px solid transparent;border-right:8px solid #3d546f;border-top:8px solid transparent;left:0;position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-well{border:1px solid #000;border-radius:3px;padding:8px;width:100%}.tox .tox-well>:first-child{margin-top:0}.tox .tox-well>:last-child{margin-bottom:0}.tox .tox-well>:only-child{margin:0}.tox .tox-custom-editor{border:1px solid #000;border-radius:3px;display:flex;flex:1;position:relative}.tox .tox-dialog-loading::before{background-color:rgba(0,0,0,.5);content:"";height:100%;position:absolute;width:100%;z-index:1000}.tox .tox-tab{cursor:pointer}.tox .tox-dialog__content-js{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-content .tox-collection{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-image-tools-edit-panel{height:60px}.tox .tox-image-tools__sidebar{height:60px} diff --git a/public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css b/public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css new file mode 100644 index 00000000..3a45cacf --- /dev/null +++ b/public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tinymce-mobile-outer-container{all:initial;display:block}.tinymce-mobile-outer-container *{border:0;box-sizing:initial;cursor:inherit;float:none;line-height:1;margin:0;outline:0;padding:0;-webkit-tap-highlight-color:transparent;text-shadow:none;white-space:nowrap}.tinymce-mobile-icon-arrow-back::before{content:"\e5cd"}.tinymce-mobile-icon-image::before{content:"\e412"}.tinymce-mobile-icon-cancel-circle::before{content:"\e5c9"}.tinymce-mobile-icon-full-dot::before{content:"\e061"}.tinymce-mobile-icon-align-center::before{content:"\e234"}.tinymce-mobile-icon-align-left::before{content:"\e236"}.tinymce-mobile-icon-align-right::before{content:"\e237"}.tinymce-mobile-icon-bold::before{content:"\e238"}.tinymce-mobile-icon-italic::before{content:"\e23f"}.tinymce-mobile-icon-unordered-list::before{content:"\e241"}.tinymce-mobile-icon-ordered-list::before{content:"\e242"}.tinymce-mobile-icon-font-size::before{content:"\e245"}.tinymce-mobile-icon-underline::before{content:"\e249"}.tinymce-mobile-icon-link::before{content:"\e157"}.tinymce-mobile-icon-unlink::before{content:"\eca2"}.tinymce-mobile-icon-color::before{content:"\e891"}.tinymce-mobile-icon-previous::before{content:"\e314"}.tinymce-mobile-icon-next::before{content:"\e315"}.tinymce-mobile-icon-large-font::before,.tinymce-mobile-icon-style-formats::before{content:"\e264"}.tinymce-mobile-icon-undo::before{content:"\e166"}.tinymce-mobile-icon-redo::before{content:"\e15a"}.tinymce-mobile-icon-removeformat::before{content:"\e239"}.tinymce-mobile-icon-small-font::before{content:"\e906"}.tinymce-mobile-format-matches::after,.tinymce-mobile-icon-readonly-back::before{content:"\e5ca"}.tinymce-mobile-icon-small-heading::before{content:"small"}.tinymce-mobile-icon-large-heading::before{content:"large"}.tinymce-mobile-icon-large-heading::before,.tinymce-mobile-icon-small-heading::before{font-family:sans-serif;font-size:80%}.tinymce-mobile-mask-edit-icon::before{content:"\e254"}.tinymce-mobile-icon-back::before{content:"\e5c4"}.tinymce-mobile-icon-heading::before{content:"Headings";font-family:sans-serif;font-size:80%;font-weight:700}.tinymce-mobile-icon-h1::before{content:"H1";font-weight:700}.tinymce-mobile-icon-h2::before{content:"H2";font-weight:700}.tinymce-mobile-icon-h3::before{content:"H3";font-weight:700}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask{align-items:center;display:flex;justify-content:center;background:rgba(51,51,51,.5);height:100%;position:absolute;top:0;width:100%}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container{align-items:center;border-radius:50%;display:flex;flex-direction:column;font-family:sans-serif;font-size:1em;justify-content:space-between}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{align-items:center;display:flex;justify-content:center;flex-direction:column;font-size:1em}@media only screen and (min-device-width:700px){.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{font-size:1.2em}}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em;background-color:#fff;color:#207ab7}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before{content:"\e900";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon{z-index:2}.tinymce-mobile-android-container.tinymce-mobile-android-maximized{background:#fff;border:none;bottom:0;display:flex;flex-direction:column;left:0;position:fixed;right:0;top:0}.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized){position:relative}.tinymce-mobile-android-container .tinymce-mobile-editor-socket{display:flex;flex-grow:1}.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe{display:flex!important;flex-grow:1;height:auto!important}.tinymce-mobile-android-scroll-reload{overflow:hidden}:not(.tinymce-mobile-readonly-mode)>.tinymce-mobile-android-selection-context-toolbar{margin-top:23px}.tinymce-mobile-toolstrip{background:#fff;display:flex;flex:0 0 auto;z-index:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar{align-items:center;background-color:#fff;border-bottom:1px solid #ccc;display:flex;flex:1;height:2.5em;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex-shrink:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container{background:#f44336}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group{flex-grow:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button{align-items:center;display:flex;height:80%;margin-left:2px;margin-right:2px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected{background:#c8cbcf;color:#ccc}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type{background:#207ab7;color:#eceff1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex:1;padding-bottom:.4em;padding-top:.4em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog{display:flex;min-height:1.5em;overflow:hidden;padding-left:0;padding-right:0;position:relative;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain{display:flex;height:100%;transition:left cubic-bezier(.4,0,1,1) .15s;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen{display:flex;flex:0 0 auto;justify-content:space-between;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input{font-family:Sans-serif}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container{display:flex;flex-grow:1;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x{-ms-grid-row-align:center;align-self:center;background:inherit;border:none;border-radius:50%;color:#888;font-size:.6em;font-weight:700;height:100%;padding-right:2px;position:absolute;right:0}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x{display:none}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before{align-items:center;display:flex;font-weight:700;height:100%;padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before{visibility:hidden}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item{color:#ccc;font-size:10px;line-height:10px;margin:0 2px;padding-top:3px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active{color:#c8cbcf}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before{margin-left:.5em;margin-right:.9em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before{margin-left:.9em;margin-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider{display:flex;flex:1;margin-left:0;margin-right:0;padding:.28em 0;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line{background:#ccc;display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container{padding-left:2em;padding-right:2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient{background:linear-gradient(to right,red 0,#feff00 17%,#0f0 33%,#00feff 50%,#00f 67%,#ff00fe 83%,red 100%);display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black{background:#000;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white{background:#fff;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb{align-items:center;background-clip:padding-box;background-color:#455a64;border:.5em solid rgba(136,136,136,0);border-radius:3em;bottom:0;color:#fff;display:flex;height:.5em;justify-content:center;left:-10px;margin:auto;position:absolute;top:0;transition:border 120ms cubic-bezier(.39,.58,.57,1);width:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active{border:.5em solid rgba(136,136,136,.39)}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper{flex-direction:column;justify-content:center}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog){height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container{display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input{background:#fff;border:none;border-radius:0;color:#455a64;flex-grow:1;font-size:.85em;padding-bottom:.1em;padding-left:5px;padding-top:.1em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder{color:#888}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder{color:#888}.tinymce-mobile-dropup{background:#fff;display:flex;overflow:hidden;width:100%}.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking{transition:height .3s ease-out}.tinymce-mobile-dropup.tinymce-mobile-dropup-growing{transition:height .3s ease-in}.tinymce-mobile-dropup.tinymce-mobile-dropup-closed{flex-grow:0}.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing){flex-grow:1}.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}@media only screen and (orientation:landscape){.tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:150px}}.tinymce-mobile-styles-menu{font-family:sans-serif;outline:4px solid #000;overflow:hidden;position:relative;width:100%}.tinymce-mobile-styles-menu [role=menu]{display:flex;flex-direction:column;height:100%;position:absolute;width:100%}.tinymce-mobile-styles-menu [role=menu].transitioning{transition:transform .5s ease-in-out}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item{border-bottom:1px solid #ddd;color:#455a64;cursor:pointer;display:flex;padding:1em 1em;position:relative}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before{color:#455a64;content:"\e314";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after{color:#455a64;content:"\e315";font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after{font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser,.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator{align-items:center;background:#fff;border-top:#455a64;color:#455a64;display:flex;min-height:2.5em;padding-left:1em;padding-right:1em}.tinymce-mobile-styles-menu [data-transitioning-destination=before][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=before]{transform:translate(-100%)}.tinymce-mobile-styles-menu [data-transitioning-destination=current][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=current]{transform:translate(0)}.tinymce-mobile-styles-menu [data-transitioning-destination=after][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=after]{transform:translate(100%)}@font-face{font-family:tinymce-mobile;font-style:normal;font-weight:400;src:url(fonts/tinymce-mobile.woff?8x92w3) format('woff')}@media (min-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:25px}}@media (max-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:18px}}.tinymce-mobile-icon{font-family:tinymce-mobile,sans-serif}.mixin-flex-and-centre{align-items:center;display:flex;justify-content:center}.mixin-flex-bar{align-items:center;display:flex;height:100%}.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe{background-color:#fff;width:100%}.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{background-color:#207ab7;border-radius:50%;bottom:1em;color:#fff;font-size:1em;height:2.1em;position:fixed;right:2em;width:2.1em;align-items:center;display:flex;justify-content:center}@media only screen and (min-device-width:700px){.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{font-size:1.2em}}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket{height:300px;overflow:hidden}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe{height:100%}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip{display:none}input[type=file]::-webkit-file-upload-button{display:none}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{bottom:50%}} diff --git a/public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css b/public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css new file mode 100644 index 00000000..a0893b91 --- /dev/null +++ b/public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201} diff --git a/public/resource/tinymce/skins/ui/oxide/content.inline.min.css b/public/resource/tinymce/skins/ui/oxide/content.inline.min.css new file mode 100644 index 00000000..b4ab9a3a --- /dev/null +++ b/public/resource/tinymce/skins/ui/oxide/content.inline.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:1298}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #b4d7ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #b4d7ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #b4d7ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#b4d7ff}.mce-content-body .mce-edit-focus{outline:3px solid #b4d7ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid rgba(180,215,255,.7);bottom:-1px;content:'';left:-1px;mix-blend-mode:multiply;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#b4d7ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'} diff --git a/public/resource/tinymce/skins/ui/oxide/content.min.css b/public/resource/tinymce/skins/ui/oxide/content.min.css new file mode 100644 index 00000000..844858d0 --- /dev/null +++ b/public/resource/tinymce/skins/ui/oxide/content.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:1298}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #b4d7ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #b4d7ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #b4d7ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#b4d7ff}.mce-content-body .mce-edit-focus{outline:3px solid #b4d7ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid rgba(180,215,255,.7);bottom:-1px;content:'';left:-1px;mix-blend-mode:multiply;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#b4d7ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'}body{font-family:sans-serif}table{border-collapse:collapse} diff --git a/public/resource/tinymce/skins/ui/oxide/content.mobile.min.css b/public/resource/tinymce/skins/ui/oxide/content.mobile.min.css new file mode 100644 index 00000000..35f7dc08 --- /dev/null +++ b/public/resource/tinymce/skins/ui/oxide/content.mobile.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse} diff --git a/public/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff b/public/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff new file mode 100644 index 0000000000000000000000000000000000000000..1e3be038a607cb7c2544ed8ae3d6621f77bf4c38 GIT binary patch literal 4624 zcmb7IeQaFC5#QN&AGUL{efE7g{=BM1W-|RaVdWQe^e?BC`eGz4^i8S3PQw?Hhd_eQHxTkckXZB zdzU((wCVGko!Qyh+1c6InRotvZ%+>+hNrBQtrFOI4t*}DZ$7=>Sr=uD3c$ZlKuKBQ z8~ervCczs9SOk2!>AAqrz+v$CC}f1JfYPDSqx->|V$6{ekbe8M#Bh3Gkg?)-Fdi3B zeB$}UFqn*$pv&q7*net~hsUOlfG7Ho2zaowY%JPRytMvu{&xRPm(h_~w##F>vqE&a5-ssH##mlfAk}44^ zXRJKd!Ifw&ce{$Y9BAg5c>e>p_Z;t!=P{izddGWie?aHLdKL3Cn9rG=d2vt;esWqH zoD}uAoi3Z~4+LABvADt+so4~t%VlyIJ{O3tm$NC+(!yenQD%NVr*btG$T3+_WX=LH z#1M2ZNEtrO+-x;l2i>M^5o%GQ@s?N+gw*19H@G~vl3Q5Zf*t6jjW0GOTmAmlWYgSS zJeiEo%~LA-FW|YAd_Em$OE#@dw)y*#@p!UtnWa);V1HY3ZBw!>(3gY{iFFa_c6iW9 zIQ@xck^{xu9_o;UyQH#ba@y?L$xW?8J35?$p1z46ZjIctZ8QCKCa29bMC1-t@pT>S zTUT1WMjQz-75d)5zJxv~@Yd)bY)ejQBx_XQiaMJ z>$5`NO3?L*ND{UQeF8%xl)$_>w9tmQpfEebzedazFeh#~d}suN+vzsqLiW~@TLhoe zk1%xEcxP2ZL)FuoXeYzb-J5goljDxPL2@@#RW)d&X#&6QO5U=04_628@ONSvtgpha zDqqmoVep`A4<+PK$V>K+T}}{8Rj+Q|UAzCtl!Fh)uXJg{x$}HMJH7LcBLzj-r{h;< zzote8Id%pcAyE;87D<8glyaFeq#k)OEDB%yA ze%CeZ!?4TEs#pj+%14DBZHn8jxaF2as6}p3+!6p-&@I>5lbP3&N$svcIF-`0R5(o2 zh7la++|;-euckH44a4BAwB++#-cZ z)kFyC=eUS-4D0t}H8LdZY!JD^sW@F85io)%=8HU)ouhEeo-K_dJ3BV+8fo0JXIjlP zZt0H`0=Yv~I|PpRZ)r5_iAYmY9V=wT@BsoN9<3vftB|}TOH;|yNk_e7(2-?y{&cSK zG=E5Nz^Ko4>KxcbY!Q13!=HBS$lM96_+0y3M1yWTAt2u5C;6MWMXbRN?RI{$eHnAx z&t=-PSjZ>Qe2V2-YGs1YWemAq zVHdG{9V$QvsY~Cgq-L*PZqMPGv|px$)K~3<%+fBtG{oIRPL_7ye$-(`C=tS)^xC}% zue73qiF&{nXJ*>-@668G!`IrAeB;ad09shzt{O?7omLE_X@H|#ozGt&64 zb-&_lLkZI8TzigPZvUr=4g2-8M6M8b9EQLgoPswYg)d)j&%gZHJO!2>(?;I*8d>aG z#oS295Kcq{uD4R2@VEG($}WWiF-6YK)kjqks%o_U{CIAVX2;tX7o|unkew5?Gn3(| zOePS^{$(;Xi4ph;`KO#;k+vaLt8n5@doi+OEvH&?*+3(WgqkT9-$b0fTHm;)r=NmR zJnJ9o>UvNR(JMoIdRBf{%kd}jmZ)b)#4>dnDfq0G(?~S%d zv50QeMR$Kzd*S$AEXdp5Fhqe0Pz zZ!oS2e!i-tWEJ2^YoVo}V7S0tV7CujimbVJtVNb#yB&<-f&xpSb@m2=wBZ|qU-_^; z?C{lk+;tlxk&Sh3Pwh(D7~kNh`O=~TMWuRUu^0=9)`CYEVwhvGWUt4Wd3`6*H)Zs>LLYQcC#*~B78EfTt7RQ*l)b{v zqntLNsC`h&zZCY{x*}gfPU4at;nfileU3>zeyLdO7;;lFIft~ zsm6#wb5Jjtv;_VxleU0<%cQON-O*ywHt`@C4fn-Y83}=|hJPOpN>1H%C#7)9etg_yG)$div{padding-bottom:4px}.tox .accessibility-issue__description>div>div{align-items:center;display:flex;margin-bottom:4px}.tox .accessibility-issue__description>:last-child:not(:only-child){border-color:#ccc;border-style:solid}.tox .accessibility-issue__repair{margin-top:16px}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description{background-color:rgba(32,122,183,.1);border-color:rgba(32,122,183,.4);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description>:last-child{border-color:rgba(32,122,183,.4)}.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2{color:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg{fill:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--info a .tox-icon{color:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description{background-color:rgba(255,165,0,.1);border-color:rgba(255,165,0,.5);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description>:last-child{border-color:rgba(255,165,0,.5)}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2{color:#cc8500}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg{fill:#cc8500}.tox .tox-dialog__body-content .accessibility-issue--warn a .tox-icon{color:#cc8500}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description{background-color:rgba(204,0,0,.1);border-color:rgba(204,0,0,.4);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description>:last-child{border-color:rgba(204,0,0,.4)}.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2{color:#c00}.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg{fill:#c00}.tox .tox-dialog__body-content .accessibility-issue--error a .tox-icon{color:#c00}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description{background-color:rgba(120,171,70,.1);border-color:rgba(120,171,70,.4);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description>:last-child{border-color:rgba(120,171,70,.4)}.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2{color:#78ab46}.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg{fill:#78ab46}.tox .tox-dialog__body-content .accessibility-issue--success a .tox-icon{color:#78ab46}.tox .tox-dialog__body-content .accessibility-issue__header h1,.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2{margin-top:0}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-left:4px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-left:auto}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description{padding:4px 4px 4px 8px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description>:last-child{border-left-width:1px;padding-left:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-right:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-right:auto}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description{padding:4px 8px 4px 4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description>:last-child{border-right-width:1px;padding-right:4px}.tox .tox-anchorbar{display:flex;flex:0 0 auto}.tox .tox-bar{display:flex;flex:0 0 auto}.tox .tox-button{background-color:#207ab7;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#207ab7;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;line-height:24px;margin:0;outline:0;padding:4px 16px;text-align:center;text-decoration:none;text-transform:none;white-space:nowrap}.tox .tox-button[disabled]{background-color:#207ab7;background-image:none;border-color:#207ab7;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-button:focus:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:hover:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:active:not(:disabled){background-color:#185d8c;background-image:none;border-color:#185d8c;box-shadow:none;color:#fff}.tox .tox-button--secondary{background-color:#f0f0f0;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#f0f0f0;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;color:#222f3e;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;outline:0;padding:4px 16px;text-decoration:none;text-transform:none}.tox .tox-button--secondary[disabled]{background-color:#f0f0f0;background-image:none;border-color:#f0f0f0;box-shadow:none;color:rgba(34,47,62,.5)}.tox .tox-button--secondary:focus:not(:disabled){background-color:#e3e3e3;background-image:none;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--secondary:hover:not(:disabled){background-color:#e3e3e3;background-image:none;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--secondary:active:not(:disabled){background-color:#d6d6d6;background-image:none;border-color:#d6d6d6;box-shadow:none;color:#222f3e}.tox .tox-button--icon,.tox .tox-button.tox-button--icon,.tox .tox-button.tox-button--secondary.tox-button--icon{padding:4px}.tox .tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg{display:block;fill:currentColor}.tox .tox-button-link{background:0;border:none;box-sizing:border-box;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;white-space:nowrap}.tox .tox-button-link--sm{font-size:14px}.tox .tox-button--naked{background-color:transparent;border-color:transparent;box-shadow:unset;color:#222f3e}.tox .tox-button--naked[disabled]{background-color:#f0f0f0;border-color:#f0f0f0;box-shadow:none;color:rgba(34,47,62,.5)}.tox .tox-button--naked:hover:not(:disabled){background-color:#e3e3e3;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--naked:focus:not(:disabled){background-color:#e3e3e3;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--naked:active:not(:disabled){background-color:#d6d6d6;border-color:#d6d6d6;box-shadow:none;color:#222f3e}.tox .tox-button--naked .tox-icon svg{fill:currentColor}.tox .tox-button--naked.tox-button--icon:hover:not(:disabled){color:#222f3e}.tox .tox-checkbox{align-items:center;border-radius:3px;cursor:pointer;display:flex;height:36px;min-width:36px}.tox .tox-checkbox__input{height:1px;overflow:hidden;position:absolute;top:auto;width:1px}.tox .tox-checkbox__icons{align-items:center;border-radius:3px;box-shadow:0 0 0 2px transparent;box-sizing:content-box;display:flex;height:24px;justify-content:center;padding:calc(4px - 1px);width:24px}.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:block;fill:rgba(34,47,62,.3)}.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:none;fill:#207ab7}.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg{display:none;fill:#207ab7}.tox .tox-checkbox--disabled{color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg{fill:rgba(34,47,62,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{fill:rgba(34,47,62,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{fill:rgba(34,47,62,.5)}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__checked svg{display:block}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:block}.tox input.tox-checkbox__input:focus+.tox-checkbox__icons{border-radius:3px;box-shadow:inset 0 0 0 1px #207ab7;padding:calc(4px - 1px)}.tox:not([dir=rtl]) .tox-checkbox__label{margin-left:4px}.tox:not([dir=rtl]) .tox-checkbox__input{left:-10000px}.tox:not([dir=rtl]) .tox-bar .tox-checkbox{margin-left:4px}.tox[dir=rtl] .tox-checkbox__label{margin-right:4px}.tox[dir=rtl] .tox-checkbox__input{right:-10000px}.tox[dir=rtl] .tox-bar .tox-checkbox{margin-right:4px}.tox .tox-collection--toolbar .tox-collection__group{display:flex;padding:0}.tox .tox-collection--grid .tox-collection__group{display:flex;flex-wrap:wrap;max-height:208px;overflow-x:hidden;overflow-y:auto;padding:0}.tox .tox-collection--list .tox-collection__group{border-bottom-width:0;border-color:#ccc;border-left-width:0;border-right-width:0;border-style:solid;border-top-width:1px;padding:4px 0}.tox .tox-collection--list .tox-collection__group:first-child{border-top-width:0}.tox .tox-collection__group-heading{background-color:#e6e6e6;color:rgba(34,47,62,.7);cursor:default;font-size:12px;font-style:normal;font-weight:400;margin-bottom:4px;margin-top:-4px;padding:4px 8px;text-transform:none;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection__item{align-items:center;color:#222f3e;cursor:pointer;display:flex;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection--list .tox-collection__item{padding:4px 8px}.tox .tox-collection--toolbar .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--grid .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--list .tox-collection__item--enabled{background-color:#fff;color:#222f3e}.tox .tox-collection--list .tox-collection__item--active{background-color:#dee0e2}.tox .tox-collection--toolbar .tox-collection__item--enabled{background-color:#c8cbcf;color:#222f3e}.tox .tox-collection--toolbar .tox-collection__item--active{background-color:#dee0e2}.tox .tox-collection--grid .tox-collection__item--enabled{background-color:#c8cbcf;color:#222f3e}.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled){background-color:#dee0e2;color:#222f3e}.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#222f3e}.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#222f3e}.tox .tox-collection__item-checkmark,.tox .tox-collection__item-icon{align-items:center;display:flex;height:24px;justify-content:center;width:24px}.tox .tox-collection__item-checkmark svg,.tox .tox-collection__item-icon svg{fill:currentColor}.tox .tox-collection--toolbar-lg .tox-collection__item-icon{height:48px;width:48px}.tox .tox-collection__item-label{color:currentColor;display:inline-block;flex:1;-ms-flex-preferred-size:auto;font-size:14px;font-style:normal;font-weight:400;line-height:24px;text-transform:none;word-break:break-all}.tox .tox-collection__item-accessory{color:rgba(34,47,62,.7);display:inline-block;font-size:14px;height:24px;line-height:24px;text-transform:none}.tox .tox-collection__item-caret{align-items:center;display:flex;min-height:24px}.tox .tox-collection__item-caret::after{content:'';font-size:0;min-height:inherit}.tox .tox-collection__item-caret svg{fill:#222f3e}.tox .tox-collection__item--state-disabled{background-color:transparent;color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-collection__item--state-disabled .tox-collection__item-caret svg{fill:rgba(34,47,62,.5)}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg{display:none}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory+.tox-collection__item-checkmark{display:none}.tox .tox-collection--horizontal{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:nowrap;margin-bottom:0;overflow-x:auto;padding:0}.tox .tox-collection--horizontal .tox-collection__group{align-items:center;display:flex;flex-wrap:nowrap;margin:0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item{height:34px;margin:2px 0 3px 0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item-label{white-space:nowrap}.tox .tox-collection--horizontal .tox-collection__item-caret{margin-left:4px}.tox .tox-collection__item-container{display:flex}.tox .tox-collection__item-container--row{align-items:center;flex:1 1 auto;flex-direction:row}.tox .tox-collection__item-container--row.tox-collection__item-container--align-left{margin-right:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--align-right{justify-content:flex-end;margin-left:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top{align-items:flex-start;margin-bottom:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle{align-items:center}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom{align-items:flex-end;margin-top:auto}.tox .tox-collection__item-container--column{-ms-grid-row-align:center;align-self:center;flex:1 1 auto;flex-direction:column}.tox .tox-collection__item-container--column.tox-collection__item-container--align-left{align-items:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--align-right{align-items:flex-end}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top{align-self:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle{-ms-grid-row-align:center;align-self:center}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom{align-self:flex-end}.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-right:1px solid #ccc}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>:not(:first-child){margin-left:8px}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-left:4px}.tox:not([dir=rtl]) .tox-collection__item-accessory{margin-left:16px;text-align:right}.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret{margin-left:16px}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-left:1px solid #ccc}.tox[dir=rtl] .tox-collection--list .tox-collection__item>:not(:first-child){margin-right:8px}.tox[dir=rtl] .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-right:4px}.tox[dir=rtl] .tox-collection__item-accessory{margin-right:16px;text-align:left}.tox[dir=rtl] .tox-collection .tox-collection__item-caret{margin-right:16px;transform:rotateY(180deg)}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret{margin-right:4px}.tox .tox-color-picker-container{display:flex;flex-direction:row;height:225px;margin:0}.tox .tox-sv-palette{box-sizing:border-box;display:flex;height:100%}.tox .tox-sv-palette-spectrum{height:100%}.tox .tox-sv-palette,.tox .tox-sv-palette-spectrum{width:225px}.tox .tox-sv-palette-thumb{background:0 0;border:1px solid #000;border-radius:50%;box-sizing:content-box;height:12px;position:absolute;width:12px}.tox .tox-sv-palette-inner-thumb{border:1px solid #fff;border-radius:50%;height:10px;position:absolute;width:10px}.tox .tox-hue-slider{box-sizing:border-box;height:100%;width:25px}.tox .tox-hue-slider-spectrum{background:linear-gradient(to bottom,red,#ff0080,#f0f,#8000ff,#00f,#0080ff,#0ff,#00ff80,#0f0,#80ff00,#ff0,#ff8000,red);height:100%;width:100%}.tox .tox-hue-slider,.tox .tox-hue-slider-spectrum{width:20px}.tox .tox-hue-slider-thumb{background:#fff;border:1px solid #000;box-sizing:content-box;height:4px;width:100%}.tox .tox-rgb-form{display:flex;flex-direction:column;justify-content:space-between}.tox .tox-rgb-form div{align-items:center;display:flex;justify-content:space-between;margin-bottom:5px;width:inherit}.tox .tox-rgb-form input{width:6em}.tox .tox-rgb-form input.tox-invalid{border:1px solid red!important}.tox .tox-rgb-form .tox-rgba-preview{border:1px solid #000;flex-grow:2;margin-bottom:0}.tox:not([dir=rtl]) .tox-sv-palette{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider-thumb{margin-left:-1px}.tox:not([dir=rtl]) .tox-rgb-form label{margin-right:.5em}.tox[dir=rtl] .tox-sv-palette{margin-left:15px}.tox[dir=rtl] .tox-hue-slider{margin-left:15px}.tox[dir=rtl] .tox-hue-slider-thumb{margin-right:-1px}.tox[dir=rtl] .tox-rgb-form label{margin-left:.5em}.tox .tox-toolbar .tox-swatches,.tox .tox-toolbar__overflow .tox-swatches,.tox .tox-toolbar__primary .tox-swatches{margin:2px 0 3px 4px}.tox .tox-collection--list .tox-collection__group .tox-swatches-menu{border:0;margin:-4px 0}.tox .tox-swatches__row{display:flex}.tox .tox-swatch{height:30px;transition:transform .15s,box-shadow .15s;width:30px}.tox .tox-swatch:focus,.tox .tox-swatch:hover{box-shadow:0 0 0 1px rgba(127,127,127,.3) inset;transform:scale(.8)}.tox .tox-swatch--remove{align-items:center;display:flex;justify-content:center}.tox .tox-swatch--remove svg path{stroke:#e74c3c}.tox .tox-swatches__picker-btn{align-items:center;background-color:transparent;border:0;cursor:pointer;display:flex;height:30px;justify-content:center;outline:0;padding:0;width:30px}.tox .tox-swatches__picker-btn svg{height:24px;width:24px}.tox .tox-swatches__picker-btn:hover{background:#dee0e2}.tox:not([dir=rtl]) .tox-swatches__picker-btn{margin-left:auto}.tox[dir=rtl] .tox-swatches__picker-btn{margin-right:auto}.tox .tox-comment-thread{background:#fff;position:relative}.tox .tox-comment-thread>:not(:first-child){margin-top:8px}.tox .tox-comment{background:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 4px 8px 0 rgba(34,47,62,.1);padding:8px 8px 16px 8px;position:relative}.tox .tox-comment__header{align-items:center;color:#222f3e;display:flex;justify-content:space-between}.tox .tox-comment__date{color:rgba(34,47,62,.7);font-size:12px}.tox .tox-comment__body{color:#222f3e;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;margin-top:8px;position:relative;text-transform:initial}.tox .tox-comment__body textarea{resize:none;white-space:normal;width:100%}.tox .tox-comment__expander{padding-top:8px}.tox .tox-comment__expander p{color:rgba(34,47,62,.7);font-size:14px;font-style:normal}.tox .tox-comment__body p{margin:0}.tox .tox-comment__buttonspacing{padding-top:16px;text-align:center}.tox .tox-comment-thread__overlay::after{background:#fff;bottom:0;content:"";display:flex;left:0;opacity:.9;position:absolute;right:0;top:0;z-index:5}.tox .tox-comment__reply{display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end;margin-top:8px}.tox .tox-comment__reply>:first-child{margin-bottom:8px;width:100%}.tox .tox-comment__edit{display:flex;flex-wrap:wrap;justify-content:flex-end;margin-top:16px}.tox .tox-comment__gradient::after{background:linear-gradient(rgba(255,255,255,0),#fff);bottom:0;content:"";display:block;height:5em;margin-top:-40px;position:absolute;width:100%}.tox .tox-comment__overlay{background:#fff;bottom:0;display:flex;flex-direction:column;flex-grow:1;left:0;opacity:.9;position:absolute;right:0;text-align:center;top:0;z-index:5}.tox .tox-comment__loading-text{align-items:center;color:#222f3e;display:flex;flex-direction:column;position:relative}.tox .tox-comment__loading-text>div{padding-bottom:16px}.tox .tox-comment__overlaytext{bottom:0;flex-direction:column;font-size:14px;left:0;padding:1em;position:absolute;right:0;top:0;z-index:10}.tox .tox-comment__overlaytext p{background-color:#fff;box-shadow:0 0 8px 8px #fff;color:#222f3e;text-align:center}.tox .tox-comment__overlaytext div:nth-of-type(2){font-size:.8em}.tox .tox-comment__busy-spinner{align-items:center;background-color:#fff;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:20}.tox .tox-comment__scroll{display:flex;flex-direction:column;flex-shrink:1;overflow:auto}.tox .tox-conversations{margin:8px}.tox:not([dir=rtl]) .tox-comment__edit{margin-left:8px}.tox:not([dir=rtl]) .tox-comment__buttonspacing>:last-child,.tox:not([dir=rtl]) .tox-comment__edit>:last-child,.tox:not([dir=rtl]) .tox-comment__reply>:last-child{margin-left:8px}.tox[dir=rtl] .tox-comment__edit{margin-right:8px}.tox[dir=rtl] .tox-comment__buttonspacing>:last-child,.tox[dir=rtl] .tox-comment__edit>:last-child,.tox[dir=rtl] .tox-comment__reply>:last-child{margin-right:8px}.tox .tox-user{align-items:center;display:flex}.tox .tox-user__avatar svg{fill:rgba(34,47,62,.7)}.tox .tox-user__name{color:rgba(34,47,62,.7);font-size:12px;font-style:normal;font-weight:700;text-transform:uppercase}.tox:not([dir=rtl]) .tox-user__avatar svg{margin-right:8px}.tox:not([dir=rtl]) .tox-user__avatar+.tox-user__name{margin-left:8px}.tox[dir=rtl] .tox-user__avatar svg{margin-left:8px}.tox[dir=rtl] .tox-user__avatar+.tox-user__name{margin-right:8px}.tox .tox-dialog-wrap{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:fixed;right:0;top:0;z-index:1100}.tox .tox-dialog-wrap__backdrop{background-color:rgba(255,255,255,.75);bottom:0;left:0;position:absolute;right:0;top:0;z-index:1}.tox .tox-dialog-wrap__backdrop--opaque{background-color:#fff}.tox .tox-dialog{background-color:#fff;border-color:#ccc;border-radius:3px;border-style:solid;border-width:1px;box-shadow:0 16px 16px -10px rgba(34,47,62,.15),0 0 40px 1px rgba(34,47,62,.15);display:flex;flex-direction:column;max-height:100%;max-width:480px;overflow:hidden;position:relative;width:95vw;z-index:2}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog{align-self:flex-start;margin:8px auto;width:calc(100vw - 16px)}}.tox .tox-dialog-inline{z-index:1100}.tox .tox-dialog__header{align-items:center;background-color:#fff;border-bottom:none;color:#222f3e;display:flex;font-size:16px;justify-content:space-between;padding:8px 16px 0 16px;position:relative}.tox .tox-dialog__header .tox-button{z-index:1}.tox .tox-dialog__draghandle{cursor:grab;height:100%;left:0;position:absolute;top:0;width:100%}.tox .tox-dialog__draghandle:active{cursor:grabbing}.tox .tox-dialog__dismiss{margin-left:auto}.tox .tox-dialog__title{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1.3;margin:0;text-transform:none}.tox .tox-dialog__body{color:#222f3e;display:flex;flex:1;-ms-flex-preferred-size:auto;font-size:16px;font-style:normal;font-weight:400;line-height:1.3;min-width:0;text-align:left;text-transform:none}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body{flex-direction:column}}.tox .tox-dialog__body-nav{align-items:flex-start;display:flex;flex-direction:column;padding:16px 16px}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body-nav{flex-direction:row;-webkit-overflow-scrolling:touch;overflow-x:auto;padding-bottom:0}}.tox .tox-dialog__body-nav-item{border-bottom:2px solid transparent;color:rgba(34,47,62,.7);display:inline-block;font-size:14px;line-height:1.3;margin-bottom:8px;text-decoration:none;white-space:nowrap}.tox .tox-dialog__body-nav-item:focus{background-color:rgba(32,122,183,.1)}.tox .tox-dialog__body-nav-item--active{border-bottom:2px solid #207ab7;color:#207ab7}.tox .tox-dialog__body-content{box-sizing:border-box;display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto;max-height:650px;overflow:auto;-webkit-overflow-scrolling:touch;padding:16px 16px}.tox .tox-dialog__body-content>*{margin-bottom:0;margin-top:16px}.tox .tox-dialog__body-content>:first-child{margin-top:0}.tox .tox-dialog__body-content>:last-child{margin-bottom:0}.tox .tox-dialog__body-content>:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog__body-content a{color:#207ab7;cursor:pointer;text-decoration:none}.tox .tox-dialog__body-content a:focus,.tox .tox-dialog__body-content a:hover{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content a:active{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content svg{fill:#222f3e}.tox .tox-dialog__body-content ul{display:block;list-style-type:disc;margin-bottom:16px;-webkit-margin-end:0;margin-inline-end:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem}.tox .tox-dialog__body-content .tox-form__group h1{color:#222f3e;font-size:20px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group h2{color:#222f3e;font-size:16px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group p{margin-bottom:16px}.tox .tox-dialog__body-content .tox-form__group h1:first-child,.tox .tox-dialog__body-content .tox-form__group h2:first-child,.tox .tox-dialog__body-content .tox-form__group p:first-child{margin-top:0}.tox .tox-dialog__body-content .tox-form__group h1:last-child,.tox .tox-dialog__body-content .tox-form__group h2:last-child,.tox .tox-dialog__body-content .tox-form__group p:last-child{margin-bottom:0}.tox .tox-dialog__body-content .tox-form__group h1:only-child,.tox .tox-dialog__body-content .tox-form__group h2:only-child,.tox .tox-dialog__body-content .tox-form__group p:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog--width-lg{height:650px;max-width:1200px}.tox .tox-dialog--width-md{max-width:800px}.tox .tox-dialog--width-md .tox-dialog__body-content{overflow:auto}.tox .tox-dialog__body-content--centered{text-align:center}.tox .tox-dialog__footer{align-items:center;background-color:#fff;border-top:1px solid #ccc;display:flex;justify-content:space-between;padding:8px 16px}.tox .tox-dialog__footer-end,.tox .tox-dialog__footer-start{display:flex}.tox .tox-dialog__busy-spinner{align-items:center;background-color:rgba(255,255,255,.75);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:3}.tox .tox-dialog__table{border-collapse:collapse;width:100%}.tox .tox-dialog__table thead th{font-weight:700;padding-bottom:8px}.tox .tox-dialog__table tbody tr{border-bottom:1px solid #ccc}.tox .tox-dialog__table tbody tr:last-child{border-bottom:none}.tox .tox-dialog__table td{padding-bottom:8px;padding-top:8px}.tox .tox-dialog__popups{position:absolute;width:100%;z-index:1100}.tox .tox-dialog__body-iframe{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox .tox-dialog-dock-fadeout{opacity:0;visibility:hidden}.tox .tox-dialog-dock-fadein{opacity:1;visibility:visible}.tox .tox-dialog-dock-transition{transition:visibility 0s linear .3s,opacity .3s ease}.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein{transition-delay:0s}.tox.tox-platform-ie .tox-dialog-wrap{position:-ms-device-fixed}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav{margin-right:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child){margin-left:8px}}.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end>*,.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start>*{margin-left:8px}.tox[dir=rtl] .tox-dialog__body{text-align:right}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav{margin-left:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child){margin-right:8px}}.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end>*,.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start>*{margin-right:8px}body.tox-dialog__disable-scroll{overflow:hidden}.tox .tox-dropzone-container{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dropzone{align-items:center;background:#fff;border:2px dashed #ccc;box-sizing:border-box;display:flex;flex-direction:column;flex-grow:1;justify-content:center;min-height:100px;padding:10px}.tox .tox-dropzone p{color:rgba(34,47,62,.7);margin:0 0 16px 0}.tox .tox-edit-area{display:flex;flex:1;-ms-flex-preferred-size:auto;overflow:hidden;position:relative}.tox .tox-edit-area__iframe{background-color:#fff;border:0;box-sizing:border-box;flex:1;-ms-flex-preferred-size:auto;height:100%;position:absolute;width:100%}.tox.tox-inline-edit-area{border:1px dotted #ccc}.tox .tox-editor-container{display:flex;flex:1 1 auto;flex-direction:column;overflow:hidden}.tox .tox-editor-header{z-index:1}.tox:not(.tox-tinymce-inline) .tox-editor-header{box-shadow:none;transition:box-shadow .5s}.tox.tox-tinymce--toolbar-bottom .tox-editor-header,.tox.tox-tinymce-inline .tox-editor-header{margin-bottom:-1px}.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header{background-color:transparent;box-shadow:0 4px 4px -3px rgba(0,0,0,.25)}.tox-editor-dock-fadeout{opacity:0;visibility:hidden}.tox-editor-dock-fadein{opacity:1;visibility:visible}.tox-editor-dock-transition{transition:visibility 0s linear .25s,opacity .25s ease}.tox-editor-dock-transition.tox-editor-dock-fadein{transition-delay:0s}.tox .tox-control-wrap{flex:1;position:relative}.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid{display:none}.tox .tox-control-wrap svg{display:block}.tox .tox-control-wrap__status-icon-wrap{position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-control-wrap__status-icon-invalid svg{fill:#c00}.tox .tox-control-wrap__status-icon-unknown svg{fill:orange}.tox .tox-control-wrap__status-icon-valid svg{fill:green}.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield{padding-right:32px}.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap{right:4px}.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield{padding-left:32px}.tox[dir=rtl] .tox-control-wrap__status-icon-wrap{left:4px}.tox .tox-autocompleter{max-width:25em}.tox .tox-autocompleter .tox-menu{max-width:25em}.tox .tox-autocompleter .tox-autocompleter-highlight{font-weight:700}.tox .tox-color-input{display:flex;position:relative;z-index:1}.tox .tox-color-input .tox-textfield{z-index:-1}.tox .tox-color-input span{border-color:rgba(34,47,62,.2);border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;height:24px;position:absolute;top:6px;width:24px}.tox .tox-color-input span:focus:not([aria-disabled=true]),.tox .tox-color-input span:hover:not([aria-disabled=true]){border-color:#207ab7;cursor:pointer}.tox .tox-color-input span::before{background-image:linear-gradient(45deg,rgba(0,0,0,.25) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.25) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.25) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.25) 75%);background-position:0 0,0 6px,6px -6px,-6px 0;background-size:12px 12px;border:1px solid #fff;border-radius:3px;box-sizing:border-box;content:'';height:24px;left:-1px;position:absolute;top:-1px;width:24px;z-index:-1}.tox .tox-color-input span[aria-disabled=true]{cursor:not-allowed}.tox:not([dir=rtl]) .tox-color-input .tox-textfield{padding-left:36px}.tox:not([dir=rtl]) .tox-color-input span{left:6px}.tox[dir=rtl] .tox-color-input .tox-textfield{padding-right:36px}.tox[dir=rtl] .tox-color-input span{right:6px}.tox .tox-label,.tox .tox-toolbar-label{color:rgba(34,47,62,.7);display:block;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;padding:0 8px 0 0;text-transform:none;white-space:nowrap}.tox .tox-toolbar-label{padding:0 8px}.tox[dir=rtl] .tox-label{padding:0 0 0 8px}.tox .tox-form{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group{box-sizing:border-box;margin-bottom:4px}.tox .tox-form-group--maximize{flex:1}.tox .tox-form__group--error{color:#c00}.tox .tox-form__group--collection{display:flex}.tox .tox-form__grid{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between}.tox .tox-form__grid--2col>.tox-form__group{width:calc(50% - (8px / 2))}.tox .tox-form__grid--3col>.tox-form__group{width:calc(100% / 3 - (8px / 2))}.tox .tox-form__grid--4col>.tox-form__group{width:calc(25% - (8px / 2))}.tox .tox-form__controls-h-stack{align-items:center;display:flex}.tox .tox-form__group--inline{align-items:center;display:flex}.tox .tox-form__group--stretched{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-textarea{flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox:not([dir=rtl]) .tox-form__controls-h-stack>:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-form__controls-h-stack>:not(:first-child){margin-right:4px}.tox .tox-lock.tox-locked .tox-lock-icon__unlock,.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock{display:none}.tox .tox-listboxfield .tox-listbox--select,.tox .tox-textarea,.tox .tox-textfield,.tox .tox-toolbar-textfield{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#ccc;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#222f3e;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-textarea[disabled],.tox .tox-textfield[disabled]{background-color:#f2f2f2;color:rgba(34,47,62,.85);cursor:not-allowed}.tox .tox-listboxfield .tox-listbox--select:focus,.tox .tox-textarea:focus,.tox .tox-textfield:focus{background-color:#fff;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-toolbar-textfield{border-width:0;margin-bottom:3px;margin-top:2px;max-width:250px}.tox .tox-naked-btn{background-color:transparent;border:0;border-color:transparent;box-shadow:unset;color:#207ab7;cursor:pointer;display:block;margin:0;padding:0}.tox .tox-naked-btn svg{display:block;fill:#222f3e}.tox:not([dir=rtl]) .tox-toolbar-textfield+*{margin-left:4px}.tox[dir=rtl] .tox-toolbar-textfield+*{margin-right:4px}.tox .tox-listboxfield{cursor:pointer;position:relative}.tox .tox-listboxfield .tox-listbox--select[disabled]{background-color:#f2f2f2;color:rgba(34,47,62,.85);cursor:not-allowed}.tox .tox-listbox__select-label{cursor:default;flex:1;margin:0 4px}.tox .tox-listbox__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-listbox__select-chevron svg{fill:#222f3e}.tox .tox-listboxfield .tox-listbox--select{align-items:center;display:flex}.tox:not([dir=rtl]) .tox-listboxfield svg{right:8px}.tox[dir=rtl] .tox-listboxfield svg{left:8px}.tox .tox-selectfield{cursor:pointer;position:relative}.tox .tox-selectfield select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#ccc;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#222f3e;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-selectfield select[disabled]{background-color:#f2f2f2;color:rgba(34,47,62,.85);cursor:not-allowed}.tox .tox-selectfield select::-ms-expand{display:none}.tox .tox-selectfield select:focus{background-color:#fff;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-selectfield svg{pointer-events:none;position:absolute;top:50%;transform:translateY(-50%)}.tox:not([dir=rtl]) .tox-selectfield select[size="0"],.tox:not([dir=rtl]) .tox-selectfield select[size="1"]{padding-right:24px}.tox:not([dir=rtl]) .tox-selectfield svg{right:8px}.tox[dir=rtl] .tox-selectfield select[size="0"],.tox[dir=rtl] .tox-selectfield select[size="1"]{padding-left:24px}.tox[dir=rtl] .tox-selectfield svg{left:8px}.tox .tox-textarea{-webkit-appearance:textarea;-moz-appearance:textarea;appearance:textarea;white-space:pre-wrap}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}.tox .tox-help__more-link{list-style:none;margin-top:1em}.tox .tox-image-tools{width:100%}.tox .tox-image-tools__toolbar{align-items:center;display:flex;justify-content:center}.tox .tox-image-tools__image{background-color:#666;height:380px;overflow:auto;position:relative;width:100%}.tox .tox-image-tools__image,.tox .tox-image-tools__image+.tox-image-tools__toolbar{margin-top:8px}.tox .tox-image-tools__image-bg{background:url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==)}.tox .tox-image-tools__toolbar>.tox-spacer{flex:1;-ms-flex-preferred-size:auto}.tox .tox-croprect-block{background:#000;opacity:.5;position:absolute;zoom:1}.tox .tox-croprect-handle{border:2px solid #fff;height:20px;left:0;position:absolute;top:0;width:20px}.tox .tox-croprect-handle-move{border:0;cursor:move;position:absolute}.tox .tox-croprect-handle-nw{border-width:2px 0 0 2px;cursor:nw-resize;left:100px;margin:-2px 0 0 -2px;top:100px}.tox .tox-croprect-handle-ne{border-width:2px 2px 0 0;cursor:ne-resize;left:200px;margin:-2px 0 0 -20px;top:100px}.tox .tox-croprect-handle-sw{border-width:0 0 2px 2px;cursor:sw-resize;left:100px;margin:-20px 2px 0 -2px;top:200px}.tox .tox-croprect-handle-se{border-width:0 2px 2px 0;cursor:se-resize;left:200px;margin:-20px 0 0 -20px;top:200px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-left:8px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-left:32px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-left:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-right:8px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-right:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-right:32px}.tox .tox-insert-table-picker{display:flex;flex-wrap:wrap;width:170px}.tox .tox-insert-table-picker>div{border-color:#ccc;border-style:solid;border-width:0 1px 1px 0;box-sizing:border-box;height:17px;width:17px}.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker{margin:-4px 0}.tox .tox-insert-table-picker .tox-insert-table-picker__selected{background-color:rgba(32,122,183,.5);border-color:rgba(32,122,183,.5)}.tox .tox-insert-table-picker__label{color:rgba(34,47,62,.7);display:block;font-size:14px;padding:4px;text-align:center;width:100%}.tox:not([dir=rtl]) .tox-insert-table-picker>div:nth-child(10n){border-right:0}.tox[dir=rtl] .tox-insert-table-picker>div:nth-child(10n+1){border-right:0}.tox .tox-menu{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 4px 8px 0 rgba(34,47,62,.1);display:inline-block;overflow:hidden;vertical-align:top;z-index:1150}.tox .tox-menu.tox-collection.tox-collection--list{padding:0}.tox .tox-menu.tox-collection.tox-collection--toolbar{padding:4px}.tox .tox-menu.tox-collection.tox-collection--grid{padding:4px}.tox .tox-menu__label blockquote,.tox .tox-menu__label code,.tox .tox-menu__label h1,.tox .tox-menu__label h2,.tox .tox-menu__label h3,.tox .tox-menu__label h4,.tox .tox-menu__label h5,.tox .tox-menu__label h6,.tox .tox-menu__label p{margin:0}.tox .tox-menubar{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E") left 0 top 0 #fff;background-color:#fff;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 4px 0 4px}.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar{border-top:1px solid #ccc}.tox .tox-mbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#222f3e;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0 4px;text-transform:none;width:auto}.tox .tox-mbtn[disabled]{background-color:transparent;border:0;box-shadow:none;color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-mbtn:focus:not(:disabled){background:#dee0e2;border:0;box-shadow:none;color:#222f3e}.tox .tox-mbtn--active{background:#c8cbcf;border:0;box-shadow:none;color:#222f3e}.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active){background:#dee0e2;border:0;box-shadow:none;color:#222f3e}.tox .tox-mbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-mbtn[disabled] .tox-mbtn__select-label{cursor:not-allowed}.tox .tox-mbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px;display:none}.tox .tox-notification{border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;display:-ms-grid;display:grid;font-size:14px;font-weight:400;-ms-grid-columns:minmax(40px,1fr) auto minmax(40px,1fr);grid-template-columns:minmax(40px,1fr) auto minmax(40px,1fr);margin-top:4px;opacity:0;padding:4px;transition:transform .1s ease-in,opacity 150ms ease-in}.tox .tox-notification p{font-size:14px;font-weight:400}.tox .tox-notification a{cursor:pointer;text-decoration:underline}.tox .tox-notification--in{opacity:1}.tox .tox-notification--success{background-color:#e4eeda;border-color:#d7e6c8;color:#222f3e}.tox .tox-notification--success p{color:#222f3e}.tox .tox-notification--success a{color:#547831}.tox .tox-notification--success svg{fill:#222f3e}.tox .tox-notification--error{background-color:#f8dede;border-color:#f2bfbf;color:#222f3e}.tox .tox-notification--error p{color:#222f3e}.tox .tox-notification--error a{color:#c00}.tox .tox-notification--error svg{fill:#222f3e}.tox .tox-notification--warn,.tox .tox-notification--warning{background-color:#fffaea;border-color:#ffe89d;color:#222f3e}.tox .tox-notification--warn p,.tox .tox-notification--warning p{color:#222f3e}.tox .tox-notification--warn a,.tox .tox-notification--warning a{color:#222f3e}.tox .tox-notification--warn svg,.tox .tox-notification--warning svg{fill:#222f3e}.tox .tox-notification--info{background-color:#d9edf7;border-color:#779ecb;color:#222f3e}.tox .tox-notification--info p{color:#222f3e}.tox .tox-notification--info a{color:#222f3e}.tox .tox-notification--info svg{fill:#222f3e}.tox .tox-notification__body{-ms-grid-row-align:center;align-self:center;color:#222f3e;font-size:14px;-ms-grid-column-span:1;grid-column-end:3;-ms-grid-column:2;grid-column-start:2;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;text-align:center;white-space:normal;word-break:break-all;word-break:break-word}.tox .tox-notification__body>*{margin:0}.tox .tox-notification__body>*+*{margin-top:1rem}.tox .tox-notification__icon{-ms-grid-row-align:center;align-self:center;-ms-grid-column-span:1;grid-column-end:2;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification__icon svg{display:block}.tox .tox-notification__dismiss{-ms-grid-row-align:start;align-self:start;-ms-grid-column-span:1;grid-column-end:4;-ms-grid-column:3;grid-column-start:3;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification .tox-progress-bar{-ms-grid-column-span:3;grid-column-end:4;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:3;-ms-grid-row:2;grid-row-start:2;-ms-grid-column-align:center;justify-self:center}.tox .tox-pop{display:inline-block;position:relative}.tox .tox-pop--resizing{transition:width .1s ease}.tox .tox-pop--resizing .tox-toolbar,.tox .tox-pop--resizing .tox-toolbar__group{flex-wrap:nowrap}.tox .tox-pop--transition{transition:.15s ease;transition-property:left,right,top,bottom}.tox .tox-pop--transition::after,.tox .tox-pop--transition::before{transition:all .15s,visibility 0s,opacity 75ms ease 75ms}.tox .tox-pop__dialog{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);min-width:0;overflow:hidden}.tox .tox-pop__dialog>:not(.tox-toolbar){margin:4px 4px 4px 8px}.tox .tox-pop__dialog .tox-toolbar{background-color:transparent;margin-bottom:-1px}.tox .tox-pop::after,.tox .tox-pop::before{border-style:solid;content:'';display:block;height:0;opacity:1;position:absolute;width:0}.tox .tox-pop.tox-pop--inset::after,.tox .tox-pop.tox-pop--inset::before{opacity:0;transition:all 0s .15s,visibility 0s,opacity 75ms ease}.tox .tox-pop.tox-pop--bottom::after,.tox .tox-pop.tox-pop--bottom::before{left:50%;top:100%}.tox .tox-pop.tox-pop--bottom::after{border-color:#fff transparent transparent transparent;border-width:8px;margin-left:-8px;margin-top:-1px}.tox .tox-pop.tox-pop--bottom::before{border-color:#ccc transparent transparent transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--top::after,.tox .tox-pop.tox-pop--top::before{left:50%;top:0;transform:translateY(-100%)}.tox .tox-pop.tox-pop--top::after{border-color:transparent transparent #fff transparent;border-width:8px;margin-left:-8px;margin-top:1px}.tox .tox-pop.tox-pop--top::before{border-color:transparent transparent #ccc transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--left::after,.tox .tox-pop.tox-pop--left::before{left:0;top:calc(50% - 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--left::after{border-color:transparent #fff transparent transparent;border-width:8px;margin-left:-15px}.tox .tox-pop.tox-pop--left::before{border-color:transparent #ccc transparent transparent;border-width:10px;margin-left:-19px}.tox .tox-pop.tox-pop--right::after,.tox .tox-pop.tox-pop--right::before{left:100%;top:calc(50% + 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--right::after{border-color:transparent transparent transparent #fff;border-width:8px;margin-left:-1px}.tox .tox-pop.tox-pop--right::before{border-color:transparent transparent transparent #ccc;border-width:10px;margin-left:-1px}.tox .tox-pop.tox-pop--align-left::after,.tox .tox-pop.tox-pop--align-left::before{left:20px}.tox .tox-pop.tox-pop--align-right::after,.tox .tox-pop.tox-pop--align-right::before{left:calc(100% - 20px)}.tox .tox-sidebar-wrap{display:flex;flex-direction:row;flex-grow:1;-ms-flex-preferred-size:0;min-height:0}.tox .tox-sidebar{background-color:#fff;display:flex;flex-direction:row;justify-content:flex-end}.tox .tox-sidebar__slider{display:flex;overflow:hidden}.tox .tox-sidebar__pane-container{display:flex}.tox .tox-sidebar__pane{display:flex}.tox .tox-sidebar--sliding-closed{opacity:0}.tox .tox-sidebar--sliding-open{opacity:1}.tox .tox-sidebar--sliding-growing,.tox .tox-sidebar--sliding-shrinking{transition:width .5s ease,opacity .5s ease}.tox .tox-selector{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;display:inline-block;height:10px;position:absolute;width:10px}.tox.tox-platform-touch .tox-selector{height:12px;width:12px}.tox .tox-slider{align-items:center;display:flex;flex:1;-ms-flex-preferred-size:auto;height:24px;justify-content:center;position:relative}.tox .tox-slider__rail{background-color:transparent;border:1px solid #ccc;border-radius:3px;height:10px;min-width:120px;width:100%}.tox .tox-slider__handle{background-color:#207ab7;border:2px solid #185d8c;border-radius:3px;box-shadow:none;height:24px;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:14px}.tox .tox-source-code{overflow:auto}.tox .tox-spinner{display:flex}.tox .tox-spinner>div{animation:tam-bouncing-dots 1.5s ease-in-out 0s infinite both;background-color:rgba(34,47,62,.7);border-radius:100%;height:8px;width:8px}.tox .tox-spinner>div:nth-child(1){animation-delay:-.32s}.tox .tox-spinner>div:nth-child(2){animation-delay:-.16s}@keyframes tam-bouncing-dots{0%,100%,80%{transform:scale(0)}40%{transform:scale(1)}}.tox:not([dir=rtl]) .tox-spinner>div:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-spinner>div:not(:first-child){margin-right:4px}.tox .tox-statusbar{align-items:center;background-color:#fff;border-top:1px solid #ccc;color:rgba(34,47,62,.7);display:flex;flex:0 0 auto;font-size:12px;font-weight:400;height:18px;overflow:hidden;padding:0 8px;position:relative;text-transform:uppercase}.tox .tox-statusbar__text-container{display:flex;flex:1 1 auto;justify-content:flex-end;overflow:hidden}.tox .tox-statusbar__path{display:flex;flex:1 1 auto;margin-right:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tox .tox-statusbar__path>*{display:inline;white-space:nowrap}.tox .tox-statusbar__wordcount{flex:0 0 auto;margin-left:1ch}.tox .tox-statusbar a,.tox .tox-statusbar__path-item,.tox .tox-statusbar__wordcount{color:rgba(34,47,62,.7);text-decoration:none}.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]){cursor:pointer;text-decoration:underline}.tox .tox-statusbar__resize-handle{align-items:flex-end;align-self:stretch;cursor:nwse-resize;display:flex;flex:0 0 auto;justify-content:flex-end;margin-left:auto;margin-right:-8px;padding-left:1ch}.tox .tox-statusbar__resize-handle svg{display:block;fill:rgba(34,47,62,.7)}.tox .tox-statusbar__resize-handle:focus svg{background-color:#dee0e2;border-radius:1px;box-shadow:0 0 0 2px #dee0e2}.tox:not([dir=rtl]) .tox-statusbar__path>*{margin-right:4px}.tox:not([dir=rtl]) .tox-statusbar__branding{margin-left:1ch}.tox[dir=rtl] .tox-statusbar{flex-direction:row-reverse}.tox[dir=rtl] .tox-statusbar__path>*{margin-left:4px}.tox .tox-throbber{z-index:1299}.tox .tox-throbber__busy-spinner{align-items:center;background-color:rgba(255,255,255,.6);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}.tox .tox-tbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#222f3e;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0;text-transform:none;width:34px}.tox .tox-tbtn svg{display:block;fill:#222f3e}.tox .tox-tbtn.tox-tbtn-more{padding-left:5px;padding-right:5px;width:inherit}.tox .tox-tbtn:focus{background:#dee0e2;border:0;box-shadow:none}.tox .tox-tbtn:hover{background:#dee0e2;border:0;box-shadow:none;color:#222f3e}.tox .tox-tbtn:hover svg{fill:#222f3e}.tox .tox-tbtn:active{background:#c8cbcf;border:0;box-shadow:none;color:#222f3e}.tox .tox-tbtn:active svg{fill:#222f3e}.tox .tox-tbtn--disabled,.tox .tox-tbtn--disabled:hover,.tox .tox-tbtn:disabled,.tox .tox-tbtn:disabled:hover{background:0 0;border:0;box-shadow:none;color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-tbtn--disabled svg,.tox .tox-tbtn--disabled:hover svg,.tox .tox-tbtn:disabled svg,.tox .tox-tbtn:disabled:hover svg{fill:rgba(34,47,62,.5)}.tox .tox-tbtn--enabled,.tox .tox-tbtn--enabled:hover{background:#c8cbcf;border:0;box-shadow:none;color:#222f3e}.tox .tox-tbtn--enabled:hover>*,.tox .tox-tbtn--enabled>*{transform:none}.tox .tox-tbtn--enabled svg,.tox .tox-tbtn--enabled:hover svg{fill:#222f3e}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled){color:#222f3e}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg{fill:#222f3e}.tox .tox-tbtn:active>*{transform:none}.tox .tox-tbtn--md{height:51px;width:51px}.tox .tox-tbtn--lg{flex-direction:column;height:68px;width:68px}.tox .tox-tbtn--return{-ms-grid-row-align:stretch;align-self:stretch;height:unset;width:16px}.tox .tox-tbtn--labeled{padding:0 4px;width:unset}.tox .tox-tbtn__vlabel{display:block;font-size:10px;font-weight:400;letter-spacing:-.025em;margin-bottom:4px;white-space:nowrap}.tox .tox-tbtn--select{margin:2px 0 3px 0;padding:0 4px;width:auto}.tox .tox-tbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-tbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-tbtn__select-chevron svg{fill:rgba(34,47,62,.5)}.tox .tox-tbtn--bespoke .tox-tbtn__select-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:7em}.tox .tox-split-button{border:0;border-radius:3px;box-sizing:border-box;display:flex;margin:2px 0 3px 0;overflow:hidden}.tox .tox-split-button:hover{box-shadow:0 0 0 1px #dee0e2 inset}.tox .tox-split-button:focus{background:#dee0e2;box-shadow:none;color:#222f3e}.tox .tox-split-button>*{border-radius:0}.tox .tox-split-button__chevron{width:16px}.tox .tox-split-button__chevron svg{fill:rgba(34,47,62,.5)}.tox .tox-split-button .tox-tbtn{margin:0}.tox.tox-platform-touch .tox-split-button .tox-tbtn:first-child{width:30px}.tox.tox-platform-touch .tox-split-button__chevron{width:20px}.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus,.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,.tox .tox-split-button.tox-tbtn--disabled:focus,.tox .tox-split-button.tox-tbtn--disabled:hover{background:0 0;box-shadow:none;color:rgba(34,47,62,.5)}.tox .tox-toolbar-overlord{background-color:#fff}.tox .tox-toolbar,.tox .tox-toolbar__overflow,.tox .tox-toolbar__primary{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E") left 0 top 0 #fff;background-color:#fff;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 0}.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed{height:0;opacity:0;padding-bottom:0;padding-top:0;visibility:hidden}.tox .tox-toolbar__overflow--growing{transition:height .3s ease,opacity .2s linear .1s}.tox .tox-toolbar__overflow--shrinking{transition:opacity .3s ease,height .2s linear .1s,visibility 0s linear .3s}.tox .tox-menubar+.tox-toolbar,.tox .tox-menubar+.tox-toolbar-overlord .tox-toolbar__primary{border-top:1px solid #ccc;margin-top:-1px}.tox .tox-toolbar--scrolling{flex-wrap:nowrap;overflow-x:auto}.tox .tox-pop .tox-toolbar{border-width:0}.tox .tox-toolbar--no-divider{background-image:none}.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary,.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child{border-top:1px solid #ccc}.tox.tox-tinymce-aux .tox-toolbar__overflow{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15)}.tox .tox-toolbar__group{align-items:center;display:flex;flex-wrap:wrap;margin:0 0;padding:0 4px 0 4px}.tox .tox-toolbar__group--pull-right{margin-left:auto}.tox .tox-toolbar--scrolling .tox-toolbar__group{flex-shrink:0;flex-wrap:nowrap}.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type){border-right:1px solid #ccc}.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type){border-left:1px solid #ccc}.tox .tox-tooltip{display:inline-block;padding:8px;position:relative}.tox .tox-tooltip__body{background-color:#222f3e;border-radius:3px;box-shadow:0 2px 4px rgba(34,47,62,.3);color:rgba(255,255,255,.75);font-size:14px;font-style:normal;font-weight:400;padding:4px 8px;text-transform:none}.tox .tox-tooltip__arrow{position:absolute}.tox .tox-tooltip--down .tox-tooltip__arrow{border-left:8px solid transparent;border-right:8px solid transparent;border-top:8px solid #222f3e;bottom:0;left:50%;position:absolute;transform:translateX(-50%)}.tox .tox-tooltip--up .tox-tooltip__arrow{border-bottom:8px solid #222f3e;border-left:8px solid transparent;border-right:8px solid transparent;left:50%;position:absolute;top:0;transform:translateX(-50%)}.tox .tox-tooltip--right .tox-tooltip__arrow{border-bottom:8px solid transparent;border-left:8px solid #222f3e;border-top:8px solid transparent;position:absolute;right:0;top:50%;transform:translateY(-50%)}.tox .tox-tooltip--left .tox-tooltip__arrow{border-bottom:8px solid transparent;border-right:8px solid #222f3e;border-top:8px solid transparent;left:0;position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-well{border:1px solid #ccc;border-radius:3px;padding:8px;width:100%}.tox .tox-well>:first-child{margin-top:0}.tox .tox-well>:last-child{margin-bottom:0}.tox .tox-well>:only-child{margin:0}.tox .tox-custom-editor{border:1px solid #ccc;border-radius:3px;display:flex;flex:1;position:relative}.tox .tox-dialog-loading::before{background-color:rgba(0,0,0,.5);content:"";height:100%;position:absolute;width:100%;z-index:1000}.tox .tox-tab{cursor:pointer}.tox .tox-dialog__content-js{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-content .tox-collection{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-image-tools-edit-panel{height:60px}.tox .tox-image-tools__sidebar{height:60px} diff --git a/public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css b/public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css new file mode 100644 index 00000000..3a45cacf --- /dev/null +++ b/public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tinymce-mobile-outer-container{all:initial;display:block}.tinymce-mobile-outer-container *{border:0;box-sizing:initial;cursor:inherit;float:none;line-height:1;margin:0;outline:0;padding:0;-webkit-tap-highlight-color:transparent;text-shadow:none;white-space:nowrap}.tinymce-mobile-icon-arrow-back::before{content:"\e5cd"}.tinymce-mobile-icon-image::before{content:"\e412"}.tinymce-mobile-icon-cancel-circle::before{content:"\e5c9"}.tinymce-mobile-icon-full-dot::before{content:"\e061"}.tinymce-mobile-icon-align-center::before{content:"\e234"}.tinymce-mobile-icon-align-left::before{content:"\e236"}.tinymce-mobile-icon-align-right::before{content:"\e237"}.tinymce-mobile-icon-bold::before{content:"\e238"}.tinymce-mobile-icon-italic::before{content:"\e23f"}.tinymce-mobile-icon-unordered-list::before{content:"\e241"}.tinymce-mobile-icon-ordered-list::before{content:"\e242"}.tinymce-mobile-icon-font-size::before{content:"\e245"}.tinymce-mobile-icon-underline::before{content:"\e249"}.tinymce-mobile-icon-link::before{content:"\e157"}.tinymce-mobile-icon-unlink::before{content:"\eca2"}.tinymce-mobile-icon-color::before{content:"\e891"}.tinymce-mobile-icon-previous::before{content:"\e314"}.tinymce-mobile-icon-next::before{content:"\e315"}.tinymce-mobile-icon-large-font::before,.tinymce-mobile-icon-style-formats::before{content:"\e264"}.tinymce-mobile-icon-undo::before{content:"\e166"}.tinymce-mobile-icon-redo::before{content:"\e15a"}.tinymce-mobile-icon-removeformat::before{content:"\e239"}.tinymce-mobile-icon-small-font::before{content:"\e906"}.tinymce-mobile-format-matches::after,.tinymce-mobile-icon-readonly-back::before{content:"\e5ca"}.tinymce-mobile-icon-small-heading::before{content:"small"}.tinymce-mobile-icon-large-heading::before{content:"large"}.tinymce-mobile-icon-large-heading::before,.tinymce-mobile-icon-small-heading::before{font-family:sans-serif;font-size:80%}.tinymce-mobile-mask-edit-icon::before{content:"\e254"}.tinymce-mobile-icon-back::before{content:"\e5c4"}.tinymce-mobile-icon-heading::before{content:"Headings";font-family:sans-serif;font-size:80%;font-weight:700}.tinymce-mobile-icon-h1::before{content:"H1";font-weight:700}.tinymce-mobile-icon-h2::before{content:"H2";font-weight:700}.tinymce-mobile-icon-h3::before{content:"H3";font-weight:700}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask{align-items:center;display:flex;justify-content:center;background:rgba(51,51,51,.5);height:100%;position:absolute;top:0;width:100%}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container{align-items:center;border-radius:50%;display:flex;flex-direction:column;font-family:sans-serif;font-size:1em;justify-content:space-between}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{align-items:center;display:flex;justify-content:center;flex-direction:column;font-size:1em}@media only screen and (min-device-width:700px){.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{font-size:1.2em}}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em;background-color:#fff;color:#207ab7}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before{content:"\e900";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon{z-index:2}.tinymce-mobile-android-container.tinymce-mobile-android-maximized{background:#fff;border:none;bottom:0;display:flex;flex-direction:column;left:0;position:fixed;right:0;top:0}.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized){position:relative}.tinymce-mobile-android-container .tinymce-mobile-editor-socket{display:flex;flex-grow:1}.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe{display:flex!important;flex-grow:1;height:auto!important}.tinymce-mobile-android-scroll-reload{overflow:hidden}:not(.tinymce-mobile-readonly-mode)>.tinymce-mobile-android-selection-context-toolbar{margin-top:23px}.tinymce-mobile-toolstrip{background:#fff;display:flex;flex:0 0 auto;z-index:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar{align-items:center;background-color:#fff;border-bottom:1px solid #ccc;display:flex;flex:1;height:2.5em;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex-shrink:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container{background:#f44336}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group{flex-grow:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button{align-items:center;display:flex;height:80%;margin-left:2px;margin-right:2px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected{background:#c8cbcf;color:#ccc}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type{background:#207ab7;color:#eceff1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex:1;padding-bottom:.4em;padding-top:.4em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog{display:flex;min-height:1.5em;overflow:hidden;padding-left:0;padding-right:0;position:relative;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain{display:flex;height:100%;transition:left cubic-bezier(.4,0,1,1) .15s;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen{display:flex;flex:0 0 auto;justify-content:space-between;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input{font-family:Sans-serif}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container{display:flex;flex-grow:1;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x{-ms-grid-row-align:center;align-self:center;background:inherit;border:none;border-radius:50%;color:#888;font-size:.6em;font-weight:700;height:100%;padding-right:2px;position:absolute;right:0}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x{display:none}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before{align-items:center;display:flex;font-weight:700;height:100%;padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before{visibility:hidden}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item{color:#ccc;font-size:10px;line-height:10px;margin:0 2px;padding-top:3px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active{color:#c8cbcf}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before{margin-left:.5em;margin-right:.9em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before{margin-left:.9em;margin-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider{display:flex;flex:1;margin-left:0;margin-right:0;padding:.28em 0;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line{background:#ccc;display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container{padding-left:2em;padding-right:2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient{background:linear-gradient(to right,red 0,#feff00 17%,#0f0 33%,#00feff 50%,#00f 67%,#ff00fe 83%,red 100%);display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black{background:#000;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white{background:#fff;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb{align-items:center;background-clip:padding-box;background-color:#455a64;border:.5em solid rgba(136,136,136,0);border-radius:3em;bottom:0;color:#fff;display:flex;height:.5em;justify-content:center;left:-10px;margin:auto;position:absolute;top:0;transition:border 120ms cubic-bezier(.39,.58,.57,1);width:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active{border:.5em solid rgba(136,136,136,.39)}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper{flex-direction:column;justify-content:center}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog){height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container{display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input{background:#fff;border:none;border-radius:0;color:#455a64;flex-grow:1;font-size:.85em;padding-bottom:.1em;padding-left:5px;padding-top:.1em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder{color:#888}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder{color:#888}.tinymce-mobile-dropup{background:#fff;display:flex;overflow:hidden;width:100%}.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking{transition:height .3s ease-out}.tinymce-mobile-dropup.tinymce-mobile-dropup-growing{transition:height .3s ease-in}.tinymce-mobile-dropup.tinymce-mobile-dropup-closed{flex-grow:0}.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing){flex-grow:1}.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}@media only screen and (orientation:landscape){.tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:150px}}.tinymce-mobile-styles-menu{font-family:sans-serif;outline:4px solid #000;overflow:hidden;position:relative;width:100%}.tinymce-mobile-styles-menu [role=menu]{display:flex;flex-direction:column;height:100%;position:absolute;width:100%}.tinymce-mobile-styles-menu [role=menu].transitioning{transition:transform .5s ease-in-out}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item{border-bottom:1px solid #ddd;color:#455a64;cursor:pointer;display:flex;padding:1em 1em;position:relative}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before{color:#455a64;content:"\e314";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after{color:#455a64;content:"\e315";font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after{font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser,.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator{align-items:center;background:#fff;border-top:#455a64;color:#455a64;display:flex;min-height:2.5em;padding-left:1em;padding-right:1em}.tinymce-mobile-styles-menu [data-transitioning-destination=before][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=before]{transform:translate(-100%)}.tinymce-mobile-styles-menu [data-transitioning-destination=current][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=current]{transform:translate(0)}.tinymce-mobile-styles-menu [data-transitioning-destination=after][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=after]{transform:translate(100%)}@font-face{font-family:tinymce-mobile;font-style:normal;font-weight:400;src:url(fonts/tinymce-mobile.woff?8x92w3) format('woff')}@media (min-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:25px}}@media (max-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:18px}}.tinymce-mobile-icon{font-family:tinymce-mobile,sans-serif}.mixin-flex-and-centre{align-items:center;display:flex;justify-content:center}.mixin-flex-bar{align-items:center;display:flex;height:100%}.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe{background-color:#fff;width:100%}.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{background-color:#207ab7;border-radius:50%;bottom:1em;color:#fff;font-size:1em;height:2.1em;position:fixed;right:2em;width:2.1em;align-items:center;display:flex;justify-content:center}@media only screen and (min-device-width:700px){.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{font-size:1.2em}}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket{height:300px;overflow:hidden}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe{height:100%}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip{display:none}input[type=file]::-webkit-file-upload-button{display:none}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{bottom:50%}} diff --git a/public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css b/public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css new file mode 100644 index 00000000..a0893b91 --- /dev/null +++ b/public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201} diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 00000000..4b16f57a --- /dev/null +++ b/src/App.vue @@ -0,0 +1,27 @@ + + + diff --git a/src/api/demo/account.ts b/src/api/demo/account.ts new file mode 100644 index 00000000..f37e034e --- /dev/null +++ b/src/api/demo/account.ts @@ -0,0 +1,22 @@ +import { defHttp } from '@/utils/http/axios' +import { GetAccountInfoModel } from './model/accountModel' + +enum Api { + ACCOUNT_INFO = '/account/getAccountInfo', + SESSION_TIMEOUT = '/user/sessionTimeout', + TOKEN_EXPIRED = '/user/tokenExpired' +} + +// Get personal center-basic settings + +export const accountInfoApi = () => { + return defHttp.get({ url: Api.ACCOUNT_INFO }) +} + +export const sessionTimeoutApi = () => { + return defHttp.post({ url: Api.SESSION_TIMEOUT }) +} + +export const tokenExpiredApi = () => { + return defHttp.post({ url: Api.TOKEN_EXPIRED }) +} diff --git a/src/api/demo/cascader.ts b/src/api/demo/cascader.ts new file mode 100644 index 00000000..328a17e3 --- /dev/null +++ b/src/api/demo/cascader.ts @@ -0,0 +1,10 @@ +import { defHttp } from '@/utils/http/axios' +import { AreaModel, AreaParams } from '@/api/demo/model/areaModel' + +enum Api { + AREA_RECORD = '/cascader/getAreaRecord' +} + +export const areaRecord = (data: AreaParams) => { + return defHttp.post({ url: Api.AREA_RECORD, data }) +} diff --git a/src/api/demo/error.ts b/src/api/demo/error.ts new file mode 100644 index 00000000..fed41f24 --- /dev/null +++ b/src/api/demo/error.ts @@ -0,0 +1,14 @@ +import { defHttp } from '@/utils/http/axios' + +enum Api { + // The address does not exist + Error = '/error' +} + +/** + * @description: Trigger ajax error + */ + +export const fireErrorApi = () => { + return defHttp.get({ url: Api.Error }) +} diff --git a/src/api/demo/model/accountModel.ts b/src/api/demo/model/accountModel.ts new file mode 100644 index 00000000..06c48888 --- /dev/null +++ b/src/api/demo/model/accountModel.ts @@ -0,0 +1,7 @@ +export interface GetAccountInfoModel { + email: string + name: string + introduction: string + phone: string + address: string +} diff --git a/src/api/demo/model/areaModel.ts b/src/api/demo/model/areaModel.ts new file mode 100644 index 00000000..ac40079b --- /dev/null +++ b/src/api/demo/model/areaModel.ts @@ -0,0 +1,12 @@ +export interface AreaModel { + id: string + code: string + parentCode: string + name: string + levelType: number + [key: string]: string | number +} + +export interface AreaParams { + parentCode: string +} diff --git a/src/api/demo/model/optionsModel.ts b/src/api/demo/model/optionsModel.ts new file mode 100644 index 00000000..5eb42b69 --- /dev/null +++ b/src/api/demo/model/optionsModel.ts @@ -0,0 +1,15 @@ +import { BasicFetchResult } from '@/api/model/baseModel' + +export interface DemoOptionsItem { + label: string + value: string +} + +export interface selectParams { + id: number | string +} + +/** + * @description: Request list return value + */ +export type DemoOptionsGetResultModel = BasicFetchResult diff --git a/src/api/demo/model/systemModel.ts b/src/api/demo/model/systemModel.ts new file mode 100644 index 00000000..b88b878d --- /dev/null +++ b/src/api/demo/model/systemModel.ts @@ -0,0 +1,74 @@ +import { BasicPageParams, BasicFetchResult } from '@/api/model/baseModel' + +export type AccountParams = BasicPageParams & { + account?: string + nickname?: string +} + +export type RoleParams = { + roleName?: string + status?: string +} + +export type RolePageParams = BasicPageParams & RoleParams + +export type DeptParams = { + deptName?: string + status?: string +} + +export type MenuParams = { + menuName?: string + status?: string +} + +export interface AccountListItem { + id: string + account: string + email: string + nickname: string + role: number + createTime: string + remark: string + status: number +} + +export interface DeptListItem { + id: string + orderNo: string + createTime: string + remark: string + status: number +} + +export interface MenuListItem { + id: string + orderNo: string + createTime: string + status: number + icon: string + component: string + permission: string +} + +export interface RoleListItem { + id: string + roleName: string + roleValue: string + status: number + orderNo: string + createTime: string +} + +/** + * @description: Request list return value + */ +export type AccountListGetResultModel = BasicFetchResult + +export type DeptListGetResultModel = BasicFetchResult + +export type MenuListGetResultModel = BasicFetchResult + +export type RolePageListGetResultModel = BasicFetchResult + +export type RoleListGetResultModel = RoleListItem[] diff --git a/src/api/demo/model/tableModel.ts b/src/api/demo/model/tableModel.ts new file mode 100644 index 00000000..1aa255ca --- /dev/null +++ b/src/api/demo/model/tableModel.ts @@ -0,0 +1,20 @@ +import { BasicPageParams, BasicFetchResult } from '@/api/model/baseModel' +/** + * @description: Request list interface parameters + */ +export type DemoParams = BasicPageParams + +export interface DemoListItem { + id: string + beginTime: string + endTime: string + address: string + name: string + no: number + status: number +} + +/** + * @description: Request list return value + */ +export type DemoListGetResultModel = BasicFetchResult diff --git a/src/api/demo/select.ts b/src/api/demo/select.ts new file mode 100644 index 00000000..4acd4e4f --- /dev/null +++ b/src/api/demo/select.ts @@ -0,0 +1,12 @@ +import { defHttp } from '@/utils/http/axios' +import { DemoOptionsItem, selectParams } from './model/optionsModel' +enum Api { + OPTIONS_LIST = '/select/getDemoOptions' +} + +/** + * @description: Get sample options value + */ +export const optionsListApi = (params?: selectParams) => { + return defHttp.get({ url: Api.OPTIONS_LIST, params }) +} diff --git a/src/api/demo/system.ts b/src/api/demo/system.ts new file mode 100644 index 00000000..3902ff86 --- /dev/null +++ b/src/api/demo/system.ts @@ -0,0 +1,51 @@ +import { + AccountParams, + DeptListItem, + MenuParams, + RoleParams, + RolePageParams, + MenuListGetResultModel, + DeptListGetResultModel, + AccountListGetResultModel, + RolePageListGetResultModel, + RoleListGetResultModel +} from './model/systemModel' +import { defHttp } from '@/utils/http/axios' + +enum Api { + AccountList = '/system/getAccountList', + IsAccountExist = '/system/accountExist', + DeptList = '/system/getDeptList', + setRoleStatus = '/system/setRoleStatus', + MenuList = '/system/getMenuList', + RolePageList = '/system/getRoleListByPage', + GetAllRoleList = '/system/getAllRoleList' +} + +export const getAccountList = (params: AccountParams) => { + return defHttp.get({ url: Api.AccountList, params }) +} + +export const getDeptList = (params?: DeptListItem) => { + return defHttp.get({ url: Api.DeptList, params }) +} + +export const getMenuList = (params?: MenuParams) => { + return defHttp.get({ url: Api.MenuList, params }) +} + +export const getRoleListByPage = (params?: RolePageParams) => { + return defHttp.get({ url: Api.RolePageList, params }) +} + +export const getAllRoleList = (params?: RoleParams) => { + return defHttp.get({ url: Api.GetAllRoleList, params }) +} + +export const setRoleStatus = (id: number, status: string) => { + return defHttp.post({ url: Api.setRoleStatus, params: { id, status } }) +} + +export const isAccountExist = (account: string) => { + return defHttp.post({ url: Api.IsAccountExist, params: { account } }, { errorMessageMode: 'none' }) +} diff --git a/src/api/demo/table.ts b/src/api/demo/table.ts new file mode 100644 index 00000000..9affd4d7 --- /dev/null +++ b/src/api/demo/table.ts @@ -0,0 +1,21 @@ +import { defHttp } from '@/utils/http/axios' +import { DemoParams, DemoListGetResultModel } from './model/tableModel' + +enum Api { + DEMO_LIST = '/table/getDemoList' +} + +/** + * @description: Get sample list value + */ + +export const demoListApi = (params: DemoParams) => { + return defHttp.get({ + url: Api.DEMO_LIST, + params, + headers: { + // @ts-ignore + ignoreCancelToken: true + } + }) +} diff --git a/src/api/demo/tree.ts b/src/api/demo/tree.ts new file mode 100644 index 00000000..c8219103 --- /dev/null +++ b/src/api/demo/tree.ts @@ -0,0 +1,12 @@ +import { defHttp } from '@/utils/http/axios' + +enum Api { + TREE_OPTIONS_LIST = '/tree/getDemoOptions' +} + +/** + * @description: Get sample options value + */ +export const treeOptionsListApi = (params?: Recordable) => { + return defHttp.get({ url: Api.TREE_OPTIONS_LIST, params }) +} diff --git a/src/api/model/baseModel.ts b/src/api/model/baseModel.ts new file mode 100644 index 00000000..076b9868 --- /dev/null +++ b/src/api/model/baseModel.ts @@ -0,0 +1,9 @@ +export interface BasicPageParams { + page: number + pageSize: number +} + +export interface BasicFetchResult { + items: T[] + total: number +} diff --git a/src/api/sys/menu.ts b/src/api/sys/menu.ts new file mode 100644 index 00000000..af25b3a1 --- /dev/null +++ b/src/api/sys/menu.ts @@ -0,0 +1,14 @@ +import { defHttp } from '@/utils/http/axios' +import { getMenuListResultModel } from './model/menuModel' + +enum Api { + GetMenuList = '/getMenuList' +} + +/** + * @description: Get user menu based on id + */ + +export const getMenuList = () => { + return defHttp.get({ url: Api.GetMenuList }) +} diff --git a/src/api/sys/model/menuModel.ts b/src/api/sys/model/menuModel.ts new file mode 100644 index 00000000..2d7789fe --- /dev/null +++ b/src/api/sys/model/menuModel.ts @@ -0,0 +1,16 @@ +import type { RouteMeta } from 'vue-router' +export interface RouteItem { + path: string + component: any + meta: RouteMeta + name?: string + alias?: string | string[] + redirect?: string + caseSensitive?: boolean + children?: RouteItem[] +} + +/** + * @description: Get menu return value + */ +export type getMenuListResultModel = RouteItem[] diff --git a/src/api/sys/model/uploadModel.ts b/src/api/sys/model/uploadModel.ts new file mode 100644 index 00000000..4d1698fa --- /dev/null +++ b/src/api/sys/model/uploadModel.ts @@ -0,0 +1,5 @@ +export interface UploadApiResult { + message: string + code: number + url: string +} diff --git a/src/api/sys/model/userModel.ts b/src/api/sys/model/userModel.ts new file mode 100644 index 00000000..3e3dc97b --- /dev/null +++ b/src/api/sys/model/userModel.ts @@ -0,0 +1,38 @@ +/** + * @description: Login interface parameters + */ +export interface LoginParams { + username: string + password: string +} + +export interface RoleInfo { + roleName: string + value: string +} + +/** + * @description: Login interface return value + */ +export interface LoginResultModel { + userId: string | number + token: string + role: RoleInfo +} + +/** + * @description: Get user information return value + */ +export interface GetUserInfoModel { + roles: RoleInfo[] + // 用户id + userId: string | number + // 用户名 + username: string + // 真实名字 + realName: string + // 头像 + avatar: string + // 介绍 + desc?: string +} diff --git a/src/api/sys/upload.ts b/src/api/sys/upload.ts new file mode 100644 index 00000000..2e88b065 --- /dev/null +++ b/src/api/sys/upload.ts @@ -0,0 +1,19 @@ +import { UploadApiResult } from './model/uploadModel' +import { defHttp } from '@/utils/http/axios' +import { UploadFileParams } from '@/types/axios' +import { useGlobSetting } from '@/hooks/setting' + +const { uploadUrl = '' } = useGlobSetting() + +/** + * @description: Upload interface + */ +export function uploadApi(params: UploadFileParams, onUploadProgress: (progressEvent: ProgressEvent) => void) { + return defHttp.uploadFile( + { + url: uploadUrl, + onUploadProgress + }, + params + ) +} diff --git a/src/api/sys/user.ts b/src/api/sys/user.ts new file mode 100644 index 00000000..d1f43d1c --- /dev/null +++ b/src/api/sys/user.ts @@ -0,0 +1,55 @@ +import { defHttp } from '@/utils/http/axios' +import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userModel' + +import { ErrorMessageMode } from '@/types/axios' + +enum Api { + Login = '/login', + Logout = '/logout', + GetUserInfo = '/getUserInfo', + GetPermCode = '/getPermCode', + TestRetry = '/testRetry' +} + +/** + * @description: user login api + */ +export const loginApi = (params: LoginParams, mode: ErrorMessageMode = 'modal') => { + return defHttp.post( + { + url: Api.Login, + params + }, + { + errorMessageMode: mode + } + ) +} + +/** + * @description: getUserInfo + */ +export const getUserInfo = () => { + return defHttp.get({ url: Api.GetUserInfo }, { errorMessageMode: 'none' }) +} + +export const getPermCode = () => { + return defHttp.get({ url: Api.GetPermCode }) +} + +export const doLogout = () => { + return defHttp.get({ url: Api.Logout }) +} + +export const testRetry = () => { + return defHttp.get( + { url: Api.TestRetry }, + { + retryRequest: { + isOpenRetry: true, + count: 5, + waitTime: 1000 + } + } + ) +} diff --git a/src/assets/icons/download-count.svg b/src/assets/icons/download-count.svg new file mode 100644 index 00000000..1c951958 --- /dev/null +++ b/src/assets/icons/download-count.svg @@ -0,0 +1 @@ +Asset 91 \ No newline at end of file diff --git a/src/assets/icons/dynamic-avatar-1.svg b/src/assets/icons/dynamic-avatar-1.svg new file mode 100644 index 00000000..e1553e50 --- /dev/null +++ b/src/assets/icons/dynamic-avatar-1.svg @@ -0,0 +1 @@ +Asset 15 \ No newline at end of file diff --git a/src/assets/icons/dynamic-avatar-2.svg b/src/assets/icons/dynamic-avatar-2.svg new file mode 100644 index 00000000..c4c17223 --- /dev/null +++ b/src/assets/icons/dynamic-avatar-2.svg @@ -0,0 +1 @@ +Asset 16 \ No newline at end of file diff --git a/src/assets/icons/dynamic-avatar-3.svg b/src/assets/icons/dynamic-avatar-3.svg new file mode 100644 index 00000000..81145f9b --- /dev/null +++ b/src/assets/icons/dynamic-avatar-3.svg @@ -0,0 +1 @@ +Asset 17 \ No newline at end of file diff --git a/src/assets/icons/dynamic-avatar-4.svg b/src/assets/icons/dynamic-avatar-4.svg new file mode 100644 index 00000000..e586ed4e --- /dev/null +++ b/src/assets/icons/dynamic-avatar-4.svg @@ -0,0 +1 @@ +Asset 120 \ No newline at end of file diff --git a/src/assets/icons/dynamic-avatar-5.svg b/src/assets/icons/dynamic-avatar-5.svg new file mode 100644 index 00000000..746e4b88 --- /dev/null +++ b/src/assets/icons/dynamic-avatar-5.svg @@ -0,0 +1 @@ +Asset 110 \ No newline at end of file diff --git a/src/assets/icons/dynamic-avatar-6.svg b/src/assets/icons/dynamic-avatar-6.svg new file mode 100644 index 00000000..b2432f22 --- /dev/null +++ b/src/assets/icons/dynamic-avatar-6.svg @@ -0,0 +1 @@ +Asset 100 \ No newline at end of file diff --git a/src/assets/icons/moon.svg b/src/assets/icons/moon.svg new file mode 100644 index 00000000..e6667f0d --- /dev/null +++ b/src/assets/icons/moon.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/assets/icons/sun.svg b/src/assets/icons/sun.svg new file mode 100644 index 00000000..a3997cbf --- /dev/null +++ b/src/assets/icons/sun.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/icons/test.svg b/src/assets/icons/test.svg new file mode 100644 index 00000000..244252dd --- /dev/null +++ b/src/assets/icons/test.svg @@ -0,0 +1,21 @@ + + + + Icon1@3x + Created with Sketch. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/icons/total-sales.svg b/src/assets/icons/total-sales.svg new file mode 100644 index 00000000..eff79640 --- /dev/null +++ b/src/assets/icons/total-sales.svg @@ -0,0 +1 @@ +Asset 500 \ No newline at end of file diff --git a/src/assets/icons/transaction.svg b/src/assets/icons/transaction.svg new file mode 100644 index 00000000..7ba9e2f0 --- /dev/null +++ b/src/assets/icons/transaction.svg @@ -0,0 +1 @@ +Asset 480% \ No newline at end of file diff --git a/src/assets/icons/visit-count.svg b/src/assets/icons/visit-count.svg new file mode 100644 index 00000000..ba2a3061 --- /dev/null +++ b/src/assets/icons/visit-count.svg @@ -0,0 +1 @@ +Asset 510 \ No newline at end of file diff --git a/src/assets/images/demo.png b/src/assets/images/demo.png new file mode 100644 index 0000000000000000000000000000000000000000..1a45c9835b7b2c708c114b04fb445e6ef00d8827 GIT binary patch literal 33342 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 K^riIu-2VXLS6svZ literal 0 HcmV?d00001 diff --git a/src/assets/images/logo.png b/src/assets/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..16f2e7ab7c74552c5653652547ebce7adc4901dc GIT binary patch literal 28304 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 literal 0 HcmV?d00001 diff --git a/src/assets/svg/illustration.svg b/src/assets/svg/illustration.svg new file mode 100644 index 00000000..b45215b0 --- /dev/null +++ b/src/assets/svg/illustration.svg @@ -0,0 +1 @@ +Asset 336 \ No newline at end of file diff --git a/src/assets/svg/login-bg-dark.svg b/src/assets/svg/login-bg-dark.svg new file mode 100644 index 00000000..888da7af --- /dev/null +++ b/src/assets/svg/login-bg-dark.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svg/login-bg.svg b/src/assets/svg/login-bg.svg new file mode 100644 index 00000000..7b66bafc --- /dev/null +++ b/src/assets/svg/login-bg.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/svg/login-box-bg.svg b/src/assets/svg/login-box-bg.svg new file mode 100644 index 00000000..ee7dbdc2 --- /dev/null +++ b/src/assets/svg/login-box-bg.svg @@ -0,0 +1 @@ +responsive \ No newline at end of file diff --git a/src/assets/svg/net-error.svg b/src/assets/svg/net-error.svg new file mode 100644 index 00000000..81f20044 --- /dev/null +++ b/src/assets/svg/net-error.svg @@ -0,0 +1 @@ +personal settings \ No newline at end of file diff --git a/src/assets/svg/no-data.svg b/src/assets/svg/no-data.svg new file mode 100644 index 00000000..2b9f2570 --- /dev/null +++ b/src/assets/svg/no-data.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svg/preview/p-rotate.svg b/src/assets/svg/preview/p-rotate.svg new file mode 100644 index 00000000..5153a816 --- /dev/null +++ b/src/assets/svg/preview/p-rotate.svg @@ -0,0 +1 @@ + diff --git a/src/assets/svg/preview/resume.svg b/src/assets/svg/preview/resume.svg new file mode 100644 index 00000000..0e86c5f6 --- /dev/null +++ b/src/assets/svg/preview/resume.svg @@ -0,0 +1 @@ + diff --git a/src/assets/svg/preview/scale.svg b/src/assets/svg/preview/scale.svg new file mode 100644 index 00000000..1f7adaee --- /dev/null +++ b/src/assets/svg/preview/scale.svg @@ -0,0 +1 @@ + diff --git a/src/assets/svg/preview/unrotate.svg b/src/assets/svg/preview/unrotate.svg new file mode 100644 index 00000000..e4708be1 --- /dev/null +++ b/src/assets/svg/preview/unrotate.svg @@ -0,0 +1 @@ + diff --git a/src/assets/svg/preview/unscale.svg b/src/assets/svg/preview/unscale.svg new file mode 100644 index 00000000..1359b34c --- /dev/null +++ b/src/assets/svg/preview/unscale.svg @@ -0,0 +1 @@ + diff --git a/src/components/Application/index.ts b/src/components/Application/index.ts new file mode 100644 index 00000000..e44bab72 --- /dev/null +++ b/src/components/Application/index.ts @@ -0,0 +1,17 @@ +import { withInstall } from '@/utils' + +import appLogo from './src/AppLogo.vue' +import appProvider from './src/AppProvider.vue' +import appSearch from './src/search/AppSearch.vue' +import appSizePicker from './src/AppSizePicker.vue' +import appLocalePicker from './src/AppLocalePicker.vue' +import appDarkModeToggle from './src/AppDarkModeToggle.vue' + +export { useAppProviderContext } from './src/useAppContext' + +export const AppLogo = withInstall(appLogo) +export const AppProvider = withInstall(appProvider) +export const AppSearch = withInstall(appSearch) +export const AppSizePicker = withInstall(appSizePicker) +export const AppLocalePicker = withInstall(appLocalePicker) +export const AppDarkModeToggle = withInstall(appDarkModeToggle) diff --git a/src/components/Application/src/AppDarkModeToggle.vue b/src/components/Application/src/AppDarkModeToggle.vue new file mode 100644 index 00000000..e50aa2ca --- /dev/null +++ b/src/components/Application/src/AppDarkModeToggle.vue @@ -0,0 +1,76 @@ + + + diff --git a/src/components/Application/src/AppLocalePicker.vue b/src/components/Application/src/AppLocalePicker.vue new file mode 100644 index 00000000..4cff0d21 --- /dev/null +++ b/src/components/Application/src/AppLocalePicker.vue @@ -0,0 +1,72 @@ + + + + diff --git a/src/components/Application/src/AppLogo.vue b/src/components/Application/src/AppLogo.vue new file mode 100644 index 00000000..5728c038 --- /dev/null +++ b/src/components/Application/src/AppLogo.vue @@ -0,0 +1,85 @@ + + + diff --git a/src/components/Application/src/AppProvider.vue b/src/components/Application/src/AppProvider.vue new file mode 100644 index 00000000..3b0fb23e --- /dev/null +++ b/src/components/Application/src/AppProvider.vue @@ -0,0 +1,77 @@ + diff --git a/src/components/Application/src/AppSizePicker.vue b/src/components/Application/src/AppSizePicker.vue new file mode 100644 index 00000000..6dd7ca02 --- /dev/null +++ b/src/components/Application/src/AppSizePicker.vue @@ -0,0 +1,68 @@ + + + + diff --git a/src/components/Application/src/search/AppSearch.vue b/src/components/Application/src/search/AppSearch.vue new file mode 100644 index 00000000..bc7ca9b3 --- /dev/null +++ b/src/components/Application/src/search/AppSearch.vue @@ -0,0 +1,33 @@ + diff --git a/src/components/Application/src/search/AppSearchFooter.vue b/src/components/Application/src/search/AppSearchFooter.vue new file mode 100644 index 00000000..8174d359 --- /dev/null +++ b/src/components/Application/src/search/AppSearchFooter.vue @@ -0,0 +1,55 @@ + + + + diff --git a/src/components/Application/src/search/AppSearchKeyItem.vue b/src/components/Application/src/search/AppSearchKeyItem.vue new file mode 100644 index 00000000..1cf0057f --- /dev/null +++ b/src/components/Application/src/search/AppSearchKeyItem.vue @@ -0,0 +1,11 @@ + + diff --git a/src/components/Application/src/search/AppSearchModal.vue b/src/components/Application/src/search/AppSearchModal.vue new file mode 100644 index 00000000..550e9a2d --- /dev/null +++ b/src/components/Application/src/search/AppSearchModal.vue @@ -0,0 +1,259 @@ + + + + diff --git a/src/components/Application/src/search/useMenuSearch.ts b/src/components/Application/src/search/useMenuSearch.ts new file mode 100644 index 00000000..304e13aa --- /dev/null +++ b/src/components/Application/src/search/useMenuSearch.ts @@ -0,0 +1,166 @@ +import type { Menu } from '@/router/types' +import { ref, onBeforeMount, unref, Ref, nextTick } from 'vue' +import { getMenus } from '@/router/menus' +import { cloneDeep } from 'lodash-es' +import { filter, forEach } from '@/utils/helper/treeHelper' +import { useGo } from '@/hooks/web/usePage' +import { useScrollTo } from '@/hooks/event/useScrollTo' +import { onKeyStroke, useDebounceFn } from '@vueuse/core' +import { useI18n } from '@/hooks/web/useI18n' + +export interface SearchResult { + name: string + path: string + icon?: string +} + +// Translate special characters +function transform(c: string) { + const code: string[] = ['$', '(', ')', '*', '+', '.', '[', ']', '?', '\\', '^', '{', '}', '|'] + return code.includes(c) ? `\\${c}` : c +} + +function createSearchReg(key: string) { + const keys = [...key].map((item) => transform(item)) + const str = ['', ...keys, ''].join('.*') + return new RegExp(str) +} + +export function useMenuSearch(refs: Ref, scrollWrap: Ref, emit: EmitType) { + const searchResult = ref([]) + const keyword = ref('') + const activeIndex = ref(-1) + + let menuList: Menu[] = [] + + const { t } = useI18n() + const go = useGo() + const handleSearch = useDebounceFn(search, 200) + + onBeforeMount(async () => { + const list = await getMenus() + menuList = cloneDeep(list) + forEach(menuList, (item) => { + item.name = t(item.name) + }) + }) + + function search(e: ChangeEvent) { + e?.stopPropagation() + const key = e.target.value + keyword.value = key.trim() + if (!key) { + searchResult.value = [] + return + } + const reg = createSearchReg(unref(keyword)) + const filterMenu = filter(menuList, (item) => { + return reg.test(item.name) && !item.hideMenu + }) + searchResult.value = handlerSearchResult(filterMenu, reg) + activeIndex.value = 0 + } + + function handlerSearchResult(filterMenu: Menu[], reg: RegExp, parent?: Menu) { + const ret: SearchResult[] = [] + filterMenu.forEach((item) => { + const { name, path, icon, children, hideMenu, meta } = item + if (!hideMenu && reg.test(name) && (!children?.length || meta?.hideChildrenInMenu)) { + ret.push({ + name: parent?.name ? `${parent.name} > ${name}` : name, + path, + icon + }) + } + if (!meta?.hideChildrenInMenu && Array.isArray(children) && children.length) { + ret.push(...handlerSearchResult(children, reg, item)) + } + }) + return ret + } + + // Activate when the mouse moves to a certain line + function handleMouseenter(e: any) { + const index = e.target.dataset.index + activeIndex.value = Number(index) + } + + // Arrow key up + function handleUp() { + if (!searchResult.value.length) return + activeIndex.value-- + if (activeIndex.value < 0) { + activeIndex.value = searchResult.value.length - 1 + } + handleScroll() + } + + // Arrow key down + function handleDown() { + if (!searchResult.value.length) return + activeIndex.value++ + if (activeIndex.value > searchResult.value.length - 1) { + activeIndex.value = 0 + } + handleScroll() + } + + // When the keyboard up and down keys move to an invisible place + // the scroll bar needs to scroll automatically + function handleScroll() { + const refList = unref(refs) + if (!refList || !Array.isArray(refList) || refList.length === 0 || !unref(scrollWrap)) { + return + } + + const index = unref(activeIndex) + const currentRef = refList[index] + if (!currentRef) { + return + } + const wrapEl = unref(scrollWrap) + if (!wrapEl) { + return + } + const scrollHeight = currentRef.offsetTop + currentRef.offsetHeight + const wrapHeight = wrapEl.offsetHeight + const { start } = useScrollTo({ + el: wrapEl, + duration: 100, + to: scrollHeight - wrapHeight + }) + start() + } + + // enter keyboard event + async function handleEnter() { + if (!searchResult.value.length) { + return + } + const result = unref(searchResult) + const index = unref(activeIndex) + if (result.length === 0 || index < 0) { + return + } + const to = result[index] + handleClose() + await nextTick() + go(to.path) + } + + // close search modal + function handleClose() { + searchResult.value = [] + emit('close') + } + + // enter search + onKeyStroke('Enter', handleEnter) + // Monitor keyboard arrow keys + onKeyStroke('ArrowUp', handleUp) + onKeyStroke('ArrowDown', handleDown) + // esc close + onKeyStroke('Escape', handleClose) + + return { handleSearch, searchResult, keyword, activeIndex, handleMouseenter, handleEnter } +} diff --git a/src/components/Application/src/useAppContext.ts b/src/components/Application/src/useAppContext.ts new file mode 100644 index 00000000..07115b15 --- /dev/null +++ b/src/components/Application/src/useAppContext.ts @@ -0,0 +1,17 @@ +import { InjectionKey, Ref } from 'vue' +import { createContext, useContext } from '@/hooks/core/useContext' + +export interface AppProviderContextProps { + prefixCls: Ref + isMobile: Ref +} + +const key: InjectionKey = Symbol() + +export function createAppProviderContext(context: AppProviderContextProps) { + return createContext(context, key) +} + +export function useAppProviderContext() { + return useContext(key) +} diff --git a/src/components/Authority/index.ts b/src/components/Authority/index.ts new file mode 100644 index 00000000..53681ddd --- /dev/null +++ b/src/components/Authority/index.ts @@ -0,0 +1,4 @@ +import { withInstall } from '@/utils' +import authority from './src/Authority.vue' + +export const Authority = withInstall(authority) diff --git a/src/components/Authority/src/Authority.vue b/src/components/Authority/src/Authority.vue new file mode 100644 index 00000000..96e8811b --- /dev/null +++ b/src/components/Authority/src/Authority.vue @@ -0,0 +1,44 @@ + + diff --git a/src/components/Basic/index.ts b/src/components/Basic/index.ts new file mode 100644 index 00000000..62b9ed58 --- /dev/null +++ b/src/components/Basic/index.ts @@ -0,0 +1,8 @@ +import { withInstall } from '@/utils' +import basicArrow from './src/BasicArrow.vue' +import basicTitle from './src/BasicTitle.vue' +import basicHelp from './src/BasicHelp.vue' + +export const BasicArrow = withInstall(basicArrow) +export const BasicTitle = withInstall(basicTitle) +export const BasicHelp = withInstall(basicHelp) diff --git a/src/components/Basic/src/BasicArrow.vue b/src/components/Basic/src/BasicArrow.vue new file mode 100644 index 00000000..2761f781 --- /dev/null +++ b/src/components/Basic/src/BasicArrow.vue @@ -0,0 +1,80 @@ + + + diff --git a/src/components/Basic/src/BasicHelp.vue b/src/components/Basic/src/BasicHelp.vue new file mode 100644 index 00000000..6e202525 --- /dev/null +++ b/src/components/Basic/src/BasicHelp.vue @@ -0,0 +1,112 @@ + + diff --git a/src/components/Basic/src/BasicTitle.vue b/src/components/Basic/src/BasicTitle.vue new file mode 100644 index 00000000..2979d870 --- /dev/null +++ b/src/components/Basic/src/BasicTitle.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/components/Button/index.ts b/src/components/Button/index.ts new file mode 100644 index 00000000..147ab705 --- /dev/null +++ b/src/components/Button/index.ts @@ -0,0 +1,9 @@ +import { withInstall } from '@/utils' +import type { ExtractPropTypes } from 'vue' +import button from './src/BasicButton.vue' +import popConfirmButton from './src/PopConfirmButton.vue' +import { buttonProps } from './src/props' + +export const Button = withInstall(button) +export const PopConfirmButton = withInstall(popConfirmButton) +export declare type ButtonProps = Partial> diff --git a/src/components/Button/src/BasicButton.vue b/src/components/Button/src/BasicButton.vue new file mode 100644 index 00000000..f99cab08 --- /dev/null +++ b/src/components/Button/src/BasicButton.vue @@ -0,0 +1,32 @@ + + diff --git a/src/components/Button/src/PopConfirmButton.vue b/src/components/Button/src/PopConfirmButton.vue new file mode 100644 index 00000000..d35b5318 --- /dev/null +++ b/src/components/Button/src/PopConfirmButton.vue @@ -0,0 +1,54 @@ + diff --git a/src/components/Button/src/props.ts b/src/components/Button/src/props.ts new file mode 100644 index 00000000..448ce1d2 --- /dev/null +++ b/src/components/Button/src/props.ts @@ -0,0 +1,26 @@ +const validColors = ['primary', 'error', 'warning', 'success', ''] as const +type ButtonColorType = (typeof validColors)[number] + +export const buttonProps = { + color: { + type: String as PropType, + validator: (v) => validColors.includes(v), + default: '' + }, + loading: { type: Boolean }, + disabled: { type: Boolean }, + /** + * Text before icon. + */ + preIcon: { type: String }, + /** + * Text after icon. + */ + postIcon: { type: String }, + /** + * preIcon and postIcon icon size. + * @default: 14 + */ + iconSize: { type: Number, default: 14 }, + onClick: { type: Function as PropType<(...args) => any>, default: null } +} diff --git a/src/components/CardList/index.ts b/src/components/CardList/index.ts new file mode 100644 index 00000000..f87a1b9e --- /dev/null +++ b/src/components/CardList/index.ts @@ -0,0 +1,4 @@ +import { withInstall } from '@/utils' +import cardList from './src/CardList.vue' + +export const CardList = withInstall(cardList) diff --git a/src/components/CardList/src/CardList.vue b/src/components/CardList/src/CardList.vue new file mode 100644 index 00000000..611a461a --- /dev/null +++ b/src/components/CardList/src/CardList.vue @@ -0,0 +1,164 @@ + + diff --git a/src/components/CardList/src/data.ts b/src/components/CardList/src/data.ts new file mode 100644 index 00000000..32718ea7 --- /dev/null +++ b/src/components/CardList/src/data.ts @@ -0,0 +1,25 @@ +import { ref } from 'vue' +// 每行个数 +export const grid = ref(12) +// slider属性 +export const useSlider = (min = 6, max = 12) => { + // 每行显示个数滑动条 + const getMarks = () => { + const l = {} + for (let i = min; i < max + 1; i++) { + l[i] = { + style: { + color: '#fff' + }, + label: i + } + } + return l + } + return { + min, + max, + marks: getMarks(), + step: 1 + } +} diff --git a/src/components/CodeEditor/index.ts b/src/components/CodeEditor/index.ts new file mode 100644 index 00000000..45362306 --- /dev/null +++ b/src/components/CodeEditor/index.ts @@ -0,0 +1,8 @@ +import { withInstall } from '@/utils' +import codeEditor from './src/CodeEditor.vue' +import jsonPreview from './src/json-preview/JsonPreview.vue' + +export const CodeEditor = withInstall(codeEditor) +export const JsonPreview = withInstall(jsonPreview) + +export * from './src/typing' diff --git a/src/components/CodeEditor/src/CodeEditor.vue b/src/components/CodeEditor/src/CodeEditor.vue new file mode 100644 index 00000000..ba013fc3 --- /dev/null +++ b/src/components/CodeEditor/src/CodeEditor.vue @@ -0,0 +1,49 @@ + + diff --git a/src/components/CodeEditor/src/codemirror/CodeMirror.vue b/src/components/CodeEditor/src/codemirror/CodeMirror.vue new file mode 100644 index 00000000..089503b6 --- /dev/null +++ b/src/components/CodeEditor/src/codemirror/CodeMirror.vue @@ -0,0 +1,110 @@ + + + diff --git a/src/components/CodeEditor/src/codemirror/codeMirror.ts b/src/components/CodeEditor/src/codemirror/codeMirror.ts new file mode 100644 index 00000000..d8da06e1 --- /dev/null +++ b/src/components/CodeEditor/src/codemirror/codeMirror.ts @@ -0,0 +1,21 @@ +import CodeMirror from 'codemirror' +import './codemirror.css' +import 'codemirror/theme/idea.css' +import 'codemirror/theme/material-palenight.css' +// import 'codemirror/addon/lint/lint.css'; + +// modes +import 'codemirror/mode/javascript/javascript' +import 'codemirror/mode/css/css' +import 'codemirror/mode/htmlmixed/htmlmixed' +// addons +// import 'codemirror/addon/edit/closebrackets'; +// import 'codemirror/addon/edit/closetag'; +// import 'codemirror/addon/comment/comment'; +// import 'codemirror/addon/fold/foldcode'; +// import 'codemirror/addon/fold/foldgutter'; +// import 'codemirror/addon/fold/brace-fold'; +// import 'codemirror/addon/fold/indent-fold'; +// import 'codemirror/addon/lint/json-lint'; +// import 'codemirror/addon/fold/comment-fold'; +export { CodeMirror } diff --git a/src/components/CodeEditor/src/codemirror/codemirror.css b/src/components/CodeEditor/src/codemirror/codemirror.css new file mode 100644 index 00000000..c87ff666 --- /dev/null +++ b/src/components/CodeEditor/src/codemirror/codemirror.css @@ -0,0 +1,525 @@ +/* BASICS */ + +.CodeMirror { + --base: #545281; + --comment: hsl(210deg 25% 60%); + --keyword: #af4ab1; + --variable: #0055d1; + --function: #c25205; + --string: #2ba46d; + --number: #c25205; + --tags: #d00; + --qualifier: #ff6032; + --important: var(--string); + + position: relative; + height: auto; + height: 100%; + overflow: hidden; + font-family: var(--font-code); + background: white; + direction: ltr; +} + +/* PADDING */ + +.CodeMirror-lines { + min-height: 1px; /* prevents collapsing before first draw */ + padding: 4px 0; /* Vertical padding around content */ + cursor: text; +} + +.CodeMirror-scrollbar-filler, +.CodeMirror-gutter-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + position: absolute; + top: 0; + left: 0; + z-index: 3; + min-height: 100%; + white-space: nowrap; + background-color: transparent; + border-right: 1px solid #ddd; +} + +.CodeMirror-linenumber { + min-width: 20px; + padding: 0 3px 0 5px; + color: var(--comment); + text-align: right; + white-space: nowrap; + opacity: 0.6; +} + +.CodeMirror-guttermarker { + color: black; +} + +.CodeMirror-guttermarker-subtle { + color: #999; +} + +/* FOLD GUTTER */ + +.CodeMirror-foldmarker { + font-family: arial; + line-height: 0.3; + color: #414141; + text-shadow: #f96 1px 1px 2px, #f96 -1px -1px 2px, #f96 1px -1px 2px, #f96 -1px 1px 2px; + cursor: pointer; +} + +.CodeMirror-foldgutter { + width: 0.7em; +} + +.CodeMirror-foldgutter-open, +.CodeMirror-foldgutter-folded { + cursor: pointer; +} + +.CodeMirror-foldgutter-open::after, +.CodeMirror-foldgutter-folded::after { + position: relative; + top: -0.1em; + display: inline-block; + font-size: 0.8em; + content: '>'; + opacity: 0.8; + transform: rotate(90deg); + transition: transform 0.2s; +} + +.CodeMirror-foldgutter-folded::after { + transform: none; +} + +/* CURSOR */ + +.CodeMirror-cursor { + position: absolute; + width: 0; + pointer-events: none; + border-right: none; + border-left: 1px solid black; +} + +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} + +.cm-fat-cursor .CodeMirror-cursor { + width: auto; + background: #7e7; + border: 0 !important; +} + +.cm-fat-cursor div.CodeMirror-cursors { + z-index: 1; +} + +.cm-fat-cursor-mark { + background-color: rgb(20 255 20 / 50%); + animation: blink 1.06s steps(1) infinite; +} + +.cm-animate-fat-cursor { + width: auto; + background-color: #7e7; + border: 0; + animation: blink 1.06s steps(1) infinite; +} +@keyframes blink { + 50% { + background-color: transparent; + } +} +@keyframes blink { + 50% { + background-color: transparent; + } +} +@keyframes blink { + 50% { + background-color: transparent; + } +} + +.cm-tab { + display: inline-block; + text-decoration: inherit; +} + +.CodeMirror-rulers { + position: absolute; + top: -50px; + right: 0; + bottom: -20px; + left: 0; + overflow: hidden; +} + +.CodeMirror-ruler { + position: absolute; + top: 0; + bottom: 0; + border-left: 1px solid #ccc; +} + +/* DEFAULT THEME */ +.cm-s-default.CodeMirror { + background-color: transparent; +} + +.cm-s-default .cm-header { + color: blue; +} + +.cm-s-default .cm-quote { + color: #090; +} + +.cm-negative { + color: #d44; +} + +.cm-positive { + color: #292; +} + +.cm-header, +.cm-strong { + font-weight: bold; +} + +.cm-em { + font-style: italic; +} + +.cm-link { + text-decoration: underline; +} + +.cm-strikethrough { + text-decoration: line-through; +} + +.cm-s-default .cm-atom, +.cm-s-default .cm-def, +.cm-s-default .cm-property, +.cm-s-default .cm-variable-2, +.cm-s-default .cm-variable-3, +.cm-s-default .cm-punctuation { + color: var(--base); +} + +.cm-s-default .cm-hr, +.cm-s-default .cm-comment { + color: var(--comment); +} + +.cm-s-default .cm-attribute, +.cm-s-default .cm-keyword { + color: var(--keyword); +} + +.cm-s-default .cm-variable { + color: var(--variable); +} + +.cm-s-default .cm-bracket, +.cm-s-default .cm-tag { + color: var(--tags); +} + +.cm-s-default .cm-number { + color: var(--number); +} + +.cm-s-default .cm-string, +.cm-s-default .cm-string-2 { + color: var(--string); +} + +.cm-s-default .cm-type { + color: #085; +} + +.cm-s-default .cm-meta { + color: #555; +} + +.cm-s-default .cm-qualifier { + color: var(--qualifier); +} + +.cm-s-default .cm-builtin { + color: #7539ff; +} + +.cm-s-default .cm-link { + color: var(--flash); +} + +.cm-s-default .cm-error { + color: #ff008c; +} + +.cm-invalidchar { + color: #ff008c; +} + +.CodeMirror-composing { + border-bottom: 2px solid; +} + +/* Default styles for common addons */ + +div.CodeMirror span.CodeMirror-matchingbracket { + color: #0b0; +} + +div.CodeMirror span.CodeMirror-nonmatchingbracket { + color: #a22; +} + +.CodeMirror-matchingtag { + background: rgb(255 150 0 / 30%); +} + +.CodeMirror-activeline-background { + background: #e8f2ff; +} + +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror-scroll { + position: relative; + height: 100%; + padding-bottom: 30px; + margin-right: -30px; + + /* 30px is the magic margin used to hide the element's real scrollbars */ + + /* See overflow: hidden in .CodeMirror */ + margin-bottom: -30px; + overflow: scroll !important; /* Things will break if this is overridden */ + outline: none; /* Prevent dragging from highlighting the element */ +} + +.CodeMirror-sizer { + position: relative; + margin-bottom: 20px !important; + border-right: 30px solid transparent; +} + +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actual scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, +.CodeMirror-hscrollbar, +.CodeMirror-scrollbar-filler, +.CodeMirror-gutter-filler { + position: absolute; + z-index: 6; + display: none; +} + +.CodeMirror-vscrollbar { + top: 0; + right: 0; + overflow-x: hidden; + overflow-y: scroll; +} + +.CodeMirror-hscrollbar { + bottom: 0; + left: 0; + overflow-x: scroll; + overflow-y: hidden; +} + +.CodeMirror-scrollbar-filler { + right: 0; + bottom: 0; +} + +.CodeMirror-gutter-filler { + bottom: 0; + left: 0; +} + +.CodeMirror-gutter { + display: inline-block; + height: 100%; + margin-bottom: -30px; + white-space: normal; + vertical-align: top; +} + +.CodeMirror-gutter-wrapper { + position: absolute; + z-index: 4; + background: none !important; + border: none !important; +} + +.CodeMirror-gutter-background { + position: absolute; + top: 0; + bottom: 0; + z-index: 4; +} + +.CodeMirror-gutter-elt { + position: absolute; + z-index: 4; + cursor: default; +} + +.CodeMirror-gutter-wrapper ::selection { + background-color: transparent; +} + +.CodeMirrorwrapper ::selection { + background-color: transparent; +} + +.CodeMirror pre { + position: relative; + z-index: 2; + padding: 0 4px; /* Horizontal padding of content */ + margin: 0; + overflow: visible; + font-family: inherit; + font-size: inherit; + line-height: inherit; + color: inherit; + word-wrap: normal; + white-space: pre; + background: transparent; + border-width: 0; + + /* Reset some styles that the rest of the page might have set */ + border-radius: 0; + -webkit-tap-highlight-color: transparent; + font-variant-ligatures: contextual; +} + +.CodeMirror-wrap pre { + word-break: normal; + word-wrap: break-word; + white-space: pre-wrap; +} + +.CodeMirror-linebackground { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; + padding: 0.1px; /* Force widget margins to stay inside of the container */ +} + +.CodeMirror-rtl pre { + direction: rtl; +} + +.CodeMirror-code { + outline: none; +} + +/* Force content-box sizing for the elements where we expect it */ +.CodeMirror-scroll, +.CodeMirror-sizer, +.CodeMirror-gutter, +.CodeMirror-gutters, +.CodeMirror-linenumber { + box-sizing: content-box; +} + +.CodeMirror-measure { + position: absolute; + width: 100%; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.CodeMirror-measure pre { + position: static; +} + +div.CodeMirror-cursors { + position: relative; + z-index: 3; + visibility: hidden; +} + +div.CodeMirror-dragcursors { + visibility: visible; +} + +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { + background: #d9d9d9; +} + +.CodeMirror-focused .CodeMirror-selected { + background: #d7d4f0; +} + +.CodeMirror-crosshair { + cursor: crosshair; +} + +.CodeMirror-line::selection, +.CodeMirror-line > span::selection, +.CodeMirror-line > span > span::selection { + background: #d7d4f0; +} + +.cm-searching { + background-color: #ffa; + background-color: rgb(255 255 0 / 40%); +} + +/* Used to force a border model for a node */ +.cm-force-border { + padding-right: 0.1px; +} + +@media print { + /* Hide the cursor when printing */ + .CodeMirror div.CodeMirror-cursors { + visibility: hidden; + } +} + +/* See issue #2901 */ +.cm-tab-wrap-hack::after { + content: ''; +} + +/* Help users use markselection to safely style text background */ +span.CodeMirror-selectedtext { + background: none; +} diff --git a/src/components/CodeEditor/src/json-preview/JsonPreview.vue b/src/components/CodeEditor/src/json-preview/JsonPreview.vue new file mode 100644 index 00000000..61750c19 --- /dev/null +++ b/src/components/CodeEditor/src/json-preview/JsonPreview.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/components/CodeEditor/src/typing.ts b/src/components/CodeEditor/src/typing.ts new file mode 100644 index 00000000..8e3b8f6d --- /dev/null +++ b/src/components/CodeEditor/src/typing.ts @@ -0,0 +1,5 @@ +export enum MODE { + JSON = 'application/json', + HTML = 'htmlmixed', + JS = 'javascript' +} diff --git a/src/components/Container/index.ts b/src/components/Container/index.ts new file mode 100644 index 00000000..3997898b --- /dev/null +++ b/src/components/Container/index.ts @@ -0,0 +1,10 @@ +import { withInstall } from '@/utils' +import collapseContainer from './src/collapse/CollapseContainer.vue' +import scrollContainer from './src/ScrollContainer.vue' +import lazyContainer from './src/LazyContainer.vue' + +export const CollapseContainer = withInstall(collapseContainer) +export const ScrollContainer = withInstall(scrollContainer) +export const LazyContainer = withInstall(lazyContainer) + +export * from './src/typing' diff --git a/src/components/Container/src/LazyContainer.vue b/src/components/Container/src/LazyContainer.vue new file mode 100644 index 00000000..af892956 --- /dev/null +++ b/src/components/Container/src/LazyContainer.vue @@ -0,0 +1,125 @@ + + diff --git a/src/components/Container/src/ScrollContainer.vue b/src/components/Container/src/ScrollContainer.vue new file mode 100644 index 00000000..615b448c --- /dev/null +++ b/src/components/Container/src/ScrollContainer.vue @@ -0,0 +1,90 @@ + + + + diff --git a/src/components/Container/src/collapse/CollapseContainer.vue b/src/components/Container/src/collapse/CollapseContainer.vue new file mode 100644 index 00000000..28fc25dc --- /dev/null +++ b/src/components/Container/src/collapse/CollapseContainer.vue @@ -0,0 +1,118 @@ + + + diff --git a/src/components/Container/src/collapse/CollapseHeader.vue b/src/components/Container/src/collapse/CollapseHeader.vue new file mode 100644 index 00000000..efaab3e2 --- /dev/null +++ b/src/components/Container/src/collapse/CollapseHeader.vue @@ -0,0 +1,42 @@ + diff --git a/src/components/Container/src/typing.ts b/src/components/Container/src/typing.ts new file mode 100644 index 00000000..57c7922f --- /dev/null +++ b/src/components/Container/src/typing.ts @@ -0,0 +1,17 @@ +export type ScrollType = 'default' | 'main' + +export interface CollapseContainerOptions { + canExpand?: boolean + title?: string + helpMessage?: Array | string +} +export interface ScrollContainerOptions { + enableScroll?: boolean + type?: ScrollType +} + +export type ScrollActionType = RefType<{ + scrollBottom: () => void + getScrollWrap: () => Nullable + scrollTo: (top: number) => void +}> diff --git a/src/components/ContextMenu/index.ts b/src/components/ContextMenu/index.ts new file mode 100644 index 00000000..ad4ae593 --- /dev/null +++ b/src/components/ContextMenu/index.ts @@ -0,0 +1,3 @@ +export { createContextMenu, destroyContextMenu } from './src/createContextMenu' + +export * from './src/typing' diff --git a/src/components/ContextMenu/src/ContextMenu.vue b/src/components/ContextMenu/src/ContextMenu.vue new file mode 100644 index 00000000..fe24b65f --- /dev/null +++ b/src/components/ContextMenu/src/ContextMenu.vue @@ -0,0 +1,204 @@ + + diff --git a/src/components/ContextMenu/src/createContextMenu.ts b/src/components/ContextMenu/src/createContextMenu.ts new file mode 100644 index 00000000..db943dbf --- /dev/null +++ b/src/components/ContextMenu/src/createContextMenu.ts @@ -0,0 +1,75 @@ +import contextMenuVue from './ContextMenu.vue' +import { isClient } from '@/utils/is' +import { CreateContextOptions, ContextMenuProps } from './typing' +import { createVNode, render } from 'vue' + +const menuManager: { + domList: Element[] + resolve: Fn +} = { + domList: [], + resolve: () => {} +} + +export const createContextMenu = function (options: CreateContextOptions) { + const { event } = options || {} + + event && event?.preventDefault() + + if (!isClient) { + return + } + return new Promise((resolve) => { + const body = document.body + + const container = document.createElement('div') + const propsData: Partial = {} + if (options.styles) { + propsData.styles = options.styles + } + + if (options.items) { + propsData.items = options.items + } + + if (options.event) { + propsData.customEvent = event + propsData.axis = { x: event.clientX, y: event.clientY } + } + + const vm = createVNode(contextMenuVue, propsData) + render(vm, container) + + const handleClick = function () { + menuManager.resolve('') + } + + menuManager.domList.push(container) + + const remove = function () { + menuManager.domList.forEach((dom: Element) => { + try { + dom && body.removeChild(dom) + } catch (error) {} + }) + body.removeEventListener('click', handleClick) + body.removeEventListener('scroll', handleClick) + } + + menuManager.resolve = function (arg) { + remove() + resolve(arg) + } + remove() + body.appendChild(container) + body.addEventListener('click', handleClick) + body.addEventListener('scroll', handleClick) + }) +} + +export const destroyContextMenu = function () { + if (menuManager) { + menuManager.resolve('') + menuManager.domList = [] + } +} diff --git a/src/components/ContextMenu/src/typing.ts b/src/components/ContextMenu/src/typing.ts new file mode 100644 index 00000000..ebe93895 --- /dev/null +++ b/src/components/ContextMenu/src/typing.ts @@ -0,0 +1,36 @@ +export interface Axis { + x: number + y: number +} + +export interface ContextMenuItem { + label: string + icon?: string + hidden?: boolean + disabled?: boolean + handler?: Fn + divider?: boolean + children?: ContextMenuItem[] +} +export interface CreateContextOptions { + event: MouseEvent + icon?: string + styles?: any + items?: ContextMenuItem[] +} + +export interface ContextMenuProps { + event?: MouseEvent + styles?: any + items: ContextMenuItem[] + customEvent?: MouseEvent + axis?: Axis + width?: number + showIcon?: boolean +} + +export interface ItemContentProps { + showIcon: boolean | undefined + item: ContextMenuItem + handler: Fn +} diff --git a/src/components/CountDown/index.ts b/src/components/CountDown/index.ts new file mode 100644 index 00000000..6ebb021c --- /dev/null +++ b/src/components/CountDown/index.ts @@ -0,0 +1,6 @@ +import { withInstall } from '@/utils' +import countButton from './src/CountButton.vue' +import countdownInput from './src/CountdownInput.vue' + +export const CountdownInput = withInstall(countdownInput) +export const CountButton = withInstall(countButton) diff --git a/src/components/CountDown/src/CountButton.vue b/src/components/CountDown/src/CountButton.vue new file mode 100644 index 00000000..2a240ed5 --- /dev/null +++ b/src/components/CountDown/src/CountButton.vue @@ -0,0 +1,52 @@ + + diff --git a/src/components/CountDown/src/CountdownInput.vue b/src/components/CountDown/src/CountdownInput.vue new file mode 100644 index 00000000..41136929 --- /dev/null +++ b/src/components/CountDown/src/CountdownInput.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/components/CountDown/src/useCountdown.ts b/src/components/CountDown/src/useCountdown.ts new file mode 100644 index 00000000..2feb6f2b --- /dev/null +++ b/src/components/CountDown/src/useCountdown.ts @@ -0,0 +1,51 @@ +import { ref, unref } from 'vue' +import { tryOnUnmounted } from '@vueuse/core' + +export function useCountdown(count: number) { + const currentCount = ref(count) + + const isStart = ref(false) + + let timerId: ReturnType | null + + function clear() { + timerId && window.clearInterval(timerId) + } + + function stop() { + isStart.value = false + clear() + timerId = null + } + + function start() { + if (unref(isStart) || !!timerId) { + return + } + isStart.value = true + timerId = setInterval(() => { + if (unref(currentCount) === 1) { + stop() + currentCount.value = count + } else { + currentCount.value -= 1 + } + }, 1000) + } + + function reset() { + currentCount.value = count + stop() + } + + function restart() { + reset() + start() + } + + tryOnUnmounted(() => { + reset() + }) + + return { start, reset, restart, clear, stop, currentCount, isStart } +} diff --git a/src/components/CountTo/index.ts b/src/components/CountTo/index.ts new file mode 100644 index 00000000..c3260ec4 --- /dev/null +++ b/src/components/CountTo/index.ts @@ -0,0 +1,4 @@ +import { withInstall } from '@/utils' +import countTo from './src/CountTo.vue' + +export const CountTo = withInstall(countTo) diff --git a/src/components/CountTo/src/CountTo.vue b/src/components/CountTo/src/CountTo.vue new file mode 100644 index 00000000..a7d65999 --- /dev/null +++ b/src/components/CountTo/src/CountTo.vue @@ -0,0 +1,105 @@ + + diff --git a/src/components/Cropper/index.ts b/src/components/Cropper/index.ts new file mode 100644 index 00000000..4e6b3d2d --- /dev/null +++ b/src/components/Cropper/index.ts @@ -0,0 +1,7 @@ +import { withInstall } from '@/utils' +import cropperImage from './src/Cropper.vue' +import avatarCropper from './src/CropperAvatar.vue' + +export * from './src/typing' +export const CropperImage = withInstall(cropperImage) +export const CropperAvatar = withInstall(avatarCropper) diff --git a/src/components/Cropper/src/CopperModal.vue b/src/components/Cropper/src/CopperModal.vue new file mode 100644 index 00000000..fc6f396d --- /dev/null +++ b/src/components/Cropper/src/CopperModal.vue @@ -0,0 +1,233 @@ + + + + diff --git a/src/components/Cropper/src/Cropper.vue b/src/components/Cropper/src/Cropper.vue new file mode 100644 index 00000000..e1c86876 --- /dev/null +++ b/src/components/Cropper/src/Cropper.vue @@ -0,0 +1,173 @@ + + + diff --git a/src/components/Cropper/src/CropperAvatar.vue b/src/components/Cropper/src/CropperAvatar.vue new file mode 100644 index 00000000..03ed0ab1 --- /dev/null +++ b/src/components/Cropper/src/CropperAvatar.vue @@ -0,0 +1,116 @@ + + + + diff --git a/src/components/Cropper/src/typing.ts b/src/components/Cropper/src/typing.ts new file mode 100644 index 00000000..bcad3b45 --- /dev/null +++ b/src/components/Cropper/src/typing.ts @@ -0,0 +1,8 @@ +import type Cropper from 'cropperjs' + +export interface CropendResult { + imgBase64: string + imgInfo: Cropper.Data +} + +export type { Cropper } diff --git a/src/components/Description/index.ts b/src/components/Description/index.ts new file mode 100644 index 00000000..046a3b03 --- /dev/null +++ b/src/components/Description/index.ts @@ -0,0 +1,6 @@ +import { withInstall } from '@/utils' +import description from './src/Description.vue' + +export * from './src/typing' +export { useDescription } from './src/useDescription' +export const Description = withInstall(description) diff --git a/src/components/Description/src/Description.vue b/src/components/Description/src/Description.vue new file mode 100644 index 00000000..f9cf7f77 --- /dev/null +++ b/src/components/Description/src/Description.vue @@ -0,0 +1,184 @@ + diff --git a/src/components/Description/src/typing.ts b/src/components/Description/src/typing.ts new file mode 100644 index 00000000..69f7fd7e --- /dev/null +++ b/src/components/Description/src/typing.ts @@ -0,0 +1,47 @@ +import type { VNode, CSSProperties } from 'vue' +import type { CollapseContainerOptions } from '@/components/Container' +import type { DescriptionsProps } from 'ant-design-vue/es/descriptions' + +export interface DescItem { + labelMinWidth?: number + contentMinWidth?: number + labelStyle?: CSSProperties + field: string + label: string | VNode | JSX.Element + // Merge column + span?: number + show?: (...arg: any) => boolean + // render + render?: (val: any, data: Recordable) => VNode | undefined | JSX.Element | Element | string | number +} + +export interface DescriptionProps extends DescriptionsProps { + // Whether to include the collapse component + useCollapse?: boolean + /** + * item configuration + * @type DescItem + */ + schema: DescItem[] + /** + * 数据 + * @type object + */ + data: Recordable + /** + * Built-in CollapseContainer component configuration + * @type CollapseContainerOptions + */ + collapseOptions?: CollapseContainerOptions +} + +export interface DescInstance { + setDescProps(descProps: Partial): void +} + +export type Register = (descInstance: DescInstance) => void + +/** + * @description: + */ +export type UseDescReturnType = [Register, DescInstance] diff --git a/src/components/Description/src/useDescription.ts b/src/components/Description/src/useDescription.ts new file mode 100644 index 00000000..1a492f56 --- /dev/null +++ b/src/components/Description/src/useDescription.ts @@ -0,0 +1,28 @@ +import type { DescriptionProps, DescInstance, UseDescReturnType } from './typing' +import { ref, getCurrentInstance, unref } from 'vue' +import { isProdMode } from '@/utils/env' + +export function useDescription(props?: Partial): UseDescReturnType { + if (!getCurrentInstance()) { + throw new Error('useDescription() can only be used inside setup() or functional components!') + } + const desc = ref>(null) + const loaded = ref(false) + + function register(instance: DescInstance) { + if (unref(loaded) && isProdMode()) { + return + } + desc.value = instance + props && instance.setDescProps(props) + loaded.value = true + } + + const methods: DescInstance = { + setDescProps: (descProps: Partial): void => { + unref(desc)?.setDescProps(descProps) + } + } + + return [register, methods] +} diff --git a/src/components/Drawer/index.ts b/src/components/Drawer/index.ts new file mode 100644 index 00000000..9d890850 --- /dev/null +++ b/src/components/Drawer/index.ts @@ -0,0 +1,6 @@ +import { withInstall } from '@/utils' +import basicDrawer from './src/BasicDrawer.vue' + +export const BasicDrawer = withInstall(basicDrawer) +export * from './src/typing' +export { useDrawer, useDrawerInner } from './src/useDrawer' diff --git a/src/components/Drawer/src/BasicDrawer.vue b/src/components/Drawer/src/BasicDrawer.vue new file mode 100644 index 00000000..256f1ff8 --- /dev/null +++ b/src/components/Drawer/src/BasicDrawer.vue @@ -0,0 +1,218 @@ + + + diff --git a/src/components/Drawer/src/components/DrawerFooter.vue b/src/components/Drawer/src/components/DrawerFooter.vue new file mode 100644 index 00000000..ffdaec41 --- /dev/null +++ b/src/components/Drawer/src/components/DrawerFooter.vue @@ -0,0 +1,70 @@ + + + + diff --git a/src/components/Drawer/src/components/DrawerHeader.vue b/src/components/Drawer/src/components/DrawerHeader.vue new file mode 100644 index 00000000..f61243d3 --- /dev/null +++ b/src/components/Drawer/src/components/DrawerHeader.vue @@ -0,0 +1,65 @@ + + + + diff --git a/src/components/Drawer/src/props.ts b/src/components/Drawer/src/props.ts new file mode 100644 index 00000000..cdbf0be5 --- /dev/null +++ b/src/components/Drawer/src/props.ts @@ -0,0 +1,42 @@ +import { useI18n } from '@/hooks/web/useI18n' +const { t } = useI18n() + +export const footerProps = { + confirmLoading: { type: Boolean }, + /** + * @description: Show close button + */ + showCancelBtn: { type: Boolean, default: true }, + cancelButtonProps: Object as PropType, + cancelText: { type: String, default: t('common.cancelText') }, + /** + * @description: Show confirmation button + */ + showOkBtn: { type: Boolean, default: true }, + okButtonProps: Object as PropType, + okText: { type: String, default: t('common.okText') }, + okType: { type: String, default: 'primary' }, + showFooter: { type: Boolean }, + footerHeight: { + type: [String, Number] as PropType, + default: 60 + } +} +export const basicProps = { + isDetail: { type: Boolean }, + title: { type: String, default: '' }, + loadingText: { type: String }, + showDetailBack: { type: Boolean, default: true }, + visible: { type: Boolean }, + loading: { type: Boolean }, + maskClosable: { type: Boolean, default: true }, + getContainer: { + type: [Object, String] as PropType + }, + closeFunc: { + type: [Function, Object] as PropType, + default: null + }, + destroyOnClose: { type: Boolean }, + ...footerProps +} diff --git a/src/components/Drawer/src/typing.ts b/src/components/Drawer/src/typing.ts new file mode 100644 index 00000000..4b92e5a5 --- /dev/null +++ b/src/components/Drawer/src/typing.ts @@ -0,0 +1,193 @@ +import type { ButtonProps } from 'ant-design-vue/lib/button/buttonTypes' +import type { CSSProperties, VNodeChild, ComputedRef } from 'vue' +import type { ScrollContainerOptions } from '@/components/Container' + +export interface DrawerInstance { + setDrawerProps: (props: Partial | boolean) => void + emitVisible?: (visible: boolean, uid: number) => void +} + +export interface ReturnMethods extends DrawerInstance { + openDrawer: (visible?: boolean, data?: T, openOnSet?: boolean) => void + closeDrawer: () => void + getVisible?: ComputedRef +} + +export type RegisterFn = (drawerInstance: DrawerInstance, uuid?: string) => void + +export interface ReturnInnerMethods extends DrawerInstance { + closeDrawer: () => void + changeLoading: (loading: boolean) => void + changeOkLoading: (loading: boolean) => void + getVisible?: ComputedRef +} + +export type UseDrawerReturnType = [RegisterFn, ReturnMethods] + +export type UseDrawerInnerReturnType = [RegisterFn, ReturnInnerMethods] + +export interface DrawerFooterProps { + showOkBtn: boolean + showCancelBtn: boolean + /** + * Text of the Cancel button + * @default 'cancel' + * @type string + */ + cancelText: string + /** + * Text of the OK button + * @default 'OK' + * @type string + */ + okText: string + + /** + * Button type of the OK button + * @default 'primary' + * @type string + */ + okType: 'primary' | 'danger' | 'dashed' | 'ghost' | 'default' + /** + * The ok button props, follow jsx rules + * @type object + */ + okButtonProps: { props: ButtonProps; on: {} } + + /** + * The cancel button props, follow jsx rules + * @type object + */ + cancelButtonProps: { props: ButtonProps; on: {} } + /** + * Whether to apply loading visual effect for OK button or not + * @default false + * @type boolean + */ + confirmLoading: boolean + + showFooter: boolean + footerHeight: string | number +} +export interface DrawerProps extends DrawerFooterProps { + isDetail?: boolean + loading?: boolean + showDetailBack?: boolean + visible?: boolean + /** + * Built-in ScrollContainer component configuration + * @type ScrollContainerOptions + */ + scrollOptions?: ScrollContainerOptions + closeFunc?: () => Promise + triggerWindowResize?: boolean + /** + * Whether a close (x) button is visible on top right of the Drawer dialog or not. + * @default true + * @type boolean + */ + closable?: boolean + + /** + * Whether to unmount child components on closing drawer or not. + * @default false + * @type boolean + */ + destroyOnClose?: boolean + + /** + * Return the mounted node for Drawer. + * @default 'body' + * @type any ( HTMLElement| () => HTMLElement | string) + */ + getContainer?: () => HTMLElement | string + + /** + * Whether to show mask or not. + * @default true + * @type boolean + */ + mask?: boolean + + /** + * Clicking on the mask (area outside the Drawer) to close the Drawer or not. + * @default true + * @type boolean + */ + maskClosable?: boolean + + /** + * Style for Drawer's mask element. + * @default {} + * @type object + */ + maskStyle?: CSSProperties + + /** + * The title for Drawer. + * @type any (string | slot) + */ + title?: VNodeChild | JSX.Element + /** + * The class name of the container of the Drawer dialog. + * @type string + */ + wrapClassName?: string + class?: string + /** + * Style of wrapper element which **contains mask** compare to `drawerStyle` + * @type object + */ + wrapStyle?: CSSProperties + + /** + * Style of the popup layer element + * @type object + */ + drawerStyle?: CSSProperties + + /** + * Style of floating layer, typically used for adjusting its position. + * @type object + */ + bodyStyle?: CSSProperties + headerStyle?: CSSProperties + + /** + * Width of the Drawer dialog. + * @default 256 + * @type string | number + */ + width?: string | number + + /** + * placement is top or bottom, height of the Drawer dialog. + * @type string | number + */ + height?: string | number + + /** + * The z-index of the Drawer. + * @default 1000 + * @type number + */ + zIndex?: number + + /** + * The placement of the Drawer. + * @default 'right' + * @type string + */ + placement?: 'top' | 'right' | 'bottom' | 'left' + afterVisibleChange?: (visible?: boolean) => void + keyboard?: boolean + /** + * Specify a callback that will be called when a user clicks mask, close button or Cancel button. + */ + onClose?: (e?: Event) => void +} +export interface DrawerActionType { + scrollBottom: () => void + scrollTo: (to: number) => void + getScrollWrap: () => Element | null +} diff --git a/src/components/Drawer/src/useDrawer.ts b/src/components/Drawer/src/useDrawer.ts new file mode 100644 index 00000000..bdd81b26 --- /dev/null +++ b/src/components/Drawer/src/useDrawer.ts @@ -0,0 +1,146 @@ +import type { UseDrawerReturnType, DrawerInstance, ReturnMethods, DrawerProps, UseDrawerInnerReturnType } from './typing' +import { ref, getCurrentInstance, unref, reactive, watchEffect, nextTick, toRaw, computed } from 'vue' +import { isProdMode } from '@/utils/env' +import { isFunction } from '@/utils/is' +import { tryOnUnmounted } from '@vueuse/core' +import { isEqual } from 'lodash-es' +import { error } from '@/utils/log' + +const dataTransferRef = reactive({}) + +const visibleData = reactive<{ [key: number]: boolean }>({}) + +/** + * @description: Applicable to separate drawer and call outside + */ +export function useDrawer(): UseDrawerReturnType { + if (!getCurrentInstance()) { + throw new Error('useDrawer() can only be used inside setup() or functional components!') + } + const drawer = ref(null) + const loaded = ref>(false) + const uid = ref('') + + function register(drawerInstance: DrawerInstance, uuid: string) { + isProdMode() && + tryOnUnmounted(() => { + drawer.value = null + loaded.value = null + dataTransferRef[unref(uid)] = null + }) + + if (unref(loaded) && isProdMode() && drawerInstance === unref(drawer)) { + return + } + uid.value = uuid + drawer.value = drawerInstance + loaded.value = true + + drawerInstance.emitVisible = (visible: boolean, uid: number) => { + visibleData[uid] = visible + } + } + + const getInstance = () => { + const instance = unref(drawer) + if (!instance) { + error('useDrawer instance is undefined!') + } + return instance + } + + const methods: ReturnMethods = { + setDrawerProps: (props: Partial): void => { + getInstance()?.setDrawerProps(props) + }, + + getVisible: computed((): boolean => { + return visibleData[~~unref(uid)] + }), + + openDrawer: (visible = true, data?: T, openOnSet = true): void => { + getInstance()?.setDrawerProps({ + visible: visible + }) + if (!data) return + + if (openOnSet) { + dataTransferRef[unref(uid)] = null + dataTransferRef[unref(uid)] = toRaw(data) + return + } + const equal = isEqual(toRaw(dataTransferRef[unref(uid)]), toRaw(data)) + if (!equal) { + dataTransferRef[unref(uid)] = toRaw(data) + } + }, + closeDrawer: () => { + getInstance()?.setDrawerProps({ visible: false }) + } + } + + return [register, methods] +} + +export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => { + const drawerInstanceRef = ref>(null) + const currentInstance = getCurrentInstance() + const uidRef = ref('') + + if (!getCurrentInstance()) { + throw new Error('useDrawerInner() can only be used inside setup() or functional components!') + } + + const getInstance = () => { + const instance = unref(drawerInstanceRef) + if (!instance) { + error('useDrawerInner instance is undefined!') + return + } + return instance + } + + const register = (modalInstance: DrawerInstance, uuid: string) => { + isProdMode() && + tryOnUnmounted(() => { + drawerInstanceRef.value = null + }) + + uidRef.value = uuid + drawerInstanceRef.value = modalInstance + currentInstance?.emit('register', modalInstance, uuid) + } + + watchEffect(() => { + const data = dataTransferRef[unref(uidRef)] + if (!data) return + if (!callbackFn || !isFunction(callbackFn)) return + nextTick(() => { + callbackFn(data) + }) + }) + + return [ + register, + { + changeLoading: (loading = true) => { + getInstance()?.setDrawerProps({ loading }) + }, + + changeOkLoading: (loading = true) => { + getInstance()?.setDrawerProps({ confirmLoading: loading }) + }, + getVisible: computed((): boolean => { + return visibleData[~~unref(uidRef)] + }), + + closeDrawer: () => { + getInstance()?.setDrawerProps({ visible: false }) + }, + + setDrawerProps: (props: Partial) => { + getInstance()?.setDrawerProps(props) + } + } + ] +} diff --git a/src/components/Dropdown/index.ts b/src/components/Dropdown/index.ts new file mode 100644 index 00000000..65ea5941 --- /dev/null +++ b/src/components/Dropdown/index.ts @@ -0,0 +1,5 @@ +import { withInstall } from '@/utils' +import dropdown from './src/Dropdown.vue' + +export * from './src/typing' +export const Dropdown = withInstall(dropdown) diff --git a/src/components/Dropdown/src/Dropdown.vue b/src/components/Dropdown/src/Dropdown.vue new file mode 100644 index 00000000..41a3b1b3 --- /dev/null +++ b/src/components/Dropdown/src/Dropdown.vue @@ -0,0 +1,87 @@ + + + diff --git a/src/components/Dropdown/src/typing.ts b/src/components/Dropdown/src/typing.ts new file mode 100644 index 00000000..4ccead6f --- /dev/null +++ b/src/components/Dropdown/src/typing.ts @@ -0,0 +1,9 @@ +export interface DropMenu { + onClick?: Fn + to?: string + icon?: string + event: string | number + text: string + disabled?: boolean + divider?: boolean +} diff --git a/src/components/Excel/index.ts b/src/components/Excel/index.ts new file mode 100644 index 00000000..701e13bf --- /dev/null +++ b/src/components/Excel/index.ts @@ -0,0 +1,8 @@ +import { withInstall } from '@/utils' +import impExcel from './src/ImportExcel.vue' +import expExcelModal from './src/ExportExcelModal.vue' + +export const ImpExcel = withInstall(impExcel) +export const ExpExcelModal = withInstall(expExcelModal) +export * from './src/typing' +export { jsonToSheetXlsx, aoaToSheetXlsx } from './src/Export2Excel' diff --git a/src/components/Excel/src/Export2Excel.ts b/src/components/Excel/src/Export2Excel.ts new file mode 100644 index 00000000..fc3cca33 --- /dev/null +++ b/src/components/Excel/src/Export2Excel.ts @@ -0,0 +1,76 @@ +import * as xlsx from 'xlsx' +import type { WorkBook } from 'xlsx' +import type { JsonToSheet, AoAToSheet } from './typing' + +const { utils, writeFile } = xlsx + +const DEF_FILE_NAME = 'excel-list.xlsx' + +/** + * @param data source data + * @param worksheet worksheet object + * @param min min width + */ +function setColumnWidth(data, worksheet, min = 3) { + const obj = {} + worksheet['!cols'] = [] + data.forEach((item) => { + Object.keys(item).forEach((key) => { + const cur = item[key] + const length = cur?.length ?? min + obj[key] = Math.max(length, obj[key] ?? min) + }) + }) + Object.keys(obj).forEach((key) => { + worksheet['!cols'].push({ + wch: obj[key] + }) + }) +} + +export function jsonToSheetXlsx({ + data, + header, + filename = DEF_FILE_NAME, + json2sheetOpts = {}, + write2excelOpts = { bookType: 'xlsx' } +}: JsonToSheet) { + const arrData = [...data] + if (header) { + arrData.unshift(header) + json2sheetOpts.skipHeader = true + } + + const worksheet = utils.json_to_sheet(arrData, json2sheetOpts) + setColumnWidth(arrData, worksheet) + /* add worksheet to workbook */ + const workbook: WorkBook = { + SheetNames: [filename], + Sheets: { + [filename]: worksheet + } + } + /* output format determined by filename */ + writeFile(workbook, filename, write2excelOpts) + /* at this point, out.xlsb will have been downloaded */ +} + +export function aoaToSheetXlsx({ data, header, filename = DEF_FILE_NAME, write2excelOpts = { bookType: 'xlsx' } }: AoAToSheet) { + const arrData = [...data] + if (header) { + arrData.unshift(header) + } + + const worksheet = utils.aoa_to_sheet(arrData) + + /* add worksheet to workbook */ + const workbook: WorkBook = { + SheetNames: [filename], + Sheets: { + [filename]: worksheet + } + } + /* output format determined by filename */ + writeFile(workbook, filename, write2excelOpts) + /* at this point, out.xlsb will have been downloaded */ +} diff --git a/src/components/Excel/src/ExportExcelModal.vue b/src/components/Excel/src/ExportExcelModal.vue new file mode 100644 index 00000000..3023fb48 --- /dev/null +++ b/src/components/Excel/src/ExportExcelModal.vue @@ -0,0 +1,65 @@ + + diff --git a/src/components/Excel/src/ImportExcel.vue b/src/components/Excel/src/ImportExcel.vue new file mode 100644 index 00000000..f1b9edb3 --- /dev/null +++ b/src/components/Excel/src/ImportExcel.vue @@ -0,0 +1,211 @@ + + diff --git a/src/components/Excel/src/typing.ts b/src/components/Excel/src/typing.ts new file mode 100644 index 00000000..6b57f8a5 --- /dev/null +++ b/src/components/Excel/src/typing.ts @@ -0,0 +1,27 @@ +import type { JSON2SheetOpts, WritingOptions, BookType } from 'xlsx' + +export interface ExcelData { + header: string[] + results: T[] + meta: { sheetName: string } +} + +export interface JsonToSheet { + data: T[] + header?: T + filename?: string + json2sheetOpts?: JSON2SheetOpts + write2excelOpts?: WritingOptions +} + +export interface AoAToSheet { + data: T[][] + header?: T[] + filename?: string + write2excelOpts?: WritingOptions +} + +export interface ExportModalResult { + filename: string + bookType: BookType +} diff --git a/src/components/FlowChart/index.ts b/src/components/FlowChart/index.ts new file mode 100644 index 00000000..3a73b290 --- /dev/null +++ b/src/components/FlowChart/index.ts @@ -0,0 +1,4 @@ +import { withInstall } from '@/utils' +import flowChart from './src/FlowChart.vue' + +export const FlowChart = withInstall(flowChart) diff --git a/src/components/FlowChart/src/FlowChart.vue b/src/components/FlowChart/src/FlowChart.vue new file mode 100644 index 00000000..7e5011af --- /dev/null +++ b/src/components/FlowChart/src/FlowChart.vue @@ -0,0 +1,142 @@ + + diff --git a/src/components/FlowChart/src/FlowChartToolbar.vue b/src/components/FlowChart/src/FlowChartToolbar.vue new file mode 100644 index 00000000..b2b060a9 --- /dev/null +++ b/src/components/FlowChart/src/FlowChartToolbar.vue @@ -0,0 +1,153 @@ + + + diff --git a/src/components/FlowChart/src/adpterForTurbo.ts b/src/components/FlowChart/src/adpterForTurbo.ts new file mode 100644 index 00000000..7deaad39 --- /dev/null +++ b/src/components/FlowChart/src/adpterForTurbo.ts @@ -0,0 +1,75 @@ +const TurboType = { + SEQUENCE_FLOW: 1, + START_EVENT: 2, + END_EVENT: 3, + USER_TASK: 4, + SERVICE_TASK: 5, + EXCLUSIVE_GATEWAY: 6 +} + +function convertFlowElementToEdge(element) { + const { incoming, outgoing, properties, key } = element + const { text, startPoint, endPoint, pointsList, logicFlowType } = properties + const edge = { + id: key, + type: logicFlowType, + sourceNodeId: incoming[0], + targetNodeId: outgoing[0], + text, + startPoint, + endPoint, + pointsList, + properties: {} + } + const excludeProperties = ['startPoint', 'endPoint', 'pointsList', 'text', 'logicFlowType'] + Object.keys(element.properties).forEach((property) => { + if (excludeProperties.indexOf(property) === -1) { + edge.properties[property] = element.properties[property] + } + }) + return edge +} + +function convertFlowElementToNode(element) { + const { properties, key } = element + const { x, y, text, logicFlowType } = properties + const node = { + id: key, + type: logicFlowType, + x, + y, + text, + properties: {} + } + const excludeProperties = ['x', 'y', 'text', 'logicFlowType'] + Object.keys(element.properties).forEach((property) => { + if (excludeProperties.indexOf(property) === -1) { + node.properties[property] = element.properties[property] + } + }) + return node +} + +export function toLogicFlowData(data) { + const lfData: { + // TODO type + nodes: any[] + edges: any[] + } = { + nodes: [], + edges: [] + } + const list = data.flowElementList + list && + list.length > 0 && + list.forEach((element) => { + if (element.type === TurboType.SEQUENCE_FLOW) { + const edge = convertFlowElementToEdge(element) + lfData.edges.push(edge) + } else { + const node = convertFlowElementToNode(element) + lfData.nodes.push(node) + } + }) + return lfData +} diff --git a/src/components/FlowChart/src/config.ts b/src/components/FlowChart/src/config.ts new file mode 100644 index 00000000..53be5ad3 --- /dev/null +++ b/src/components/FlowChart/src/config.ts @@ -0,0 +1,96 @@ +export const nodeList = [ + { + text: '开始', + type: 'start', + class: 'node-start' + }, + { + text: '矩形', + type: 'rect', + class: 'node-rect' + }, + { + type: 'user', + text: '用户', + class: 'node-user' + }, + { + type: 'push', + text: '推送', + class: 'node-push' + }, + { + type: 'download', + text: '位置', + class: 'node-download' + }, + { + type: 'end', + text: '结束', + class: 'node-end' + } +] + +export const BpmnNode = [ + { + type: 'bpmn:startEvent', + text: '开始', + class: 'bpmn-start' + }, + { + type: 'bpmn:endEvent', + text: '结束', + class: 'bpmn-end' + }, + { + type: 'bpmn:exclusiveGateway', + text: '网关', + class: 'bpmn-exclusiveGateway' + }, + { + type: 'bpmn:userTask', + text: '用户', + class: 'bpmn-user' + } +] + +export function configDefaultDndPanel(lf) { + return [ + { + text: '选区', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAOVJREFUOBGtVMENwzAIjKP++2026ETdpv10iy7WFbqFyyW6GBywLCv5gI+Dw2Bluj1znuSjhb99Gkn6QILDY2imo60p8nsnc9bEo3+QJ+AKHfMdZHnl78wyTnyHZD53Zzx73MRSgYvnqgCUHj6gwdck7Zsp1VOrz0Uz8NbKunzAW+Gu4fYW28bUYutYlzSa7B84Fh7d1kjLwhcSdYAYrdkMQVpsBr5XgDGuXwQfQr0y9zwLda+DUYXLaGKdd2ZTtvbolaO87pdo24hP7ov16N0zArH1ur3iwJpXxm+v7oAJNR4JEP8DoAuSFEkYH7cAAAAASUVORK5CYII=', + callback: () => { + lf.updateEditConfig({ + stopMoveGraph: true + }) + } + }, + { + type: 'circle', + text: '开始', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg==' + }, + { + type: 'rect', + text: '用户任务', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==', + cls: 'important-node' + }, + { + type: 'rect', + text: '系统任务', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==', + cls: 'import_icon' + }, + { + type: 'diamond', + text: '条件判断', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAAHeEJUAAAAABGdBTUEAALGPC/xhBQAAAvVJREFUOBGNVEFrE0EU/mY3bQoiFlOkaUJrQUQoWMGePLX24EH0IIoHKQiCV0G8iE1covgLiqA/QTzVm1JPogc9tIJYFaQtlhQxqYjSpunu+L7JvmUTU3AgmTfvffPNN++9WSA1DO182f6xwILzD5btfAoQmwL5KJEwiQyVbSVZ0IgRyV6PTpIJ81E5ZvqfHQR0HUOBHW4L5Et2kQ6Zf7iAOhTFAA8s0pEP7AXO1uAA52SbqGk6h/6J45LaLhO64ByfcUzM39V7ZiAdS2yCePPEIQYvTUHqM/n7dgQNfBKWPjpF4ISk8q3J4nB11qw6X8l+FsF3EhlkEMfrjIer3wJTLwS2aCNcj4DbGxXTw00JmAuO+Ni6bBxVUCvS5d9aa04+so4pHW5jLTywuXAL7jJ+D06sl82Sgl2JuVBQn498zkc2bGKxULHjCnSMadBKYDYYHAtsby1EQ5lNGrQd4Y3v4Zo0XdGEmDno46yCM9Tk+RiJmUYHS/aXHPNTcjxcbTFna000PFJHIVZ5lFRqRpJWk9/+QtlOUYJj9HG5pVFEU7zqIYDVsw2s+AJaD8wTd2umgSCCyUxgGsS1Y6TBwXQQTFuZaHcd8gAGioE90hlsY+wMcs30RduYtxanjMGal8H5dMW67dmT1JFtYUEe8LiQLRsPZ6IIc7A4J5tqco3T0pnv/4u0kyzrYUq7gASuEyI8VXKvB9Odytv6jS/PNaZBln0nioJG/AVQRZvApOdhjj3Jt8QC8Im09SafwdBdvIpztpxWxpeKCC+EsFdS8DCyuCn2munFpL7ctHKp+Xc5cMybeIyMAN33SPL3ZR9QV1XVwLyzHm6Iv0/yeUuUb7PPlZC4D4HZkeu6dpF4v9j9MreGtMbxMMRLIcjJic9yHi7WQ3yVKzZVWUr5UrViJvn1FfUlwe/KYVfYyWRLSGNu16hR01U9IacajXPei0wx/5BqgInvJN+MMNtNme7ReU9SBbgntovn0kKHpFg7UogZvaZiOue/q1SBo9ktHzQAAAAASUVORK5CYII=' + }, + { + type: 'circle', + text: '结束', + icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC' + } + ] +} diff --git a/src/components/FlowChart/src/enum.ts b/src/components/FlowChart/src/enum.ts new file mode 100644 index 00000000..26e24960 --- /dev/null +++ b/src/components/FlowChart/src/enum.ts @@ -0,0 +1,11 @@ +export enum ToolbarTypeEnum { + ZOOM_IN = 'zoomIn', + ZOOM_OUT = 'zoomOut', + RESET_ZOOM = 'resetZoom', + + UNDO = 'undo', + REDO = 'redo', + + SNAPSHOT = 'snapshot', + VIEW_DATA = 'viewData' +} diff --git a/src/components/FlowChart/src/types.ts b/src/components/FlowChart/src/types.ts new file mode 100644 index 00000000..5e42aedb --- /dev/null +++ b/src/components/FlowChart/src/types.ts @@ -0,0 +1,14 @@ +import { NodeConfig } from '@logicflow/core' +import { ToolbarTypeEnum } from './enum' + +export interface NodeItem extends NodeConfig { + icon: string +} + +export interface ToolbarConfig { + type?: string | ToolbarTypeEnum + tooltip?: string | boolean + icon?: string + disabled?: boolean + separate?: boolean +} diff --git a/src/components/FlowChart/src/useFlowContext.ts b/src/components/FlowChart/src/useFlowContext.ts new file mode 100644 index 00000000..da348e5c --- /dev/null +++ b/src/components/FlowChart/src/useFlowContext.ts @@ -0,0 +1,17 @@ +import type LogicFlow from '@logicflow/core' + +import { provide, inject } from 'vue' + +const key = Symbol('flow-chart') + +type Instance = { + logicFlow: LogicFlow +} + +export function createFlowChartContext(instance: Instance) { + provide(key, instance) +} + +export function useFlowChartContext(): Instance { + return inject(key) as Instance +} diff --git a/src/components/Form/index.ts b/src/components/Form/index.ts new file mode 100644 index 00000000..eb7b797f --- /dev/null +++ b/src/components/Form/index.ts @@ -0,0 +1,17 @@ +import BasicForm from './src/BasicForm.vue' + +export * from './src/types/form' +export * from './src/types/formItem' + +export { useComponentRegister } from './src/hooks/useComponentRegister' +export { useForm } from './src/hooks/useForm' + +export { default as ApiSelect } from './src/components/ApiSelect.vue' +export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue' +export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue' +export { default as ApiTree } from './src/components/ApiTree.vue' +export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue' +export { default as ApiCascader } from './src/components/ApiCascader.vue' +export { default as ApiTransfer } from './src/components/ApiTransfer.vue' + +export { BasicForm } diff --git a/src/components/Form/src/BasicForm.vue b/src/components/Form/src/BasicForm.vue new file mode 100644 index 00000000..cf13db95 --- /dev/null +++ b/src/components/Form/src/BasicForm.vue @@ -0,0 +1,320 @@ + + + diff --git a/src/components/Form/src/componentMap.ts b/src/components/Form/src/componentMap.ts new file mode 100644 index 00000000..2df68b05 --- /dev/null +++ b/src/components/Form/src/componentMap.ts @@ -0,0 +1,83 @@ +import type { Component } from 'vue' +import type { ComponentType } from './types' + +/** + * Component list, register here to setting it in the form + */ +import { + Input, + Select, + Radio, + Checkbox, + AutoComplete, + Cascader, + DatePicker, + InputNumber, + Switch, + TimePicker, + TreeSelect, + Slider, + Rate, + Divider +} from 'ant-design-vue' + +import ApiRadioGroup from './components/ApiRadioGroup.vue' +import RadioButtonGroup from './components/RadioButtonGroup.vue' +import ApiSelect from './components/ApiSelect.vue' +import ApiTree from './components/ApiTree.vue' +import ApiTreeSelect from './components/ApiTreeSelect.vue' +import ApiCascader from './components/ApiCascader.vue' +import ApiTransfer from './components/ApiTransfer.vue' +import { BasicUpload } from '@/components/Upload' +import { StrengthMeter } from '@/components/StrengthMeter' +import { IconPicker } from '@/components/Icon' +import { CountdownInput } from '@/components/CountDown' + +const componentMap = new Map() + +componentMap.set('Input', Input) +componentMap.set('InputGroup', Input.Group) +componentMap.set('InputPassword', Input.Password) +componentMap.set('InputSearch', Input.Search) +componentMap.set('InputTextArea', Input.TextArea) +componentMap.set('InputNumber', InputNumber) +componentMap.set('AutoComplete', AutoComplete) + +componentMap.set('Select', Select) +componentMap.set('ApiSelect', ApiSelect) +componentMap.set('ApiTree', ApiTree) +componentMap.set('TreeSelect', TreeSelect) +componentMap.set('ApiTreeSelect', ApiTreeSelect) +componentMap.set('ApiRadioGroup', ApiRadioGroup) +componentMap.set('Switch', Switch) +componentMap.set('RadioButtonGroup', RadioButtonGroup) +componentMap.set('RadioGroup', Radio.Group) +componentMap.set('Checkbox', Checkbox) +componentMap.set('CheckboxGroup', Checkbox.Group) +componentMap.set('ApiCascader', ApiCascader) +componentMap.set('Cascader', Cascader) +componentMap.set('Slider', Slider) +componentMap.set('Rate', Rate) +componentMap.set('ApiTransfer', ApiTransfer) + +componentMap.set('DatePicker', DatePicker) +componentMap.set('MonthPicker', DatePicker.MonthPicker) +componentMap.set('RangePicker', DatePicker.RangePicker) +componentMap.set('WeekPicker', DatePicker.WeekPicker) +componentMap.set('TimePicker', TimePicker) +componentMap.set('StrengthMeter', StrengthMeter) +componentMap.set('IconPicker', IconPicker) +componentMap.set('InputCountDown', CountdownInput) + +componentMap.set('Upload', BasicUpload) +componentMap.set('Divider', Divider) + +export function add(compName: ComponentType, component: Component) { + componentMap.set(compName, component) +} + +export function del(compName: ComponentType) { + componentMap.delete(compName) +} + +export { componentMap } diff --git a/src/components/Form/src/components/ApiCascader.vue b/src/components/Form/src/components/ApiCascader.vue new file mode 100644 index 00000000..434ab3ae --- /dev/null +++ b/src/components/Form/src/components/ApiCascader.vue @@ -0,0 +1,182 @@ + + diff --git a/src/components/Form/src/components/ApiRadioGroup.vue b/src/components/Form/src/components/ApiRadioGroup.vue new file mode 100644 index 00000000..007024a8 --- /dev/null +++ b/src/components/Form/src/components/ApiRadioGroup.vue @@ -0,0 +1,120 @@ + + + diff --git a/src/components/Form/src/components/ApiSelect.vue b/src/components/Form/src/components/ApiSelect.vue new file mode 100644 index 00000000..b5f825ee --- /dev/null +++ b/src/components/Form/src/components/ApiSelect.vue @@ -0,0 +1,140 @@ + + diff --git a/src/components/Form/src/components/ApiTransfer.vue b/src/components/Form/src/components/ApiTransfer.vue new file mode 100644 index 00000000..668b020e --- /dev/null +++ b/src/components/Form/src/components/ApiTransfer.vue @@ -0,0 +1,125 @@ + + diff --git a/src/components/Form/src/components/ApiTree.vue b/src/components/Form/src/components/ApiTree.vue new file mode 100644 index 00000000..8f3e7e77 --- /dev/null +++ b/src/components/Form/src/components/ApiTree.vue @@ -0,0 +1,86 @@ + + diff --git a/src/components/Form/src/components/ApiTreeSelect.vue b/src/components/Form/src/components/ApiTreeSelect.vue new file mode 100644 index 00000000..a68e1c20 --- /dev/null +++ b/src/components/Form/src/components/ApiTreeSelect.vue @@ -0,0 +1,81 @@ + + diff --git a/src/components/Form/src/components/FormAction.vue b/src/components/Form/src/components/FormAction.vue new file mode 100644 index 00000000..0cb1ec39 --- /dev/null +++ b/src/components/Form/src/components/FormAction.vue @@ -0,0 +1,101 @@ + + diff --git a/src/components/Form/src/components/FormItem.vue b/src/components/Form/src/components/FormItem.vue new file mode 100644 index 00000000..2000d6b8 --- /dev/null +++ b/src/components/Form/src/components/FormItem.vue @@ -0,0 +1,374 @@ + diff --git a/src/components/Form/src/components/RadioButtonGroup.vue b/src/components/Form/src/components/RadioButtonGroup.vue new file mode 100644 index 00000000..73fc345d --- /dev/null +++ b/src/components/Form/src/components/RadioButtonGroup.vue @@ -0,0 +1,60 @@ + + + diff --git a/src/components/Form/src/helper.ts b/src/components/Form/src/helper.ts new file mode 100644 index 00000000..fd921111 --- /dev/null +++ b/src/components/Form/src/helper.ts @@ -0,0 +1,83 @@ +import type { Rule } from 'ant-design-vue/lib/form' +import type { ComponentType } from './types' +import { useI18n } from '@/hooks/web/useI18n' +import { dateUtil } from '@/utils/dateUtil' +import { isNumber, isObject } from '@/utils/is' + +const { t } = useI18n() + +/** + * @description: 生成placeholder + */ +export function createPlaceholderMessage(component: ComponentType) { + if (component.includes('Input') || component.includes('Complete')) { + return t('common.inputText') + } + if (component.includes('Picker')) { + return t('common.chooseText') + } + if ( + component.includes('Select') || + component.includes('Cascader') || + component.includes('Checkbox') || + component.includes('Radio') || + component.includes('Switch') + ) { + // return `请选择${label}`; + return t('common.chooseText') + } + return '' +} + +const DATE_TYPE = ['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'] + +function genType() { + return [...DATE_TYPE, 'RangePicker'] +} + +export function setComponentRuleType(rule: Rule, component: ComponentType, valueFormat: string) { + if (['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'].includes(component)) { + rule.type = valueFormat ? 'string' : 'object' + } else if (['RangePicker', 'Upload', 'CheckboxGroup', 'TimePicker'].includes(component)) { + rule.type = 'array' + } else if (['InputNumber'].includes(component)) { + rule.type = 'number' + } +} + +export function processDateValue(attr: Recordable, component: string) { + const { valueFormat, value } = attr + if (valueFormat) { + attr.value = isObject(value) ? dateUtil(value as any).format(valueFormat) : value + } else if (DATE_TYPE.includes(component) && value) { + attr.value = dateUtil(attr.value) + } +} + +export function handleInputNumberValue(component?: ComponentType, val?: any) { + if (!component) return val + if (['Input', 'InputPassword', 'InputSearch', 'InputTextArea'].includes(component)) { + return val && isNumber(val) ? `${val}` : val + } + return val +} + +/** + * 时间字段 + */ +export const dateItemType = genType() + +export const defaultValueComponents = ['Input', 'InputPassword', 'InputSearch', 'InputTextArea'] + +// TODO 自定义组件封装会出现验证问题,因此这里目前改成手动触发验证 +export const NO_AUTO_LINK_COMPONENTS: ComponentType[] = [ + 'Upload', + 'ApiTransfer', + 'ApiTree', + 'ApiSelect', + 'ApiTreeSelect', + 'ApiRadioGroup', + 'ApiCascader', + 'AutoComplete', + 'RadioButtonGroup' +] diff --git a/src/components/Form/src/hooks/useAdvanced.ts b/src/components/Form/src/hooks/useAdvanced.ts new file mode 100644 index 00000000..a80d264a --- /dev/null +++ b/src/components/Form/src/hooks/useAdvanced.ts @@ -0,0 +1,159 @@ +import type { ColEx } from '../types' +import type { AdvanceState } from '../types/hooks' +import { ComputedRef, getCurrentInstance, Ref, shallowReactive } from 'vue' +import type { FormProps, FormSchema } from '../types/form' +import { computed, unref, watch } from 'vue' +import { isBoolean, isFunction, isNumber, isObject } from '@/utils/is' +import { useBreakpoint } from '@/hooks/event/useBreakpoint' +import { useDebounceFn } from '@vueuse/core' + +const BASIC_COL_LEN = 24 + +interface UseAdvancedContext { + advanceState: AdvanceState + emit: EmitType + getProps: ComputedRef + getSchema: ComputedRef + formModel: Recordable + defaultValueRef: Ref +} + +export default function ({ advanceState, emit, getProps, getSchema, formModel, defaultValueRef }: UseAdvancedContext) { + const vm = getCurrentInstance() + + const { realWidthRef, screenEnum, screenRef } = useBreakpoint() + + const getEmptySpan = computed((): number => { + if (!advanceState.isAdvanced) { + return 0 + } + // For some special cases, you need to manually specify additional blank lines + const emptySpan = unref(getProps).emptySpan || 0 + + if (isNumber(emptySpan)) { + return emptySpan + } + if (isObject(emptySpan)) { + const { span = 0 } = emptySpan + const screen = unref(screenRef) as string + + const screenSpan = (emptySpan as any)[screen.toLowerCase()] + return screenSpan || span || 0 + } + return 0 + }) + + const debounceUpdateAdvanced = useDebounceFn(updateAdvanced, 30) + + watch( + [() => unref(getSchema), () => advanceState.isAdvanced, () => unref(realWidthRef)], + () => { + const { showAdvancedButton } = unref(getProps) + if (showAdvancedButton) { + debounceUpdateAdvanced() + } + }, + { immediate: true } + ) + + function getAdvanced(itemCol: Partial, itemColSum = 0, isLastAction = false) { + const width = unref(realWidthRef) + + const mdWidth = + parseInt(itemCol.md as string) || + parseInt(itemCol.xs as string) || + parseInt(itemCol.sm as string) || + (itemCol.span as number) || + BASIC_COL_LEN + + const lgWidth = parseInt(itemCol.lg as string) || mdWidth + const xlWidth = parseInt(itemCol.xl as string) || lgWidth + const xxlWidth = parseInt(itemCol.xxl as string) || xlWidth + if (width <= screenEnum.LG) { + itemColSum += mdWidth + } else if (width < screenEnum.XL) { + itemColSum += lgWidth + } else if (width < screenEnum.XXL) { + itemColSum += xlWidth + } else { + itemColSum += xxlWidth + } + + if (isLastAction) { + advanceState.hideAdvanceBtn = false + if (itemColSum <= BASIC_COL_LEN * 2) { + // When less than or equal to 2 lines, the collapse and expand buttons are not displayed + advanceState.hideAdvanceBtn = true + advanceState.isAdvanced = true + } else if (itemColSum > BASIC_COL_LEN * 2 && itemColSum <= BASIC_COL_LEN * (unref(getProps).autoAdvancedLine || 3)) { + advanceState.hideAdvanceBtn = false + + // More than 3 lines collapsed by default + } else if (!advanceState.isLoad) { + advanceState.isLoad = true + advanceState.isAdvanced = !advanceState.isAdvanced + } + return { isAdvanced: advanceState.isAdvanced, itemColSum } + } + if (itemColSum > BASIC_COL_LEN * (unref(getProps).alwaysShowLines || 1)) { + return { isAdvanced: advanceState.isAdvanced, itemColSum } + } else { + // The first line is always displayed + return { isAdvanced: true, itemColSum } + } + } + + const fieldsIsAdvancedMap = shallowReactive({}) + + function updateAdvanced() { + let itemColSum = 0 + let realItemColSum = 0 + const { baseColProps = {} } = unref(getProps) + + for (const schema of unref(getSchema)) { + const { show, colProps } = schema + let isShow = true + + if (isBoolean(show)) { + isShow = show + } + + if (isFunction(show)) { + isShow = show({ + schema: schema, + model: formModel, + field: schema.field, + values: { + ...unref(defaultValueRef), + ...formModel + } + }) + } + + if (isShow && (colProps || baseColProps)) { + const { itemColSum: sum, isAdvanced } = getAdvanced({ ...baseColProps, ...colProps }, itemColSum) + + itemColSum = sum || 0 + if (isAdvanced) { + realItemColSum = itemColSum + } + fieldsIsAdvancedMap[schema.field] = isAdvanced + } + } + + // 确保页面发送更新 + vm?.proxy?.$forceUpdate() + + advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpan) + + getAdvanced(unref(getProps).actionColOptions || { span: BASIC_COL_LEN }, itemColSum, true) + + emit('advanced-change') + } + + function handleToggleAdvanced() { + advanceState.isAdvanced = !advanceState.isAdvanced + } + + return { handleToggleAdvanced, fieldsIsAdvancedMap } +} diff --git a/src/components/Form/src/hooks/useAutoFocus.ts b/src/components/Form/src/hooks/useAutoFocus.ts new file mode 100644 index 00000000..120d971a --- /dev/null +++ b/src/components/Form/src/hooks/useAutoFocus.ts @@ -0,0 +1,35 @@ +import type { ComputedRef, Ref } from 'vue' +import type { FormSchema, FormActionType, FormProps } from '../types/form' + +import { unref, nextTick, watchEffect } from 'vue' + +interface UseAutoFocusContext { + getSchema: ComputedRef + getProps: ComputedRef + isInitedDefault: Ref + formElRef: Ref +} +export async function useAutoFocus({ getSchema, getProps, formElRef, isInitedDefault }: UseAutoFocusContext) { + watchEffect(async () => { + if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) { + return + } + await nextTick() + const schemas = unref(getSchema) + const formEl = unref(formElRef) + const el = (formEl as any)?.$el as HTMLElement + if (!formEl || !el || !schemas || schemas.length === 0) { + return + } + + const firstItem = schemas[0] + // Only open when the first form item is input type + if (!firstItem.component.includes('Input')) { + return + } + + const inputEl = el.querySelector('.ant-row:first-child input') as Nullable + if (!inputEl) return + inputEl?.focus() + }) +} diff --git a/src/components/Form/src/hooks/useComponentRegister.ts b/src/components/Form/src/hooks/useComponentRegister.ts new file mode 100644 index 00000000..08bc3db2 --- /dev/null +++ b/src/components/Form/src/hooks/useComponentRegister.ts @@ -0,0 +1,11 @@ +import type { ComponentType } from '../types' +import { tryOnUnmounted } from '@vueuse/core' +import { add, del } from '../componentMap' +import type { Component } from 'vue' + +export function useComponentRegister(compName: ComponentType, comp: Component) { + add(compName, comp) + tryOnUnmounted(() => { + del(compName) + }) +} diff --git a/src/components/Form/src/hooks/useForm.ts b/src/components/Form/src/hooks/useForm.ts new file mode 100644 index 00000000..d9374fef --- /dev/null +++ b/src/components/Form/src/hooks/useForm.ts @@ -0,0 +1,116 @@ +import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form' +import type { NamePath } from 'ant-design-vue/lib/form/interface' +import type { DynamicProps } from '@/types/utils' +import { ref, onUnmounted, unref, nextTick, watch } from 'vue' +import { isProdMode } from '@/utils/env' +import { error } from '@/utils/log' +import { getDynamicProps } from '@/utils' + +export declare type ValidateFields = (nameList?: NamePath[]) => Promise + +type Props = Partial> + +export function useForm(props?: Props): UseFormReturnType { + const formRef = ref>(null) + const loadedRef = ref>(false) + + async function getForm() { + const form = unref(formRef) + if (!form) { + error('The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!') + } + await nextTick() + return form as FormActionType + } + + function register(instance: FormActionType) { + isProdMode() && + onUnmounted(() => { + formRef.value = null + loadedRef.value = null + }) + if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return + + formRef.value = instance + loadedRef.value = true + + watch( + () => props, + () => { + props && instance.setProps(getDynamicProps(props)) + }, + { + immediate: true, + deep: true + } + ) + } + + const methods: FormActionType = { + scrollToField: async (name: NamePath, options?: ScrollOptions | undefined) => { + const form = await getForm() + form.scrollToField(name, options) + }, + setProps: async (formProps: Partial) => { + const form = await getForm() + form.setProps(formProps) + }, + + updateSchema: async (data: Partial | Partial[]) => { + const form = await getForm() + form.updateSchema(data) + }, + + resetSchema: async (data: Partial | Partial[]) => { + const form = await getForm() + form.resetSchema(data) + }, + + clearValidate: async (name?: string | string[]) => { + const form = await getForm() + form.clearValidate(name) + }, + + resetFields: async () => { + getForm().then(async (form) => { + await form.resetFields() + }) + }, + + removeSchemaByField: async (field: string | string[]) => { + unref(formRef)?.removeSchemaByField(field) + }, + + // TODO promisify + getFieldsValue: () => { + return unref(formRef)?.getFieldsValue() as T + }, + + setFieldsValue: async (values: Recordable) => { + const form = await getForm() + form.setFieldsValue(values) + }, + + appendSchemaByField: async (schema: FormSchema | FormSchema[], prefixField: string | undefined, first: boolean) => { + const form = await getForm() + form.appendSchemaByField(schema, prefixField, first) + }, + + submit: async (): Promise => { + const form = await getForm() + return form.submit() + }, + + validate: async (nameList?: NamePath[]): Promise => { + const form = await getForm() + return form.validate(nameList) + }, + + validateFields: async (nameList?: NamePath[]): Promise => { + const form = await getForm() + return form.validateFields(nameList) + } + } + + return [register, methods] +} diff --git a/src/components/Form/src/hooks/useFormContext.ts b/src/components/Form/src/hooks/useFormContext.ts new file mode 100644 index 00000000..cbe37fdf --- /dev/null +++ b/src/components/Form/src/hooks/useFormContext.ts @@ -0,0 +1,17 @@ +import type { InjectionKey } from 'vue' +import { createContext, useContext } from '@/hooks/core/useContext' + +export interface FormContextProps { + resetAction: () => Promise + submitAction: () => Promise +} + +const key: InjectionKey = Symbol() + +export function createFormContext(context: FormContextProps) { + return createContext(context, key) +} + +export function useFormContext() { + return useContext(key) +} diff --git a/src/components/Form/src/hooks/useFormEvents.ts b/src/components/Form/src/hooks/useFormEvents.ts new file mode 100644 index 00000000..4a95589f --- /dev/null +++ b/src/components/Form/src/hooks/useFormEvents.ts @@ -0,0 +1,316 @@ +import type { ComputedRef, Ref } from 'vue' +import type { FormProps, FormSchema, FormActionType } from '../types/form' +import type { NamePath } from 'ant-design-vue/lib/form/interface' +import { unref, toRaw, nextTick } from 'vue' +import { isArray, isFunction, isObject, isString, isDef, isNullOrUnDef, isEmpty } from '@/utils/is' +import { deepMerge } from '@/utils' +import { dateItemType, handleInputNumberValue, defaultValueComponents } from '../helper' +import { dateUtil } from '@/utils/dateUtil' +import { cloneDeep, uniqBy } from 'lodash-es' +import { error } from '@/utils/log' + +interface UseFormActionContext { + emit: EmitType + getProps: ComputedRef + getSchema: ComputedRef + formModel: Recordable + defaultValueRef: Ref + formElRef: Ref + schemaRef: Ref + handleFormValues: Fn +} +export function useFormEvents({ + emit, + getProps, + formModel, + getSchema, + defaultValueRef, + formElRef, + schemaRef, + handleFormValues +}: UseFormActionContext) { + async function resetFields(): Promise { + const { resetFunc, submitOnReset } = unref(getProps) + resetFunc && isFunction(resetFunc) && (await resetFunc()) + + const formEl = unref(formElRef) + if (!formEl) return + + Object.keys(formModel).forEach((key) => { + const schema = unref(getSchema).find((item) => item.field === key) + const isInput = schema?.component && defaultValueComponents.includes(schema.component) + const defaultValue = cloneDeep(defaultValueRef.value[key]) + formModel[key] = isInput ? defaultValue || '' : defaultValue + }) + nextTick(() => clearValidate()) + + emit('reset', toRaw(formModel)) + submitOnReset && handleSubmit() + } + + /** + * @description: Set form value + */ + async function setFieldsValue(values: Recordable): Promise { + const fields = unref(getSchema) + .map((item) => item.field) + .filter(Boolean) + + // key 支持 a.b.c 的嵌套写法 + const delimiter = '.' + const nestKeyArray = fields.filter((item) => String(item).indexOf(delimiter) >= 0) + + const validKeys: string[] = [] + Object.keys(values).forEach((key) => { + const schema = unref(getSchema).find((item) => item.field === key) + let value = values[key] + + const hasKey = Reflect.has(values, key) + + value = handleInputNumberValue(schema?.component, value) + const { componentProps } = schema || {} + let _props = componentProps as any + if (typeof componentProps === 'function') { + _props = _props({ formModel: unref(formModel) }) + } + // 0| '' is allow + if (hasKey && fields.includes(key)) { + // time type + if (itemIsDateType(key)) { + if (Array.isArray(value)) { + const arr: any[] = [] + for (const ele of value) { + arr.push(ele ? dateUtil(ele) : null) + } + unref(formModel)[key] = arr + } else { + unref(formModel)[key] = value ? (_props?.valueFormat ? value : dateUtil(value)) : null + } + } else { + unref(formModel)[key] = value + } + if (_props?.onChange) { + _props?.onChange(value) + } + validKeys.push(key) + } else { + nestKeyArray.forEach((nestKey: string) => { + try { + const value = nestKey.split('.').reduce((out, item) => out[item], values) + if (isDef(value)) { + unref(formModel)[nestKey] = unref(value) + validKeys.push(nestKey) + } + } catch (e) { + // key not exist + if (isDef(defaultValueRef.value[nestKey])) { + unref(formModel)[nestKey] = cloneDeep(unref(defaultValueRef.value[nestKey])) + } + } + }) + } + }) + validateFields(validKeys).catch((_) => {}) + } + /** + * @description: Delete based on field name + */ + async function removeSchemaByField(fields: string | string[]): Promise { + const schemaList: FormSchema[] = cloneDeep(unref(getSchema)) + if (!fields) { + return + } + + let fieldList: string[] = isString(fields) ? [fields] : fields + if (isString(fields)) { + fieldList = [fields] + } + for (const field of fieldList) { + _removeSchemaByFeild(field, schemaList) + } + schemaRef.value = schemaList + } + + /** + * @description: Delete based on field name + */ + function _removeSchemaByFeild(field: string, schemaList: FormSchema[]): void { + if (isString(field)) { + const index = schemaList.findIndex((schema) => schema.field === field) + if (index !== -1) { + delete formModel[field] + schemaList.splice(index, 1) + } + } + } + + /** + * @description: Insert after a certain field, if not insert the last + */ + async function appendSchemaByField(schema: FormSchema | FormSchema[], prefixField?: string, first = false) { + const schemaList: FormSchema[] = cloneDeep(unref(getSchema)) + + const index = schemaList.findIndex((schema) => schema.field === prefixField) + const _schemaList = isObject(schema) ? [schema as FormSchema] : (schema as FormSchema[]) + if (!prefixField || index === -1 || first) { + first ? schemaList.unshift(..._schemaList) : schemaList.push(..._schemaList) + schemaRef.value = schemaList + _setDefaultValue(schema) + return + } + if (index !== -1) { + schemaList.splice(index + 1, 0, ..._schemaList) + } + _setDefaultValue(schema) + + schemaRef.value = schemaList + } + + async function resetSchema(data: Partial | Partial[]) { + let updateData: Partial[] = [] + if (isObject(data)) { + updateData.push(data as FormSchema) + } + if (isArray(data)) { + updateData = [...data] + } + + const hasField = updateData.every((item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field)) + + if (!hasField) { + error('All children of the form Schema array that need to be updated must contain the `field` field') + return + } + schemaRef.value = updateData as FormSchema[] + } + + async function updateSchema(data: Partial | Partial[]) { + let updateData: Partial[] = [] + if (isObject(data)) { + updateData.push(data as FormSchema) + } + if (isArray(data)) { + updateData = [...data] + } + + const hasField = updateData.every((item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field)) + + if (!hasField) { + error('All children of the form Schema array that need to be updated must contain the `field` field') + return + } + const schema: FormSchema[] = [] + unref(getSchema).forEach((val) => { + let _val + updateData.forEach((item) => { + if (val.field === item.field) { + _val = item + } + }) + if (_val !== undefined && val.field === _val.field) { + const newSchema = deepMerge(val, _val) + schema.push(newSchema as FormSchema) + } else { + schema.push(val) + } + }) + _setDefaultValue(schema) + + schemaRef.value = uniqBy(schema, 'field') + } + + function _setDefaultValue(data: FormSchema | FormSchema[]) { + let schemas: FormSchema[] = [] + if (isObject(data)) { + schemas.push(data as FormSchema) + } + if (isArray(data)) { + schemas = [...data] + } + + const obj: Recordable = {} + const currentFieldsValue = getFieldsValue() + schemas.forEach((item) => { + if ( + item.component != 'Divider' && + Reflect.has(item, 'field') && + item.field && + !isNullOrUnDef(item.defaultValue) && + (!(item.field in currentFieldsValue) || isNullOrUnDef(currentFieldsValue[item.field]) || isEmpty(currentFieldsValue[item.field])) + ) { + obj[item.field] = item.defaultValue + } + }) + setFieldsValue(obj) + } + + function getFieldsValue(): Recordable { + const formEl = unref(formElRef) + if (!formEl) return {} + return handleFormValues(toRaw(unref(formModel))) + } + + /** + * @description: Is it time + */ + function itemIsDateType(key: string) { + return unref(getSchema).some((item) => { + return item.field === key ? dateItemType.includes(item.component) : false + }) + } + + async function validateFields(nameList?: NamePath[] | undefined) { + return unref(formElRef)?.validateFields(nameList) + } + + async function validate(nameList?: NamePath[] | undefined) { + return await unref(formElRef)?.validate(nameList) + } + + async function clearValidate(name?: string | string[]) { + await unref(formElRef)?.clearValidate(name) + } + + async function scrollToField(name: NamePath, options?: ScrollOptions | undefined) { + await unref(formElRef)?.scrollToField(name, options) + } + + /** + * @description: Form submission + */ + async function handleSubmit(e?: Event): Promise { + e && e.preventDefault() + const { submitFunc } = unref(getProps) + if (submitFunc && isFunction(submitFunc)) { + await submitFunc() + return + } + const formEl = unref(formElRef) + if (!formEl) return + try { + const values = await validate() + const res = handleFormValues(values) + emit('submit', res) + } catch (error: any) { + if (error?.outOfDate === false && error?.errorFields) { + return + } + throw new Error(error) + } + } + + return { + handleSubmit, + clearValidate, + validate, + validateFields, + getFieldsValue, + updateSchema, + resetSchema, + appendSchemaByField, + removeSchemaByField, + resetFields, + setFieldsValue, + scrollToField + } +} diff --git a/src/components/Form/src/hooks/useFormValues.ts b/src/components/Form/src/hooks/useFormValues.ts new file mode 100644 index 00000000..cf87c2d9 --- /dev/null +++ b/src/components/Form/src/hooks/useFormValues.ts @@ -0,0 +1,138 @@ +import { isArray, isFunction, isObject, isString, isNullOrUnDef } from '@/utils/is' +import { dateUtil } from '@/utils/dateUtil' +import { unref } from 'vue' +import type { Ref, ComputedRef } from 'vue' +import type { FormProps, FormSchema } from '../types/form' +import { cloneDeep, set } from 'lodash-es' + +interface UseFormValuesContext { + defaultValueRef: Ref + getSchema: ComputedRef + getProps: ComputedRef + formModel: Recordable +} + +/** + * @desription deconstruct array-link key. This method will mutate the target. + */ +function tryDeconstructArray(key: string, value: any, target: Recordable) { + const pattern = /^\[(.+)\]$/ + if (pattern.test(key)) { + const match = key.match(pattern) + if (match && match[1]) { + const keys = match[1].split(',') + value = Array.isArray(value) ? value : [value] + keys.forEach((k, index) => { + set(target, k.trim(), value[index]) + }) + return true + } + } +} + +/** + * @desription deconstruct object-link key. This method will mutate the target. + */ +function tryDeconstructObject(key: string, value: any, target: Recordable) { + const pattern = /^\{(.+)\}$/ + if (pattern.test(key)) { + const match = key.match(pattern) + if (match && match[1]) { + const keys = match[1].split(',') + value = isObject(value) ? value : {} + keys.forEach((k) => { + set(target, k.trim(), value[k.trim()]) + }) + return true + } + } +} + +export function useFormValues({ defaultValueRef, getSchema, formModel, getProps }: UseFormValuesContext) { + // Processing form values + function handleFormValues(values: Recordable) { + if (!isObject(values)) { + return {} + } + const res: Recordable = {} + for (const item of Object.entries(values)) { + let [, value] = item + const [key] = item + if (!key || (isArray(value) && value.length === 0) || isFunction(value)) { + continue + } + const transformDateFunc = unref(getProps).transformDateFunc + if (isObject(value)) { + value = transformDateFunc?.(value) + } + + if (isArray(value) && value[0]?.format && value[1]?.format) { + value = value.map((item) => transformDateFunc?.(item)) + } + // Remove spaces + if (isString(value)) { + // remove params from URL + if (value === '') { + value = undefined + } else { + value = value.trim() + } + } + if (!tryDeconstructArray(key, value, res) && !tryDeconstructObject(key, value, res)) { + // 没有解构成功的,按原样赋值 + set(res, key, value) + } + } + return handleRangeTimeValue(res) + } + + /** + * @description: Processing time interval parameters + */ + function handleRangeTimeValue(values: Recordable) { + const fieldMapToTime = unref(getProps).fieldMapToTime + + if (!fieldMapToTime || !Array.isArray(fieldMapToTime)) { + return values + } + + for (const [field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD'] of fieldMapToTime) { + if (!field || !startTimeKey || !endTimeKey) { + continue + } + // If the value to be converted is empty, remove the field + if (!values[field]) { + Reflect.deleteProperty(values, field) + continue + } + + const [startTime, endTime]: string[] = values[field] + + const [startTimeFormat, endTimeFormat] = Array.isArray(format) ? format : [format, format] + + values[startTimeKey] = dateUtil(startTime).format(startTimeFormat) + values[endTimeKey] = dateUtil(endTime).format(endTimeFormat) + Reflect.deleteProperty(values, field) + } + + return values + } + + function initDefault() { + const schemas = unref(getSchema) + const obj: Recordable = {} + schemas.forEach((item) => { + const { defaultValue } = item + if (!isNullOrUnDef(defaultValue)) { + obj[item.field] = defaultValue + + if (formModel[item.field] === undefined) { + formModel[item.field] = defaultValue + } + } + }) + defaultValueRef.value = cloneDeep(obj) + } + + return { handleFormValues, initDefault } +} diff --git a/src/components/Form/src/hooks/useLabelWidth.ts b/src/components/Form/src/hooks/useLabelWidth.ts new file mode 100644 index 00000000..8bba8c87 --- /dev/null +++ b/src/components/Form/src/hooks/useLabelWidth.ts @@ -0,0 +1,37 @@ +import type { Ref } from 'vue' +import { computed, unref } from 'vue' +import type { FormProps, FormSchema } from '../types/form' +import { isNumber } from '@/utils/is' + +export function useItemLabelWidth(schemaItemRef: Ref, propsRef: Ref) { + return computed(() => { + const schemaItem = unref(schemaItemRef) + const { labelCol = {}, wrapperCol = {} } = schemaItem.itemProps || {} + const { labelWidth, disabledLabelWidth } = schemaItem + + const { labelWidth: globalLabelWidth, labelCol: globalLabelCol, wrapperCol: globWrapperCol, layout } = unref(propsRef) + + // If labelWidth is set globally, all items setting + if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) { + labelCol.style = { + textAlign: 'left' + } + return { labelCol, wrapperCol } + } + let width = labelWidth || globalLabelWidth + const col = { ...globalLabelCol, ...labelCol } + const wrapCol = { ...globWrapperCol, ...wrapperCol } + + if (width) { + width = isNumber(width) ? `${width}px` : width + } + + return { + labelCol: { style: { width }, ...col }, + wrapperCol: { + style: { width: layout === 'vertical' ? '100%' : `calc(100% - ${width})` }, + ...wrapCol + } + } + }) +} diff --git a/src/components/Form/src/props.ts b/src/components/Form/src/props.ts new file mode 100644 index 00000000..ac17f1d6 --- /dev/null +++ b/src/components/Form/src/props.ts @@ -0,0 +1,103 @@ +import type { FieldMapToTime, FormSchema } from './types/form' +import type { CSSProperties } from 'vue' +import type { ColEx } from './types' +import type { TableActionType } from '@/components/Table' +import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes' +import type { RowProps } from 'ant-design-vue/lib/grid/Row' +import { propTypes } from '@/utils/propTypes' + +export const basicProps = { + model: { + type: Object as PropType, + default: () => ({}) + }, + // 标签宽度 固定宽度 + labelWidth: { + type: [Number, String] as PropType, + default: 0 + }, + fieldMapToTime: { + type: Array as PropType, + default: () => [] + }, + compact: propTypes.bool, + // 表单配置规则 + schemas: { + type: Array as PropType, + default: () => [] + }, + mergeDynamicData: { + type: Object as PropType, + default: null + }, + baseRowStyle: { + type: Object as PropType + }, + baseColProps: { + type: Object as PropType> + }, + autoSetPlaceHolder: propTypes.bool.def(true), + // 在INPUT组件上单击回车时,是否自动提交 + autoSubmitOnEnter: propTypes.bool.def(false), + submitOnReset: propTypes.bool, + submitOnChange: propTypes.bool, + size: propTypes.oneOf(['default', 'small', 'large']).def('default'), + // 禁用表单 + disabled: propTypes.bool, + emptySpan: { + type: [Number, Object] as PropType, + default: 0 + }, + // 是否显示收起展开按钮 + showAdvancedButton: propTypes.bool, + // 转化时间 + transformDateFunc: { + type: Function as PropType, + default: (date: any) => { + return date?.format?.('YYYY-MM-DD HH:mm:ss') ?? date + } + }, + rulesMessageJoinLabel: propTypes.bool.def(true), + // 超过3行自动折叠 + autoAdvancedLine: propTypes.number.def(3), + // 不受折叠影响的行数 + alwaysShowLines: propTypes.number.def(1), + + // 是否显示操作按钮 + showActionButtonGroup: propTypes.bool.def(true), + // 操作列Col配置 + actionColOptions: Object as PropType>, + // 显示重置按钮 + showResetButton: propTypes.bool.def(true), + // 是否聚焦第一个输入框,只在第一个表单项为input的时候作用 + autoFocusFirstItem: propTypes.bool, + // 重置按钮配置 + resetButtonOptions: Object as PropType>, + + // 显示确认按钮 + showSubmitButton: propTypes.bool.def(true), + // 确认按钮配置 + submitButtonOptions: Object as PropType>, + + // 自定义重置函数 + resetFunc: Function as PropType<() => Promise>, + submitFunc: Function as PropType<() => Promise>, + + // 以下为默认props + hideRequiredMark: propTypes.bool, + + labelCol: Object as PropType>, + + layout: propTypes.oneOf(['horizontal', 'vertical', 'inline']).def('horizontal'), + tableAction: { + type: Object as PropType + }, + + wrapperCol: Object as PropType>, + + colon: propTypes.bool, + + labelAlign: propTypes.string, + + rowProps: Object as PropType +} diff --git a/src/components/Form/src/types/form.ts b/src/components/Form/src/types/form.ts new file mode 100644 index 00000000..91d8883c --- /dev/null +++ b/src/components/Form/src/types/form.ts @@ -0,0 +1,211 @@ +import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface' +import type { VNode } from 'vue' +import type { ButtonProps as AntdButtonProps } from '@/components/Button' +import type { FormItem } from './formItem' +import type { ColEx, ComponentType } from './index' +import type { TableActionType } from '@/components/Table/src/types/table' +import type { CSSProperties } from 'vue' +import type { RowProps } from 'ant-design-vue/lib/grid/Row' + +export type FieldMapToTime = [string, [string, string], (string | [string, string])?][] + +export type Rule = RuleObject & { + trigger?: 'blur' | 'change' | ['change', 'blur'] +} + +export interface RenderCallbackParams { + schema: FormSchema + values: Recordable + model: Recordable + field: string +} + +export interface ButtonProps extends AntdButtonProps { + text?: string +} + +export interface FormActionType { + submit: () => Promise + setFieldsValue: (values: Recordable) => Promise + resetFields: () => Promise + getFieldsValue: () => Recordable + clearValidate: (name?: string | string[]) => Promise + updateSchema: (data: Partial | Partial[]) => Promise + resetSchema: (data: Partial | Partial[]) => Promise + setProps: (formProps: Partial) => Promise + removeSchemaByField: (field: string | string[]) => Promise + appendSchemaByField: (schema: FormSchema | FormSchema[], prefixField: string | undefined, first?: boolean | undefined) => Promise + validateFields: (nameList?: NamePath[]) => Promise + validate: (nameList?: NamePath[]) => Promise + scrollToField: (name: NamePath, options?: ScrollOptions) => Promise +} + +export type RegisterFn = (formInstance: FormActionType) => void + +export type UseFormReturnType = [RegisterFn, FormActionType] + +export interface FormProps { + name?: string + layout?: 'vertical' | 'inline' | 'horizontal' + // Form value + model?: Recordable + // The width of all items in the entire form + labelWidth?: number | string + // alignment + labelAlign?: 'left' | 'right' + // Row configuration for the entire form + rowProps?: RowProps + // Submit form on reset + submitOnReset?: boolean + // Submit form on form changing + submitOnChange?: boolean + // Col configuration for the entire form + labelCol?: Partial + // Col configuration for the entire form + wrapperCol?: Partial + + // General row style + baseRowStyle?: CSSProperties + + // General col configuration + baseColProps?: Partial + + // Form configuration rules + schemas?: FormSchema[] + // Function values used to merge into dynamic control form items + mergeDynamicData?: Recordable + // Compact mode for search forms + compact?: boolean + // Blank line span + emptySpan?: number | Partial + // Internal component size of the form + size?: 'default' | 'small' | 'large' + // Whether to disable + disabled?: boolean + // Time interval fields are mapped into multiple + fieldMapToTime?: FieldMapToTime + // Placeholder is set automatically + autoSetPlaceHolder?: boolean + // Auto submit on press enter on input + autoSubmitOnEnter?: boolean + // Check whether the information is added to the label + rulesMessageJoinLabel?: boolean + // Whether to show collapse and expand buttons + showAdvancedButton?: boolean + // Whether to focus on the first input box, only works when the first form item is input + autoFocusFirstItem?: boolean + // Automatically collapse over the specified number of rows + autoAdvancedLine?: number + // Always show lines + alwaysShowLines?: number + // Whether to show the operation button + showActionButtonGroup?: boolean + + // Reset button configuration + resetButtonOptions?: Partial + + // Confirm button configuration + submitButtonOptions?: Partial + + // Operation column configuration + actionColOptions?: Partial + + // Show reset button + showResetButton?: boolean + // Show confirmation button + showSubmitButton?: boolean + + resetFunc?: () => Promise + submitFunc?: () => Promise + transformDateFunc?: (date: any) => string + colon?: boolean +} +export interface FormSchema { + // Field name + field: string + // Event name triggered by internal value change, default change + changeEvent?: string + // Variable name bound to v-model Default value + valueField?: string + // Label name + label: string | VNode + // Auxiliary text + subLabel?: string + // Help text on the right side of the text + helpMessage?: string | string[] | ((renderCallbackParams: RenderCallbackParams) => string | string[]) + // BaseHelp component props + helpComponentProps?: Partial + // Label width, if it is passed, the labelCol and WrapperCol configured by itemProps will be invalid + labelWidth?: string | number + // Disable the adjustment of labelWidth with global settings of formModel, and manually set labelCol and wrapperCol by yourself + disabledLabelWidth?: boolean + // render component + component: ComponentType + // Component parameters + componentProps?: + | ((opt: { schema: FormSchema; tableAction: TableActionType; formActionType: FormActionType; formModel: Recordable }) => Recordable) + | object + // Required + required?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean) + + suffix?: string | number | ((values: RenderCallbackParams) => string | number) + + // Validation rules + rules?: Rule[] + // Check whether the information is added to the label + rulesMessageJoinLabel?: boolean + + // Reference formModelItem + itemProps?: Partial + + // col configuration outside formModelItem + colProps?: Partial + + // 默认值 + defaultValue?: any + + // 是否自动处理与时间相关组件的默认值 + isHandleDateDefaultValue?: boolean + + isAdvanced?: boolean + + // Matching details components + span?: number + + ifShow?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean) + + show?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean) + + // Render the content in the form-item tag + render?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string + + // Rendering col content requires outer wrapper form-item + renderColContent?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string + + renderComponentContent?: ((renderCallbackParams: RenderCallbackParams) => any) | VNode | VNode[] | string + + // Custom slot, in from-item + slot?: string + + // Custom slot, similar to renderColContent + colSlot?: string + + dynamicDisabled?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean) + + dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[] +} +export interface HelpComponentProps { + maxWidth: string + // Whether to display the serial number + showIndex: boolean + // Text list + text: any + // colour + color: string + // font size + fontSize: string + icon: string + absolute: boolean + // Positioning + position: any +} diff --git a/src/components/Form/src/types/formItem.ts b/src/components/Form/src/types/formItem.ts new file mode 100644 index 00000000..469b1986 --- /dev/null +++ b/src/components/Form/src/types/formItem.ts @@ -0,0 +1,91 @@ +import type { NamePath } from 'ant-design-vue/lib/form/interface' +import type { ColProps } from 'ant-design-vue/lib/grid/Col' +import type { HTMLAttributes, VNodeChild } from 'vue' + +export interface FormItem { + /** + * Used with label, whether to display : after label text. + * @default true + * @type boolean + */ + colon?: boolean + + /** + * The extra prompt message. It is similar to help. Usage example: to display error message and prompt message at the same time. + * @type any (string | slot) + */ + extra?: string | VNodeChild | JSX.Element + + /** + * Used with validateStatus, this option specifies the validation status icon. Recommended to be used only with Input. + * @default false + * @type boolean + */ + hasFeedback?: boolean + + /** + * The prompt message. If not provided, the prompt message will be generated by the validation rule. + * @type any (string | slot) + */ + help?: string | VNodeChild | JSX.Element + + /** + * Label test + * @type any (string | slot) + */ + label?: string | VNodeChild | JSX.Element + + /** + * The layout of label. You can set span offset to something like {span: 3, offset: 12} or sm: {span: 3, offset: 12} same as with + * @type Col + */ + labelCol?: ColProps & HTMLAttributes + + /** + * Whether provided or not, it will be generated by the validation rule. + * @default false + * @type boolean + */ + required?: boolean + + /** + * The validation status. If not provided, it will be generated by validation rule. options: 'success' 'warning' 'error' 'validating' + * @type string + */ + validateStatus?: '' | 'success' | 'warning' | 'error' | 'validating' + + /** + * The layout for input controls, same as labelCol + * @type Col + */ + wrapperCol?: ColProps + /** + * Set sub label htmlFor. + */ + htmlFor?: string + /** + * text align of label + */ + labelAlign?: 'left' | 'right' + /** + * a key of model. In the setting of validate and resetFields method, the attribute is required + */ + name?: NamePath + /** + * validation rules of form + */ + rules?: object | object[] + /** + * Whether to automatically associate form fields. In most cases, you can setting automatic association. + * If the conditions for automatic association are not met, you can manually associate them. See the notes below. + */ + autoLink?: boolean + /** + * Whether stop validate on first rule of error for this field. + */ + validateFirst?: boolean + /** + * When to validate the value of children node + */ + validateTrigger?: string | string[] | false +} diff --git a/src/components/Form/src/types/hooks.ts b/src/components/Form/src/types/hooks.ts new file mode 100644 index 00000000..7bd52810 --- /dev/null +++ b/src/components/Form/src/types/hooks.ts @@ -0,0 +1,6 @@ +export interface AdvanceState { + isAdvanced: boolean + hideAdvanceBtn: boolean + isLoad: boolean + actionSpan: number +} diff --git a/src/components/Form/src/types/index.ts b/src/components/Form/src/types/index.ts new file mode 100644 index 00000000..9f577f00 --- /dev/null +++ b/src/components/Form/src/types/index.ts @@ -0,0 +1,117 @@ +type ColSpanType = number | string +export interface ColEx { + style?: any + /** + * raster number of cells to occupy, 0 corresponds to display: none + * @default none (0) + * @type ColSpanType + */ + span?: ColSpanType + + /** + * raster order, used in flex layout mode + * @default 0 + * @type ColSpanType + */ + order?: ColSpanType + + /** + * the layout fill of flex + * @default none + * @type ColSpanType + */ + flex?: ColSpanType + + /** + * the number of cells to offset Col from the left + * @default 0 + * @type ColSpanType + */ + offset?: ColSpanType + + /** + * the number of cells that raster is moved to the right + * @default 0 + * @type ColSpanType + */ + push?: ColSpanType + + /** + * the number of cells that raster is moved to the left + * @default 0 + * @type ColSpanType + */ + pull?: ColSpanType + + /** + * <576px and also default setting, could be a span value or an object containing above props + * @type { span: ColSpanType, offset: ColSpanType } | ColSpanType + */ + xs?: { span: ColSpanType; offset: ColSpanType } | ColSpanType + + /** + * ≥576px, could be a span value or an object containing above props + * @type { span: ColSpanType, offset: ColSpanType } | ColSpanType + */ + sm?: { span: ColSpanType; offset: ColSpanType } | ColSpanType + + /** + * ≥768px, could be a span value or an object containing above props + * @type { span: ColSpanType, offset: ColSpanType } | ColSpanType + */ + md?: { span: ColSpanType; offset: ColSpanType } | ColSpanType + + /** + * ≥992px, could be a span value or an object containing above props + * @type { span: ColSpanType, offset: ColSpanType } | ColSpanType + */ + lg?: { span: ColSpanType; offset: ColSpanType } | ColSpanType + + /** + * ≥1200px, could be a span value or an object containing above props + * @type { span: ColSpanType, offset: ColSpanType } | ColSpanType + */ + xl?: { span: ColSpanType; offset: ColSpanType } | ColSpanType + + /** + * ≥1600px, could be a span value or an object containing above props + * @type { span: ColSpanType, offset: ColSpanType } | ColSpanType + */ + xxl?: { span: ColSpanType; offset: ColSpanType } | ColSpanType +} + +export type ComponentType = + | 'Input' + | 'InputGroup' + | 'InputPassword' + | 'InputSearch' + | 'InputTextArea' + | 'InputNumber' + | 'InputCountDown' + | 'Select' + | 'ApiSelect' + | 'TreeSelect' + | 'ApiTree' + | 'ApiTreeSelect' + | 'ApiRadioGroup' + | 'RadioButtonGroup' + | 'RadioGroup' + | 'Checkbox' + | 'CheckboxGroup' + | 'AutoComplete' + | 'ApiCascader' + | 'Cascader' + | 'DatePicker' + | 'MonthPicker' + | 'RangePicker' + | 'WeekPicker' + | 'TimePicker' + | 'Switch' + | 'StrengthMeter' + | 'Upload' + | 'IconPicker' + | 'Render' + | 'Slider' + | 'Rate' + | 'Divider' + | 'ApiTransfer' diff --git a/src/components/Icon/data/icons.data.ts b/src/components/Icon/data/icons.data.ts new file mode 100644 index 00000000..6c9a8ca4 --- /dev/null +++ b/src/components/Icon/data/icons.data.ts @@ -0,0 +1,793 @@ +export default { + prefix: 'ant-design', + icons: [ + 'account-book-filled', + 'account-book-outlined', + 'account-book-twotone', + 'aim-outlined', + 'alert-filled', + 'alert-outlined', + 'alert-twotone', + 'alibaba-outlined', + 'align-center-outlined', + 'align-left-outlined', + 'align-right-outlined', + 'alipay-circle-filled', + 'alipay-circle-outlined', + 'alipay-outlined', + 'alipay-square-filled', + 'aliwangwang-filled', + 'aliwangwang-outlined', + 'aliyun-outlined', + 'amazon-circle-filled', + 'amazon-outlined', + 'amazon-square-filled', + 'android-filled', + 'android-outlined', + 'ant-cloud-outlined', + 'ant-design-outlined', + 'apartment-outlined', + 'api-filled', + 'api-outlined', + 'api-twotone', + 'apple-filled', + 'apple-outlined', + 'appstore-add-outlined', + 'appstore-filled', + 'appstore-outlined', + 'appstore-twotone', + 'area-chart-outlined', + 'arrow-down-outlined', + 'arrow-left-outlined', + 'arrow-right-outlined', + 'arrow-up-outlined', + 'arrows-alt-outlined', + 'audio-filled', + 'audio-muted-outlined', + 'audio-outlined', + 'audio-twotone', + 'audit-outlined', + 'backward-filled', + 'backward-outlined', + 'bank-filled', + 'bank-outlined', + 'bank-twotone', + 'bar-chart-outlined', + 'barcode-outlined', + 'bars-outlined', + 'behance-circle-filled', + 'behance-outlined', + 'behance-square-filled', + 'behance-square-outlined', + 'bell-filled', + 'bell-outlined', + 'bell-twotone', + 'bg-colors-outlined', + 'block-outlined', + 'bold-outlined', + 'book-filled', + 'book-outlined', + 'book-twotone', + 'border-bottom-outlined', + 'border-horizontal-outlined', + 'border-inner-outlined', + 'border-left-outlined', + 'border-outer-outlined', + 'border-outlined', + 'border-right-outlined', + 'border-top-outlined', + 'border-verticle-outlined', + 'borderless-table-outlined', + 'box-plot-filled', + 'box-plot-outlined', + 'box-plot-twotone', + 'branches-outlined', + 'bug-filled', + 'bug-outlined', + 'bug-twotone', + 'build-filled', + 'build-outlined', + 'build-twotone', + 'bulb-filled', + 'bulb-outlined', + 'bulb-twotone', + 'calculator-filled', + 'calculator-outlined', + 'calculator-twotone', + 'calendar-filled', + 'calendar-outlined', + 'calendar-twotone', + 'camera-filled', + 'camera-outlined', + 'camera-twotone', + 'car-filled', + 'car-outlined', + 'car-twotone', + 'caret-down-filled', + 'caret-down-outlined', + 'caret-left-filled', + 'caret-left-outlined', + 'caret-right-filled', + 'caret-right-outlined', + 'caret-up-filled', + 'caret-up-outlined', + 'carry-out-filled', + 'carry-out-outlined', + 'carry-out-twotone', + 'check-circle-filled', + 'check-circle-outlined', + 'check-circle-twotone', + 'check-outlined', + 'check-square-filled', + 'check-square-outlined', + 'check-square-twotone', + 'chrome-filled', + 'chrome-outlined', + 'ci-circle-filled', + 'ci-circle-outlined', + 'ci-circle-twotone', + 'ci-outlined', + 'ci-twotone', + 'clear-outlined', + 'clock-circle-filled', + 'clock-circle-outlined', + 'clock-circle-twotone', + 'close-circle-filled', + 'close-circle-outlined', + 'close-circle-twotone', + 'close-outlined', + 'close-square-filled', + 'close-square-outlined', + 'close-square-twotone', + 'cloud-download-outlined', + 'cloud-filled', + 'cloud-outlined', + 'cloud-server-outlined', + 'cloud-sync-outlined', + 'cloud-twotone', + 'cloud-upload-outlined', + 'cluster-outlined', + 'code-filled', + 'code-outlined', + 'code-sandbox-circle-filled', + 'code-sandbox-outlined', + 'code-sandbox-square-filled', + 'code-twotone', + 'codepen-circle-filled', + 'codepen-circle-outlined', + 'codepen-outlined', + 'codepen-square-filled', + 'coffee-outlined', + 'column-height-outlined', + 'column-width-outlined', + 'comment-outlined', + 'compass-filled', + 'compass-outlined', + 'compass-twotone', + 'compress-outlined', + 'console-sql-outlined', + 'contacts-filled', + 'contacts-outlined', + 'contacts-twotone', + 'container-filled', + 'container-outlined', + 'container-twotone', + 'control-filled', + 'control-outlined', + 'control-twotone', + 'copy-filled', + 'copy-outlined', + 'copy-twotone', + 'copyright-circle-filled', + 'copyright-circle-outlined', + 'copyright-circle-twotone', + 'copyright-outlined', + 'copyright-twotone', + 'credit-card-filled', + 'credit-card-outlined', + 'credit-card-twotone', + 'crown-filled', + 'crown-outlined', + 'crown-twotone', + 'customer-service-filled', + 'customer-service-outlined', + 'customer-service-twotone', + 'dash-outlined', + 'dashboard-filled', + 'dashboard-outlined', + 'dashboard-twotone', + 'database-filled', + 'database-outlined', + 'database-twotone', + 'delete-column-outlined', + 'delete-filled', + 'delete-outlined', + 'delete-row-outlined', + 'delete-twotone', + 'delivered-procedure-outlined', + 'deployment-unit-outlined', + 'desktop-outlined', + 'diff-filled', + 'diff-outlined', + 'diff-twotone', + 'dingding-outlined', + 'dingtalk-circle-filled', + 'dingtalk-outlined', + 'dingtalk-square-filled', + 'disconnect-outlined', + 'dislike-filled', + 'dislike-outlined', + 'dislike-twotone', + 'dollar-circle-filled', + 'dollar-circle-outlined', + 'dollar-circle-twotone', + 'dollar-outlined', + 'dollar-twotone', + 'dot-chart-outlined', + 'double-left-outlined', + 'double-right-outlined', + 'down-circle-filled', + 'down-circle-outlined', + 'down-circle-twotone', + 'down-outlined', + 'down-square-filled', + 'down-square-outlined', + 'down-square-twotone', + 'download-outlined', + 'drag-outlined', + 'dribbble-circle-filled', + 'dribbble-outlined', + 'dribbble-square-filled', + 'dribbble-square-outlined', + 'dropbox-circle-filled', + 'dropbox-outlined', + 'dropbox-square-filled', + 'edit-filled', + 'edit-outlined', + 'edit-twotone', + 'ellipsis-outlined', + 'enter-outlined', + 'environment-filled', + 'environment-outlined', + 'environment-twotone', + 'euro-circle-filled', + 'euro-circle-outlined', + 'euro-circle-twotone', + 'euro-outlined', + 'euro-twotone', + 'exception-outlined', + 'exclamation-circle-filled', + 'exclamation-circle-outlined', + 'exclamation-circle-twotone', + 'exclamation-outlined', + 'expand-alt-outlined', + 'expand-outlined', + 'experiment-filled', + 'experiment-outlined', + 'experiment-twotone', + 'export-outlined', + 'eye-filled', + 'eye-invisible-filled', + 'eye-invisible-outlined', + 'eye-invisible-twotone', + 'eye-outlined', + 'eye-twotone', + 'facebook-filled', + 'facebook-outlined', + 'fall-outlined', + 'fast-backward-filled', + 'fast-backward-outlined', + 'fast-forward-filled', + 'fast-forward-outlined', + 'field-binary-outlined', + 'field-number-outlined', + 'field-string-outlined', + 'field-time-outlined', + 'file-add-filled', + 'file-add-outlined', + 'file-add-twotone', + 'file-done-outlined', + 'file-excel-filled', + 'file-excel-outlined', + 'file-excel-twotone', + 'file-exclamation-filled', + 'file-exclamation-outlined', + 'file-exclamation-twotone', + 'file-filled', + 'file-gif-outlined', + 'file-image-filled', + 'file-image-outlined', + 'file-image-twotone', + 'file-jpg-outlined', + 'file-markdown-filled', + 'file-markdown-outlined', + 'file-markdown-twotone', + 'file-outlined', + 'file-pdf-filled', + 'file-pdf-outlined', + 'file-pdf-twotone', + 'file-ppt-filled', + 'file-ppt-outlined', + 'file-ppt-twotone', + 'file-protect-outlined', + 'file-search-outlined', + 'file-sync-outlined', + 'file-text-filled', + 'file-text-outlined', + 'file-text-twotone', + 'file-twotone', + 'file-unknown-filled', + 'file-unknown-outlined', + 'file-unknown-twotone', + 'file-word-filled', + 'file-word-outlined', + 'file-word-twotone', + 'file-zip-filled', + 'file-zip-outlined', + 'file-zip-twotone', + 'filter-filled', + 'filter-outlined', + 'filter-twotone', + 'fire-filled', + 'fire-outlined', + 'fire-twotone', + 'flag-filled', + 'flag-outlined', + 'flag-twotone', + 'folder-add-filled', + 'folder-add-outlined', + 'folder-add-twotone', + 'folder-filled', + 'folder-open-filled', + 'folder-open-outlined', + 'folder-open-twotone', + 'folder-outlined', + 'folder-twotone', + 'folder-view-outlined', + 'font-colors-outlined', + 'font-size-outlined', + 'fork-outlined', + 'form-outlined', + 'format-painter-filled', + 'format-painter-outlined', + 'forward-filled', + 'forward-outlined', + 'frown-filled', + 'frown-outlined', + 'frown-twotone', + 'fullscreen-exit-outlined', + 'fullscreen-outlined', + 'function-outlined', + 'fund-filled', + 'fund-outlined', + 'fund-projection-screen-outlined', + 'fund-twotone', + 'fund-view-outlined', + 'funnel-plot-filled', + 'funnel-plot-outlined', + 'funnel-plot-twotone', + 'gateway-outlined', + 'gif-outlined', + 'gift-filled', + 'gift-outlined', + 'gift-twotone', + 'github-filled', + 'github-outlined', + 'gitlab-filled', + 'gitlab-outlined', + 'global-outlined', + 'gold-filled', + 'gold-outlined', + 'gold-twotone', + 'golden-filled', + 'google-circle-filled', + 'google-outlined', + 'google-plus-circle-filled', + 'google-plus-outlined', + 'google-plus-square-filled', + 'google-square-filled', + 'group-outlined', + 'hdd-filled', + 'hdd-outlined', + 'hdd-twotone', + 'heart-filled', + 'heart-outlined', + 'heart-twotone', + 'heat-map-outlined', + 'highlight-filled', + 'highlight-outlined', + 'highlight-twotone', + 'history-outlined', + 'home-filled', + 'home-outlined', + 'home-twotone', + 'hourglass-filled', + 'hourglass-outlined', + 'hourglass-twotone', + 'html5-filled', + 'html5-outlined', + 'html5-twotone', + 'idcard-filled', + 'idcard-outlined', + 'idcard-twotone', + 'ie-circle-filled', + 'ie-outlined', + 'ie-square-filled', + 'import-outlined', + 'inbox-outlined', + 'info-circle-filled', + 'info-circle-outlined', + 'info-circle-twotone', + 'info-outlined', + 'insert-row-above-outlined', + 'insert-row-below-outlined', + 'insert-row-left-outlined', + 'insert-row-right-outlined', + 'instagram-filled', + 'instagram-outlined', + 'insurance-filled', + 'insurance-outlined', + 'insurance-twotone', + 'interaction-filled', + 'interaction-outlined', + 'interaction-twotone', + 'issues-close-outlined', + 'italic-outlined', + 'key-outlined', + 'laptop-outlined', + 'layout-filled', + 'layout-outlined', + 'layout-twotone', + 'left-circle-filled', + 'left-circle-outlined', + 'left-circle-twotone', + 'left-outlined', + 'left-square-filled', + 'left-square-outlined', + 'left-square-twotone', + 'like-filled', + 'like-outlined', + 'like-twotone', + 'line-chart-outlined', + 'line-height-outlined', + 'line-outlined', + 'link-outlined', + 'linkedin-filled', + 'linkedin-outlined', + 'loading-3-quarters-outlined', + 'loading-outlined', + 'lock-filled', + 'lock-outlined', + 'lock-twotone', + 'login-outlined', + 'logout-outlined', + 'mac-command-filled', + 'mac-command-outlined', + 'mail-filled', + 'mail-outlined', + 'mail-twotone', + 'man-outlined', + 'medicine-box-filled', + 'medicine-box-outlined', + 'medicine-box-twotone', + 'medium-circle-filled', + 'medium-outlined', + 'medium-square-filled', + 'medium-workmark-outlined', + 'meh-filled', + 'meh-outlined', + 'meh-twotone', + 'menu-fold-outlined', + 'menu-outlined', + 'menu-unfold-outlined', + 'merge-cells-outlined', + 'message-filled', + 'message-outlined', + 'message-twotone', + 'minus-circle-filled', + 'minus-circle-outlined', + 'minus-circle-twotone', + 'minus-outlined', + 'minus-square-filled', + 'minus-square-outlined', + 'minus-square-twotone', + 'mobile-filled', + 'mobile-outlined', + 'mobile-twotone', + 'money-collect-filled', + 'money-collect-outlined', + 'money-collect-twotone', + 'monitor-outlined', + 'more-outlined', + 'node-collapse-outlined', + 'node-expand-outlined', + 'node-index-outlined', + 'notification-filled', + 'notification-outlined', + 'notification-twotone', + 'number-outlined', + 'one-to-one-outlined', + 'ordered-list-outlined', + 'paper-clip-outlined', + 'partition-outlined', + 'pause-circle-filled', + 'pause-circle-outlined', + 'pause-circle-twotone', + 'pause-outlined', + 'pay-circle-filled', + 'pay-circle-outlined', + 'percentage-outlined', + 'phone-filled', + 'phone-outlined', + 'phone-twotone', + 'pic-center-outlined', + 'pic-left-outlined', + 'pic-right-outlined', + 'picture-filled', + 'picture-outlined', + 'picture-twotone', + 'pie-chart-filled', + 'pie-chart-outlined', + 'pie-chart-twotone', + 'play-circle-filled', + 'play-circle-outlined', + 'play-circle-twotone', + 'play-square-filled', + 'play-square-outlined', + 'play-square-twotone', + 'plus-circle-filled', + 'plus-circle-outlined', + 'plus-circle-twotone', + 'plus-outlined', + 'plus-square-filled', + 'plus-square-outlined', + 'plus-square-twotone', + 'pound-circle-filled', + 'pound-circle-outlined', + 'pound-circle-twotone', + 'pound-outlined', + 'poweroff-outlined', + 'printer-filled', + 'printer-outlined', + 'printer-twotone', + 'profile-filled', + 'profile-outlined', + 'profile-twotone', + 'project-filled', + 'project-outlined', + 'project-twotone', + 'property-safety-filled', + 'property-safety-outlined', + 'property-safety-twotone', + 'pull-request-outlined', + 'pushpin-filled', + 'pushpin-outlined', + 'pushpin-twotone', + 'qq-circle-filled', + 'qq-outlined', + 'qq-square-filled', + 'qrcode-outlined', + 'question-circle-filled', + 'question-circle-outlined', + 'question-circle-twotone', + 'question-outlined', + 'radar-chart-outlined', + 'radius-bottomleft-outlined', + 'radius-bottomright-outlined', + 'radius-setting-outlined', + 'radius-upleft-outlined', + 'radius-upright-outlined', + 'read-filled', + 'read-outlined', + 'reconciliation-filled', + 'reconciliation-outlined', + 'reconciliation-twotone', + 'red-envelope-filled', + 'red-envelope-outlined', + 'red-envelope-twotone', + 'reddit-circle-filled', + 'reddit-outlined', + 'reddit-square-filled', + 'redo-outlined', + 'reload-outlined', + 'rest-filled', + 'rest-outlined', + 'rest-twotone', + 'retweet-outlined', + 'right-circle-filled', + 'right-circle-outlined', + 'right-circle-twotone', + 'right-outlined', + 'right-square-filled', + 'right-square-outlined', + 'right-square-twotone', + 'rise-outlined', + 'robot-filled', + 'robot-outlined', + 'rocket-filled', + 'rocket-outlined', + 'rocket-twotone', + 'rollback-outlined', + 'rotate-left-outlined', + 'rotate-right-outlined', + 'safety-certificate-filled', + 'safety-certificate-outlined', + 'safety-certificate-twotone', + 'safety-outlined', + 'save-filled', + 'save-outlined', + 'save-twotone', + 'scan-outlined', + 'schedule-filled', + 'schedule-outlined', + 'schedule-twotone', + 'scissor-outlined', + 'search-outlined', + 'security-scan-filled', + 'security-scan-outlined', + 'security-scan-twotone', + 'select-outlined', + 'send-outlined', + 'setting-filled', + 'setting-outlined', + 'setting-twotone', + 'shake-outlined', + 'share-alt-outlined', + 'shop-filled', + 'shop-outlined', + 'shop-twotone', + 'shopping-cart-outlined', + 'shopping-filled', + 'shopping-outlined', + 'shopping-twotone', + 'shrink-outlined', + 'signal-filled', + 'sisternode-outlined', + 'sketch-circle-filled', + 'sketch-outlined', + 'sketch-square-filled', + 'skin-filled', + 'skin-outlined', + 'skin-twotone', + 'skype-filled', + 'skype-outlined', + 'slack-circle-filled', + 'slack-outlined', + 'slack-square-filled', + 'slack-square-outlined', + 'sliders-filled', + 'sliders-outlined', + 'sliders-twotone', + 'small-dash-outlined', + 'smile-filled', + 'smile-outlined', + 'smile-twotone', + 'snippets-filled', + 'snippets-outlined', + 'snippets-twotone', + 'solution-outlined', + 'sort-ascending-outlined', + 'sort-descending-outlined', + 'sound-filled', + 'sound-outlined', + 'sound-twotone', + 'split-cells-outlined', + 'star-filled', + 'star-outlined', + 'star-twotone', + 'step-backward-filled', + 'step-backward-outlined', + 'step-forward-filled', + 'step-forward-outlined', + 'stock-outlined', + 'stop-filled', + 'stop-outlined', + 'stop-twotone', + 'strikethrough-outlined', + 'subnode-outlined', + 'swap-left-outlined', + 'swap-outlined', + 'swap-right-outlined', + 'switcher-filled', + 'switcher-outlined', + 'switcher-twotone', + 'sync-outlined', + 'table-outlined', + 'tablet-filled', + 'tablet-outlined', + 'tablet-twotone', + 'tag-filled', + 'tag-outlined', + 'tag-twotone', + 'tags-filled', + 'tags-outlined', + 'tags-twotone', + 'taobao-circle-filled', + 'taobao-circle-outlined', + 'taobao-outlined', + 'taobao-square-filled', + 'team-outlined', + 'thunderbolt-filled', + 'thunderbolt-outlined', + 'thunderbolt-twotone', + 'to-top-outlined', + 'tool-filled', + 'tool-outlined', + 'tool-twotone', + 'trademark-circle-filled', + 'trademark-circle-outlined', + 'trademark-circle-twotone', + 'trademark-outlined', + 'transaction-outlined', + 'translation-outlined', + 'trophy-filled', + 'trophy-outlined', + 'trophy-twotone', + 'twitter-circle-filled', + 'twitter-outlined', + 'twitter-square-filled', + 'underline-outlined', + 'undo-outlined', + 'ungroup-outlined', + 'unlock-filled', + 'unlock-outlined', + 'unlock-twotone', + 'unordered-list-outlined', + 'up-circle-filled', + 'up-circle-outlined', + 'up-circle-twotone', + 'up-outlined', + 'up-square-filled', + 'up-square-outlined', + 'up-square-twotone', + 'upload-outlined', + 'usb-filled', + 'usb-outlined', + 'usb-twotone', + 'user-add-outlined', + 'user-delete-outlined', + 'user-outlined', + 'user-switch-outlined', + 'usergroup-add-outlined', + 'usergroup-delete-outlined', + 'verified-outlined', + 'vertical-align-bottom-outlined', + 'vertical-align-middle-outlined', + 'vertical-align-top-outlined', + 'vertical-left-outlined', + 'vertical-right-outlined', + 'video-camera-add-outlined', + 'video-camera-filled', + 'video-camera-outlined', + 'video-camera-twotone', + 'wallet-filled', + 'wallet-outlined', + 'wallet-twotone', + 'warning-filled', + 'warning-outlined', + 'warning-twotone', + 'wechat-filled', + 'wechat-outlined', + 'weibo-circle-filled', + 'weibo-circle-outlined', + 'weibo-outlined', + 'weibo-square-filled', + 'weibo-square-outlined', + 'whats-app-outlined', + 'wifi-outlined', + 'windows-filled', + 'windows-outlined', + 'woman-outlined', + 'yahoo-filled', + 'yahoo-outlined', + 'youtube-filled', + 'youtube-outlined', + 'yuque-filled', + 'yuque-outlined', + 'zhihu-circle-filled', + 'zhihu-outlined', + 'zhihu-square-filled', + 'zoom-in-outlined', + 'zoom-out-outlined' + ] +} diff --git a/src/components/Icon/index.ts b/src/components/Icon/index.ts new file mode 100644 index 00000000..bda6c81d --- /dev/null +++ b/src/components/Icon/index.ts @@ -0,0 +1,7 @@ +import Icon from './src/Icon.vue' +import SvgIcon from './src/SvgIcon.vue' +import IconPicker from './src/IconPicker.vue' + +export { Icon, IconPicker, SvgIcon } + +export default Icon diff --git a/src/components/Icon/src/Icon.vue b/src/components/Icon/src/Icon.vue new file mode 100644 index 00000000..d065db03 --- /dev/null +++ b/src/components/Icon/src/Icon.vue @@ -0,0 +1,94 @@ + + + diff --git a/src/components/Icon/src/IconPicker.vue b/src/components/Icon/src/IconPicker.vue new file mode 100644 index 00000000..e355ed87 --- /dev/null +++ b/src/components/Icon/src/IconPicker.vue @@ -0,0 +1,176 @@ + + + diff --git a/src/components/Icon/src/SvgIcon.vue b/src/components/Icon/src/SvgIcon.vue new file mode 100644 index 00000000..e5479345 --- /dev/null +++ b/src/components/Icon/src/SvgIcon.vue @@ -0,0 +1,56 @@ + + + diff --git a/src/components/Loading/index.ts b/src/components/Loading/index.ts new file mode 100644 index 00000000..0a9b22cd --- /dev/null +++ b/src/components/Loading/index.ts @@ -0,0 +1,5 @@ +import Loading from './src/Loading.vue' + +export { Loading } +export { useLoading } from './src/useLoading' +export { createLoading } from './src/createLoading' diff --git a/src/components/Loading/src/Loading.vue b/src/components/Loading/src/Loading.vue new file mode 100644 index 00000000..9988c150 --- /dev/null +++ b/src/components/Loading/src/Loading.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/components/Loading/src/createLoading.ts b/src/components/Loading/src/createLoading.ts new file mode 100644 index 00000000..09976118 --- /dev/null +++ b/src/components/Loading/src/createLoading.ts @@ -0,0 +1,65 @@ +import { VNode, defineComponent } from 'vue' +import type { LoadingProps } from './typing' + +import { createVNode, render, reactive, h } from 'vue' +import Loading from './Loading.vue' + +export function createLoading(props?: Partial, target?: HTMLElement, wait = false) { + let vm: Nullable = null + const data = reactive({ + tip: '', + loading: true, + ...props + }) + + const LoadingWrap = defineComponent({ + render() { + return h(Loading, { ...data }) + } + }) + + vm = createVNode(LoadingWrap) + + if (wait) { + // TODO fix https://gitee.com/xingyuv/issues/438 + setTimeout(() => { + render(vm, document.createElement('div')) + }, 0) + } else { + render(vm, document.createElement('div')) + } + + function close() { + if (vm?.el && vm.el.parentNode) { + vm.el.parentNode.removeChild(vm.el) + } + } + + function open(target: HTMLElement = document.body) { + if (!vm || !vm.el) { + return + } + target.appendChild(vm.el as HTMLElement) + } + + if (target) { + open(target) + } + return { + vm, + close, + open, + setTip: (tip: string) => { + data.tip = tip + }, + setLoading: (loading: boolean) => { + data.loading = loading + }, + get loading() { + return data.loading + }, + get $el() { + return vm?.el as HTMLElement + } + } +} diff --git a/src/components/Loading/src/typing.ts b/src/components/Loading/src/typing.ts new file mode 100644 index 00000000..78a2288e --- /dev/null +++ b/src/components/Loading/src/typing.ts @@ -0,0 +1,10 @@ +import { SizeEnum } from '@/enums/sizeEnum' + +export interface LoadingProps { + tip: string + size: SizeEnum + absolute: boolean + loading: boolean + background: string + theme: 'dark' | 'light' +} diff --git a/src/components/Loading/src/useLoading.ts b/src/components/Loading/src/useLoading.ts new file mode 100644 index 00000000..312291f8 --- /dev/null +++ b/src/components/Loading/src/useLoading.ts @@ -0,0 +1,47 @@ +import { unref } from 'vue' +import { createLoading } from './createLoading' +import type { LoadingProps } from './typing' +import type { Ref } from 'vue' + +export interface UseLoadingOptions { + target?: any + props?: Partial +} + +interface Fn { + (): void +} + +export function useLoading(props: Partial): [Fn, Fn, (string) => void] +export function useLoading(opt: Partial): [Fn, Fn, (string) => void] + +export function useLoading(opt: Partial | Partial): [Fn, Fn, (string) => void] { + let props: Partial + let target: HTMLElement | Ref = document.body + + if (Reflect.has(opt, 'target') || Reflect.has(opt, 'props')) { + const options = opt as Partial + props = options.props || {} + target = options.target || document.body + } else { + props = opt as Partial + } + + const instance = createLoading(props, undefined, true) + + const open = (): void => { + const t = unref(target as Ref) + if (!t) return + instance.open(t) + } + + const close = (): void => { + instance.close() + } + + const setTip = (tip: string) => { + instance.setTip(tip) + } + + return [open, close, setTip] +} diff --git a/src/components/Markdown/index.ts b/src/components/Markdown/index.ts new file mode 100644 index 00000000..f8daa64d --- /dev/null +++ b/src/components/Markdown/index.ts @@ -0,0 +1,7 @@ +import { withInstall } from '@/utils' +import markDown from './src/Markdown.vue' +import markDownViewer from './src/MarkdownViewer.vue' + +export const MarkDown = withInstall(markDown) +export const MarkdownViewer = withInstall(markDownViewer) +export * from './src/typing' diff --git a/src/components/Markdown/src/Markdown.vue b/src/components/Markdown/src/Markdown.vue new file mode 100644 index 00000000..44952d0b --- /dev/null +++ b/src/components/Markdown/src/Markdown.vue @@ -0,0 +1,140 @@ + + diff --git a/src/components/Markdown/src/MarkdownViewer.vue b/src/components/Markdown/src/MarkdownViewer.vue new file mode 100644 index 00000000..6ec7e070 --- /dev/null +++ b/src/components/Markdown/src/MarkdownViewer.vue @@ -0,0 +1,62 @@ + + + diff --git a/src/components/Markdown/src/getTheme.ts b/src/components/Markdown/src/getTheme.ts new file mode 100644 index 00000000..9d2e3291 --- /dev/null +++ b/src/components/Markdown/src/getTheme.ts @@ -0,0 +1,16 @@ +/** + * 获取主题类型 深色浅色模式 对应的值 + * @param darkModeVal 深色模式值 + * @param themeMode 主题类型——外观(默认), 内容, 代码块 + */ +export const getTheme = (darkModeVal: 'light' | 'dark' | string, themeMode: 'default' | 'content' | 'code' = 'default') => { + const isDark = darkModeVal === 'dark' + switch (themeMode) { + case 'default': + return isDark ? 'dark' : 'classic' + case 'content': + return isDark ? 'dark' : 'light' + case 'code': + return isDark ? 'dracula' : 'github' + } +} diff --git a/src/components/Markdown/src/typing.ts b/src/components/Markdown/src/typing.ts new file mode 100644 index 00000000..6cea79f1 --- /dev/null +++ b/src/components/Markdown/src/typing.ts @@ -0,0 +1,4 @@ +import Vditor from 'vditor' +export interface MarkDownActionType { + getVditor: () => Vditor +} diff --git a/src/components/Menu/index.ts b/src/components/Menu/index.ts new file mode 100644 index 00000000..42f45c91 --- /dev/null +++ b/src/components/Menu/index.ts @@ -0,0 +1,3 @@ +import BasicMenu from './src/BasicMenu.vue' + +export { BasicMenu } diff --git a/src/components/Menu/src/BasicMenu.vue b/src/components/Menu/src/BasicMenu.vue new file mode 100644 index 00000000..d0cf6f8b --- /dev/null +++ b/src/components/Menu/src/BasicMenu.vue @@ -0,0 +1,137 @@ + + + diff --git a/src/components/Menu/src/components/BasicMenuItem.vue b/src/components/Menu/src/components/BasicMenuItem.vue new file mode 100644 index 00000000..06f00c4f --- /dev/null +++ b/src/components/Menu/src/components/BasicMenuItem.vue @@ -0,0 +1,14 @@ + + diff --git a/src/components/Menu/src/components/BasicSubMenuItem.vue b/src/components/Menu/src/components/BasicSubMenuItem.vue new file mode 100644 index 00000000..12465d55 --- /dev/null +++ b/src/components/Menu/src/components/BasicSubMenuItem.vue @@ -0,0 +1,36 @@ + + diff --git a/src/components/Menu/src/components/MenuItemContent.vue b/src/components/Menu/src/components/MenuItemContent.vue new file mode 100644 index 00000000..ec678cc8 --- /dev/null +++ b/src/components/Menu/src/components/MenuItemContent.vue @@ -0,0 +1,19 @@ + + diff --git a/src/components/Menu/src/index.less b/src/components/Menu/src/index.less new file mode 100644 index 00000000..8bfbb0df --- /dev/null +++ b/src/components/Menu/src/index.less @@ -0,0 +1,74 @@ +@basic-menu-prefix-cls: ~'@{namespace}-basic-menu'; + +.app-top-menu-popup { + min-width: 150px; +} + +.@{basic-menu-prefix-cls} { + width: 100%; + + .ant-menu-item { + transition: unset; + } + + &__sidebar-hor { + &.ant-menu-horizontal { + display: flex; + align-items: center; + + &.ant-menu-dark { + background-color: transparent; + + .ant-menu-submenu:hover, + .ant-menu-item-open, + .ant-menu-submenu-open, + .ant-menu-item-selected, + .ant-menu-submenu-selected, + .ant-menu-item:hover, + .ant-menu-item-active, + .ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open, + .ant-menu-submenu-active, + .ant-menu-submenu-title:hover { + color: #fff; + background-color: @top-menu-active-bg-color !important; + } + + .ant-menu-item:hover, + .ant-menu-item-active, + .ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open, + .ant-menu-submenu-active, + .ant-menu-submenu-title:hover { + background-color: @top-menu-active-bg-color; + } + + .@{basic-menu-prefix-cls}-item__level1 { + background-color: transparent; + + &.ant-menu-item-selected, + &.ant-menu-submenu-selected { + background-color: @top-menu-active-bg-color !important; + } + } + + .ant-menu-item, + .ant-menu-submenu { + &.@{basic-menu-prefix-cls}-item__level1, + .ant-menu-submenu-title { + height: @header-height; + line-height: @header-height; + } + } + } + } + } + + .ant-menu-submenu, + .ant-menu-submenu-inline { + transition: unset; + } + + .ant-menu-inline.ant-menu-sub { + box-shadow: unset !important; + transition: unset; + } +} diff --git a/src/components/Menu/src/props.ts b/src/components/Menu/src/props.ts new file mode 100644 index 00000000..60e01b3e --- /dev/null +++ b/src/components/Menu/src/props.ts @@ -0,0 +1,59 @@ +import type { Menu } from '@/router/types' + +import { MenuModeEnum, MenuTypeEnum } from '@/enums/menuEnum' +import { ThemeEnum } from '@/enums/appEnum' +import { propTypes } from '@/utils/propTypes' +import type { MenuTheme } from 'ant-design-vue' +import type { MenuMode } from 'ant-design-vue/lib/menu/src/interface' +export const basicProps = { + items: { + type: Array as PropType, + default: () => [] + }, + collapsedShowTitle: propTypes.bool, + // 最好是4 倍数 + inlineIndent: propTypes.number.def(20), + // 菜单组件的mode属性 + mode: { + type: String as PropType, + default: MenuModeEnum.INLINE + }, + + type: { + type: String as PropType, + default: MenuTypeEnum.MIX + }, + theme: { + type: String as PropType, + default: ThemeEnum.DARK + }, + inlineCollapsed: propTypes.bool, + mixSider: propTypes.bool, + + isHorizontal: propTypes.bool, + accordion: propTypes.bool.def(true), + beforeClickFn: { + type: Function as PropType<(key: string) => Promise> + } +} + +export const itemProps = { + item: { + type: Object as PropType, + default: () => ({}) + }, + level: propTypes.number, + theme: propTypes.oneOf(['dark', 'light']), + showTitle: propTypes.bool, + isHorizontal: propTypes.bool +} + +export const contentProps = { + item: { + type: Object as PropType, + default: null + }, + showTitle: propTypes.bool.def(true), + level: propTypes.number.def(0), + isHorizontal: propTypes.bool.def(true) +} diff --git a/src/components/Menu/src/types.ts b/src/components/Menu/src/types.ts new file mode 100644 index 00000000..ad8077fd --- /dev/null +++ b/src/components/Menu/src/types.ts @@ -0,0 +1,25 @@ +// import { ComputedRef } from 'vue'; +// import { ThemeEnum } from '@/enums/appEnum'; +// import { MenuModeEnum } from '@/enums/menuEnum'; +export interface MenuState { + // 默认选中的列表 + defaultSelectedKeys: string[] + + // 模式 + // mode: MenuModeEnum; + + // // 主题 + // theme: ComputedRef | ThemeEnum; + + // 缩进 + inlineIndent?: number + + // 展开数组 + openKeys: string[] + + // 当前选中的菜单项 key 数组 + selectedKeys: string[] + + // 收缩状态下展开的数组 + collapsedOpenKeys: string[] +} diff --git a/src/components/Menu/src/useOpenKeys.ts b/src/components/Menu/src/useOpenKeys.ts new file mode 100644 index 00000000..8c70d5f2 --- /dev/null +++ b/src/components/Menu/src/useOpenKeys.ts @@ -0,0 +1,78 @@ +import { MenuModeEnum } from '@/enums/menuEnum' +import type { Menu as MenuType } from '@/router/types' +import type { MenuState } from './types' + +import { computed, Ref, toRaw } from 'vue' + +import { unref } from 'vue' +import { uniq } from 'lodash-es' +import { useMenuSetting } from '@/hooks/setting/useMenuSetting' +import { getAllParentPath } from '@/router/helper/menuHelper' +import { useTimeoutFn } from '@/hooks/core/useTimeout' + +export function useOpenKeys(menuState: MenuState, menus: Ref, mode: Ref, accordion: Ref) { + const { getCollapsed, getIsMixSidebar } = useMenuSetting() + + async function setOpenKeys(path: string) { + if (mode.value === MenuModeEnum.HORIZONTAL) { + return + } + const native = unref(getIsMixSidebar) + useTimeoutFn( + () => { + const menuList = toRaw(menus.value) + if (menuList?.length === 0) { + menuState.openKeys = [] + return + } + if (!unref(accordion)) { + menuState.openKeys = uniq([...menuState.openKeys, ...getAllParentPath(menuList, path)]) + } else { + menuState.openKeys = getAllParentPath(menuList, path) + } + }, + 16, + !native + ) + } + + const getOpenKeys = computed(() => { + const collapse = unref(getIsMixSidebar) ? false : unref(getCollapsed) + + return collapse ? menuState.collapsedOpenKeys : menuState.openKeys + }) + + /** + * @description: 重置值 + */ + function resetKeys() { + menuState.selectedKeys = [] + menuState.openKeys = [] + } + + function handleOpenChange(openKeys: string[]) { + if (unref(mode) === MenuModeEnum.HORIZONTAL || !unref(accordion) || unref(getIsMixSidebar)) { + menuState.openKeys = openKeys + } else { + // const menuList = toRaw(menus.value); + // getAllParentPath(menuList, path); + const rootSubMenuKeys: string[] = [] + for (const { children, path } of unref(menus)) { + if (children && children.length > 0) { + rootSubMenuKeys.push(path) + } + } + if (!unref(getCollapsed)) { + const latestOpenKey = openKeys.find((key) => menuState.openKeys.indexOf(key) === -1) + if (rootSubMenuKeys.indexOf(latestOpenKey as string) === -1) { + menuState.openKeys = openKeys + } else { + menuState.openKeys = latestOpenKey ? [latestOpenKey] : [] + } + } else { + menuState.collapsedOpenKeys = openKeys + } + } + } + return { setOpenKeys, resetKeys, getOpenKeys, handleOpenChange } +} diff --git a/src/components/Modal/index.ts b/src/components/Modal/index.ts new file mode 100644 index 00000000..0872c8f4 --- /dev/null +++ b/src/components/Modal/index.ts @@ -0,0 +1,8 @@ +import { withInstall } from '@/utils' +import './src/index.less' +import basicModal from './src/BasicModal.vue' + +export const BasicModal = withInstall(basicModal) +export { useModalContext } from './src/hooks/useModalContext' +export { useModal, useModalInner } from './src/hooks/useModal' +export * from './src/typing' diff --git a/src/components/Modal/src/BasicModal.vue b/src/components/Modal/src/BasicModal.vue new file mode 100644 index 00000000..bfd56048 --- /dev/null +++ b/src/components/Modal/src/BasicModal.vue @@ -0,0 +1,205 @@ + + diff --git a/src/components/Modal/src/components/Modal.tsx b/src/components/Modal/src/components/Modal.tsx new file mode 100644 index 00000000..cba739d2 --- /dev/null +++ b/src/components/Modal/src/components/Modal.tsx @@ -0,0 +1,31 @@ +import { Modal } from 'ant-design-vue' +import { defineComponent, toRefs, unref } from 'vue' +import { basicProps } from '../props' +import { useModalDragMove } from '../hooks/useModalDrag' +import { useAttrs } from '@/hooks/core/useAttrs' +import { extendSlots } from '@/utils/helper/tsxHelper' + +export default defineComponent({ + name: 'Modal', + inheritAttrs: false, + props: basicProps, + emits: ['cancel'], + setup(props, { slots, emit }) { + const { visible, draggable, destroyOnClose } = toRefs(props) + const attrs = useAttrs() + useModalDragMove({ + visible, + destroyOnClose, + draggable + }) + + const onCancel = (e: Event) => { + emit('cancel', e) + } + + return () => { + const propsData = { ...unref(attrs), ...props, onCancel } as Recordable + return {extendSlots(slots)} + } + } +}) diff --git a/src/components/Modal/src/components/ModalClose.vue b/src/components/Modal/src/components/ModalClose.vue new file mode 100644 index 00000000..e2d38554 --- /dev/null +++ b/src/components/Modal/src/components/ModalClose.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/components/Modal/src/components/ModalFooter.vue b/src/components/Modal/src/components/ModalFooter.vue new file mode 100644 index 00000000..558ecb11 --- /dev/null +++ b/src/components/Modal/src/components/ModalFooter.vue @@ -0,0 +1,28 @@ + + diff --git a/src/components/Modal/src/components/ModalHeader.vue b/src/components/Modal/src/components/ModalHeader.vue new file mode 100644 index 00000000..48c5d2fb --- /dev/null +++ b/src/components/Modal/src/components/ModalHeader.vue @@ -0,0 +1,15 @@ + + diff --git a/src/components/Modal/src/components/ModalWrapper.vue b/src/components/Modal/src/components/ModalWrapper.vue new file mode 100644 index 00000000..f9cd420b --- /dev/null +++ b/src/components/Modal/src/components/ModalWrapper.vue @@ -0,0 +1,140 @@ + + diff --git a/src/components/Modal/src/hooks/useModal.ts b/src/components/Modal/src/hooks/useModal.ts new file mode 100644 index 00000000..6e47076e --- /dev/null +++ b/src/components/Modal/src/hooks/useModal.ts @@ -0,0 +1,148 @@ +import type { UseModalReturnType, ModalMethods, ModalProps, ReturnMethods, UseModalInnerReturnType } from '../typing' +import { ref, onUnmounted, unref, getCurrentInstance, reactive, watchEffect, nextTick, toRaw } from 'vue' +import { isProdMode } from '@/utils/env' +import { isFunction } from '@/utils/is' +import { isEqual } from 'lodash-es' +import { tryOnUnmounted } from '@vueuse/core' +import { error } from '@/utils/log' +import { computed } from 'vue' + +const dataTransfer = reactive({}) + +const visibleData = reactive<{ [key: number]: boolean }>({}) + +/** + * @description: Applicable to independent modal and call outside + */ +export function useModal(): UseModalReturnType { + const modal = ref>(null) + const loaded = ref>(false) + const uid = ref('') + + function register(modalMethod: ModalMethods, uuid: string) { + if (!getCurrentInstance()) { + throw new Error('useModal() can only be used inside setup() or functional components!') + } + uid.value = uuid + isProdMode() && + onUnmounted(() => { + modal.value = null + loaded.value = false + dataTransfer[unref(uid)] = null + }) + if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return + + modal.value = modalMethod + loaded.value = true + modalMethod.emitVisible = (visible: boolean, uid: number) => { + visibleData[uid] = visible + } + } + + const getInstance = () => { + const instance = unref(modal) + if (!instance) { + error('useModal instance is undefined!') + } + return instance + } + + const methods: ReturnMethods = { + setModalProps: (props: Partial): void => { + getInstance()?.setModalProps(props) + }, + + getVisible: computed((): boolean => { + return visibleData[~~unref(uid)] + }), + + redoModalHeight: () => { + getInstance()?.redoModalHeight?.() + }, + + openModal: (visible = true, data?: T, openOnSet = true): void => { + getInstance()?.setModalProps({ + visible: visible + }) + + if (!data) return + const id = unref(uid) + if (openOnSet) { + dataTransfer[id] = null + dataTransfer[id] = toRaw(data) + return + } + const equal = isEqual(toRaw(dataTransfer[id]), toRaw(data)) + if (!equal) { + dataTransfer[id] = toRaw(data) + } + }, + + closeModal: () => { + getInstance()?.setModalProps({ visible: false }) + } + } + return [register, methods] +} + +export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => { + const modalInstanceRef = ref>(null) + const currentInstance = getCurrentInstance() + const uidRef = ref('') + + const getInstance = () => { + const instance = unref(modalInstanceRef) + if (!instance) { + error('useModalInner instance is undefined!') + } + return instance + } + + const register = (modalInstance: ModalMethods, uuid: string) => { + isProdMode() && + tryOnUnmounted(() => { + modalInstanceRef.value = null + }) + uidRef.value = uuid + modalInstanceRef.value = modalInstance + currentInstance?.emit('register', modalInstance, uuid) + } + + watchEffect(() => { + const data = dataTransfer[unref(uidRef)] + if (!data) return + if (!callbackFn || !isFunction(callbackFn)) return + nextTick(() => { + callbackFn(data) + }) + }) + + return [ + register, + { + changeLoading: (loading = true) => { + getInstance()?.setModalProps({ loading }) + }, + getVisible: computed((): boolean => { + return visibleData[~~unref(uidRef)] + }), + + changeOkLoading: (loading = true) => { + getInstance()?.setModalProps({ confirmLoading: loading }) + }, + + closeModal: () => { + getInstance()?.setModalProps({ visible: false }) + }, + + setModalProps: (props: Partial) => { + getInstance()?.setModalProps(props) + }, + + redoModalHeight: () => { + const callRedo = getInstance()?.redoModalHeight + callRedo && callRedo() + } + } + ] +} diff --git a/src/components/Modal/src/hooks/useModalContext.ts b/src/components/Modal/src/hooks/useModalContext.ts new file mode 100644 index 00000000..e976e12b --- /dev/null +++ b/src/components/Modal/src/hooks/useModalContext.ts @@ -0,0 +1,16 @@ +import { InjectionKey } from 'vue' +import { createContext, useContext } from '@/hooks/core/useContext' + +export interface ModalContextProps { + redoModalHeight: () => void +} + +const key: InjectionKey = Symbol() + +export function createModalContext(context: ModalContextProps) { + return createContext(context, key) +} + +export function useModalContext() { + return useContext(key) +} diff --git a/src/components/Modal/src/hooks/useModalDrag.ts b/src/components/Modal/src/hooks/useModalDrag.ts new file mode 100644 index 00000000..143e85db --- /dev/null +++ b/src/components/Modal/src/hooks/useModalDrag.ts @@ -0,0 +1,107 @@ +import { Ref, unref, watchEffect } from 'vue' +import { useTimeoutFn } from '@/hooks/core/useTimeout' + +export interface UseModalDragMoveContext { + draggable: Ref + destroyOnClose: Ref | undefined + visible: Ref +} + +export function useModalDragMove(context: UseModalDragMoveContext) { + const getStyle = (dom: any, attr: any) => { + return getComputedStyle(dom)[attr] + } + const drag = (wrap: any) => { + if (!wrap) return + wrap.setAttribute('data-drag', unref(context.draggable)) + const dialogHeaderEl = wrap.querySelector('.ant-modal-header') + const dragDom = wrap.querySelector('.ant-modal') + + if (!dialogHeaderEl || !dragDom || !unref(context.draggable)) return + + dialogHeaderEl.style.cursor = 'move' + + dialogHeaderEl.onmousedown = (e: any) => { + if (!e) return + // 鼠标按下,计算当前元素距离可视区的距离 + const disX = e.clientX + const disY = e.clientY + const screenWidth = document.body.clientWidth // body当前宽度 + const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取) + + const dragDomWidth = dragDom.offsetWidth // 对话框宽度 + const dragDomheight = dragDom.offsetHeight // 对话框高度 + + const minDragDomLeft = dragDom.offsetLeft + + const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth + const minDragDomTop = dragDom.offsetTop + const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight + // 获取到的值带px 正则匹配替换 + const domLeft = getStyle(dragDom, 'left') + const domTop = getStyle(dragDom, 'top') + let styL = +domLeft + let styT = +domTop + + // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px + if (domLeft.includes('%')) { + styL = +document.body.clientWidth * (+domLeft.replace(/%/g, '') / 100) + styT = +document.body.clientHeight * (+domTop.replace(/%/g, '') / 100) + } else { + styL = +domLeft.replace(/px/g, '') + styT = +domTop.replace(/px/g, '') + } + + document.onmousemove = function (e) { + // 通过事件委托,计算移动的距离 + let left = e.clientX - disX + let top = e.clientY - disY + + // 边界处理 + if (-left > minDragDomLeft) { + left = -minDragDomLeft + } else if (left > maxDragDomLeft) { + left = maxDragDomLeft + } + + if (-top > minDragDomTop) { + top = -minDragDomTop + } else if (top > maxDragDomTop) { + top = maxDragDomTop + } + + // 移动当前元素 + dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;` + } + + document.onmouseup = () => { + document.onmousemove = null + document.onmouseup = null + } + } + } + + const handleDrag = () => { + const dragWraps = document.querySelectorAll('.ant-modal-wrap') + for (const wrap of Array.from(dragWraps)) { + if (!wrap) continue + const display = getStyle(wrap, 'display') + const draggable = wrap.getAttribute('data-drag') + if (display !== 'none') { + // 拖拽位置 + if (draggable === null || unref(context.destroyOnClose)) { + drag(wrap) + } + } + } + } + + watchEffect(() => { + if (!unref(context.visible) || !unref(context.draggable)) { + return + } + useTimeoutFn(() => { + handleDrag() + }, 30) + }) +} diff --git a/src/components/Modal/src/hooks/useModalFullScreen.ts b/src/components/Modal/src/hooks/useModalFullScreen.ts new file mode 100644 index 00000000..1e2c49ce --- /dev/null +++ b/src/components/Modal/src/hooks/useModalFullScreen.ts @@ -0,0 +1,43 @@ +import { computed, Ref, ref, unref } from 'vue' + +export interface UseFullScreenContext { + wrapClassName: Ref + modalWrapperRef: Ref + extHeightRef: Ref +} + +export function useFullScreen(context: UseFullScreenContext) { + // const formerHeightRef = ref(0); + const fullScreenRef = ref(false) + + const getWrapClassName = computed(() => { + const clsName = unref(context.wrapClassName) || '' + return unref(fullScreenRef) ? `fullscreen-modal ${clsName} ` : unref(clsName) + }) + + function handleFullScreen(e: Event) { + e && e.stopPropagation() + fullScreenRef.value = !unref(fullScreenRef) + + // const modalWrapper = unref(context.modalWrapperRef); + + // if (!modalWrapper) return; + + // const wrapperEl = modalWrapper.$el as HTMLElement; + // if (!wrapperEl) return; + // const modalWrapSpinEl = wrapperEl.querySelector('.ant-spin-nested-loading') as HTMLElement; + + // if (!modalWrapSpinEl) return; + + // if (!unref(formerHeightRef) && unref(fullScreenRef)) { + // formerHeightRef.value = modalWrapSpinEl.offsetHeight; + // } + + // if (unref(fullScreenRef)) { + // modalWrapSpinEl.style.height = `${window.innerHeight - unref(context.extHeightRef)}px`; + // } else { + // modalWrapSpinEl.style.height = `${unref(formerHeightRef)}px`; + // } + } + return { getWrapClassName, handleFullScreen, fullScreenRef } +} diff --git a/src/components/Modal/src/index.less b/src/components/Modal/src/index.less new file mode 100644 index 00000000..88df4101 --- /dev/null +++ b/src/components/Modal/src/index.less @@ -0,0 +1,130 @@ +.fullscreen-modal { + overflow: hidden; + + .ant-modal { + top: 0 !important; + right: 0 !important; + bottom: 0 !important; + left: 0 !important; + width: 100% !important; + height: 100%; + + &-content { + height: 100%; + } + } +} + +.ant-modal { + width: 520px; + padding-bottom: 0; + + .ant-modal-body > .scrollbar { + padding: 14px; + } + + &-title { + font-size: 16px; + font-weight: bold; + + .base-title { + cursor: move !important; + } + } + + .ant-modal-body { + padding: 0; + + > .scrollbar > .scrollbar__bar.is-horizontal { + display: none; + } + } + + &-large { + top: 60px; + + &--mini { + top: 16px; + } + } + + &-header { + padding: 16px; + } + + &-content { + box-shadow: 0 4px 8px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%); + } + + &-footer { + button + button { + margin-left: 10px; + } + } + + &-close { + font-weight: normal; + outline: none; + } + + &-close-x { + display: inline-block; + width: 96px; + height: 56px; + line-height: 56px; + } + + &-confirm-body { + .ant-modal-confirm-content { + // color: #fff; + + > * { + color: @text-color-help-dark; + } + } + } + + &-confirm-confirm.error .ant-modal-confirm-body > .anticon { + color: @error-color; + } + + &-confirm-btns { + .ant-btn:last-child { + margin-right: 0; + } + } + + &-confirm-info { + .ant-modal-confirm-body > .anticon { + color: @warning-color; + } + } + + &-confirm-confirm.success { + .ant-modal-confirm-body > .anticon { + color: @success-color; + } + } +} + +.ant-modal-confirm .ant-modal-body { + padding: 24px !important; +} + +@media screen and (max-height: 600px) { + .ant-modal { + top: 60px; + } +} + +@media screen and (max-height: 540px) { + .ant-modal { + top: 30px; + } +} + +@media screen and (max-height: 480px) { + .ant-modal { + top: 10px; + } +} diff --git a/src/components/Modal/src/props.ts b/src/components/Modal/src/props.ts new file mode 100644 index 00000000..fbfec2b2 --- /dev/null +++ b/src/components/Modal/src/props.ts @@ -0,0 +1,83 @@ +import type { CSSProperties } from 'vue' +import type { ModalWrapperProps } from './typing' +import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes' +import { useI18n } from '@/hooks/web/useI18n' + +const { t } = useI18n() + +export const modalProps = { + visible: { type: Boolean }, + scrollTop: { type: Boolean, default: true }, + height: { type: Number }, + minHeight: { type: Number }, + // open drag + draggable: { type: Boolean, default: true }, + centered: { type: Boolean }, + cancelText: { type: String, default: t('common.cancelText') }, + okText: { type: String, default: t('common.okText') }, + + closeFunc: Function as PropType<() => Promise> +} + +export const basicProps = Object.assign({}, modalProps, { + defaultFullscreen: { type: Boolean }, + // Can it be full screen + canFullscreen: { type: Boolean, default: true }, + // After enabling the wrapper, the bottom can be increased in height + wrapperFooterOffset: { type: Number, default: 0 }, + // Warm reminder message + helpMessage: [String, Array] as PropType, + // Whether to setting wrapper + useWrapper: { type: Boolean, default: true }, + loading: { type: Boolean }, + loadingTip: { type: String }, + /** + * @description: Show close button + */ + showCancelBtn: { type: Boolean, default: true }, + /** + * @description: Show confirmation button + */ + showOkBtn: { type: Boolean, default: true }, + + wrapperProps: Object as PropType>, + + afterClose: Function as PropType<() => Promise>, + + bodyStyle: Object as PropType, + + closable: { type: Boolean, default: true }, + + closeIcon: Object as PropType, + + confirmLoading: { type: Boolean }, + + destroyOnClose: { type: Boolean }, + + footer: Object as PropType, + + getContainer: Function as PropType<() => any>, + + mask: { type: Boolean, default: true }, + + maskClosable: { type: Boolean, default: true }, + keyboard: { type: Boolean, default: true }, + + maskStyle: Object as PropType, + + okType: { type: String, default: 'primary' }, + + okButtonProps: Object as PropType, + + cancelButtonProps: Object as PropType, + + title: { type: String }, + + visible: { type: Boolean }, + + width: [String, Number] as PropType, + + wrapClassName: { type: String }, + + zIndex: { type: Number } +}) diff --git a/src/components/Modal/src/typing.ts b/src/components/Modal/src/typing.ts new file mode 100644 index 00000000..03a9a4dc --- /dev/null +++ b/src/components/Modal/src/typing.ts @@ -0,0 +1,209 @@ +import type { ButtonProps } from 'ant-design-vue/lib/button/buttonTypes' +import type { CSSProperties, VNodeChild, ComputedRef } from 'vue' +/** + * @description: 弹窗对外暴露的方法 + */ +export interface ModalMethods { + setModalProps: (props: Partial) => void + emitVisible?: (visible: boolean, uid: number) => void + redoModalHeight?: () => void +} + +export type RegisterFn = (modalMethods: ModalMethods, uuid?: string) => void + +export interface ReturnMethods extends ModalMethods { + openModal: (props?: boolean, data?: T, openOnSet?: boolean) => void + closeModal: () => void + getVisible?: ComputedRef +} + +export type UseModalReturnType = [RegisterFn, ReturnMethods] + +export interface ReturnInnerMethods extends ModalMethods { + closeModal: () => void + changeLoading: (loading: boolean) => void + changeOkLoading: (loading: boolean) => void + getVisible?: ComputedRef + redoModalHeight: () => void +} + +export type UseModalInnerReturnType = [RegisterFn, ReturnInnerMethods] + +export interface ModalProps { + minHeight?: number + height?: number + // 启用wrapper后 底部可以适当增加高度 + wrapperFooterOffset?: number + draggable?: boolean + scrollTop?: boolean + + // 是否可以进行全屏 + canFullscreen?: boolean + defaultFullscreen?: boolean + visible?: boolean + // 温馨提醒信息 + helpMessage: string | string[] + + // 是否使用modalWrapper + useWrapper: boolean + + loading: boolean + loadingTip?: string + + wrapperProps: Omit + + showOkBtn: boolean + showCancelBtn: boolean + closeFunc: () => Promise + + /** + * Specify a function that will be called when modal is closed completely. + * @type Function + */ + afterClose?: () => any + + /** + * Body style for modal body element. Such as height, padding etc. + * @default {} + * @type object + */ + bodyStyle?: CSSProperties + + /** + * Text of the Cancel button + * @default 'cancel' + * @type string + */ + cancelText?: string + + /** + * Centered Modal + * @default false + * @type boolean + */ + centered?: boolean + + /** + * Whether a close (x) button is visible on top right of the modal dialog or not + * @default true + * @type boolean + */ + closable?: boolean + /** + * Whether a close (x) button is visible on top right of the modal dialog or not + */ + closeIcon?: VNodeChild | JSX.Element + + /** + * Whether to apply loading visual effect for OK button or not + * @default false + * @type boolean + */ + confirmLoading?: boolean + + /** + * Whether to unmount child components on onClose + * @default false + * @type boolean + */ + destroyOnClose?: boolean + + /** + * Footer content, set as :footer="null" when you don't need default buttons + * @default OK and Cancel buttons + * @type any (string | slot) + */ + footer?: VNodeChild | JSX.Element + + /** + * Return the mount node for Modal + * @default () => document.body + * @type Function + */ + getContainer?: (instance: any) => HTMLElement + + /** + * Whether show mask or not. + * @default true + * @type boolean + */ + mask?: boolean + + /** + * Whether to close the modal dialog when the mask (area outside the modal) is clicked + * @default true + * @type boolean + */ + maskClosable?: boolean + + /** + * Style for modal's mask element. + * @default {} + * @type object + */ + maskStyle?: CSSProperties + + /** + * Text of the OK button + * @default 'OK' + * @type string + */ + okText?: string + + /** + * Button type of the OK button + * @default 'primary' + * @type string + */ + okType?: 'primary' | 'danger' | 'dashed' | 'ghost' | 'default' + + /** + * The ok button props, follow jsx rules + * @type object + */ + okButtonProps?: ButtonProps + + /** + * The cancel button props, follow jsx rules + * @type object + */ + cancelButtonProps?: ButtonProps + + /** + * The modal dialog's title + * @type any (string | slot) + */ + title?: VNodeChild | JSX.Element + + /** + * Width of the modal dialog + * @default 520 + * @type string | number + */ + width?: string | number + + /** + * The class name of the container of the modal dialog + * @type string + */ + wrapClassName?: string + + /** + * The z-index of the Modal + * @default 1000 + * @type number + */ + zIndex?: number +} + +export interface ModalWrapperProps { + footerOffset?: number + loading: boolean + modalHeaderHeight: number + modalFooterHeight: number + minHeight: number + height: number + visible: boolean + fullScreen: boolean + useWrapper: boolean +} diff --git a/src/components/Page/index.ts b/src/components/Page/index.ts new file mode 100644 index 00000000..8ed45055 --- /dev/null +++ b/src/components/Page/index.ts @@ -0,0 +1,9 @@ +import { withInstall } from '@/utils' + +import pageFooter from './src/PageFooter.vue' +import pageWrapper from './src/PageWrapper.vue' + +export const PageFooter = withInstall(pageFooter) +export const PageWrapper = withInstall(pageWrapper) + +export const PageWrapperFixedHeightKey = 'PageWrapperFixedHeight' diff --git a/src/components/Page/src/PageFooter.vue b/src/components/Page/src/PageFooter.vue new file mode 100644 index 00000000..c6905fac --- /dev/null +++ b/src/components/Page/src/PageFooter.vue @@ -0,0 +1,42 @@ + + + + diff --git a/src/components/Page/src/PageWrapper.vue b/src/components/Page/src/PageWrapper.vue new file mode 100644 index 00000000..68da740c --- /dev/null +++ b/src/components/Page/src/PageWrapper.vue @@ -0,0 +1,162 @@ + + + diff --git a/src/components/Preview/index.ts b/src/components/Preview/index.ts new file mode 100644 index 00000000..f4ef6257 --- /dev/null +++ b/src/components/Preview/index.ts @@ -0,0 +1,2 @@ +export { default as ImagePreview } from './src/Preview.vue' +export { createImgPreview } from './src/functional' diff --git a/src/components/Preview/src/Functional.vue b/src/components/Preview/src/Functional.vue new file mode 100644 index 00000000..204be6bd --- /dev/null +++ b/src/components/Preview/src/Functional.vue @@ -0,0 +1,536 @@ + + diff --git a/src/components/Preview/src/Preview.vue b/src/components/Preview/src/Preview.vue new file mode 100644 index 00000000..b10d3552 --- /dev/null +++ b/src/components/Preview/src/Preview.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/components/Preview/src/functional.ts b/src/components/Preview/src/functional.ts new file mode 100644 index 00000000..09886713 --- /dev/null +++ b/src/components/Preview/src/functional.ts @@ -0,0 +1,17 @@ +import type { Options, Props } from './typing' +import ImgPreview from './Functional.vue' +import { isClient } from '@/utils/is' +import { createVNode, render } from 'vue' + +let instance: ReturnType | null = null +export function createImgPreview(options: Options) { + if (!isClient) return + const propsData: Partial = {} + const container = document.createElement('div') + Object.assign(propsData, { show: true, index: 0, scaleStep: 100 }, options) + + instance = createVNode(ImgPreview, propsData) + render(instance, container) + document.body.appendChild(container) + return instance.component?.exposed +} diff --git a/src/components/Preview/src/typing.ts b/src/components/Preview/src/typing.ts new file mode 100644 index 00000000..7e439630 --- /dev/null +++ b/src/components/Preview/src/typing.ts @@ -0,0 +1,55 @@ +interface onImgLoadType { + index: number + url: string + dom: HTMLImageElement +} + +export interface Options { + show?: boolean + imageList: string[] + index?: number + scaleStep?: number + defaultWidth?: number + maskClosable?: boolean + rememberState?: boolean + onImgLoad?: (img: onImgLoadType) => void + onImgError?: (img: onImgLoadType) => void +} + +export interface Props { + show: boolean + instance: Props + imageList: string[] + index: number + scaleStep: number + defaultWidth: number + maskClosable: boolean + rememberState: boolean +} + +export interface PreviewActions { + resume: () => void + close: () => void + prev: () => void + next: () => void + setScale: (scale: number) => void + setRotate: (rotate: number) => void +} + +export interface ImageProps { + alt?: string + fallback?: string + src: string + width: string | number + height?: string | number + placeholder?: string | boolean + preview?: + | boolean + | { + visible?: boolean + onVisibleChange?: (visible: boolean, prevVisible: boolean) => void + getContainer: string | HTMLElement | (() => HTMLElement) + } +} + +export type ImageItem = string | ImageProps diff --git a/src/components/Qrcode/index.ts b/src/components/Qrcode/index.ts new file mode 100644 index 00000000..46f20196 --- /dev/null +++ b/src/components/Qrcode/index.ts @@ -0,0 +1,5 @@ +import { withInstall } from '@/utils' +import qrCode from './src/Qrcode.vue' + +export const QrCode = withInstall(qrCode) +export * from './src/typing' diff --git a/src/components/Qrcode/src/Qrcode.vue b/src/components/Qrcode/src/Qrcode.vue new file mode 100644 index 00000000..722db3f4 --- /dev/null +++ b/src/components/Qrcode/src/Qrcode.vue @@ -0,0 +1,111 @@ + + diff --git a/src/components/Qrcode/src/drawCanvas.ts b/src/components/Qrcode/src/drawCanvas.ts new file mode 100644 index 00000000..95631ab8 --- /dev/null +++ b/src/components/Qrcode/src/drawCanvas.ts @@ -0,0 +1,32 @@ +import { toCanvas } from 'qrcode' +import type { QRCodeRenderersOptions } from 'qrcode' +import { RenderQrCodeParams, ContentType } from './typing' +import { cloneDeep } from 'lodash-es' + +export const renderQrCode = ({ canvas, content, width = 0, options: params = {} }: RenderQrCodeParams) => { + const options = cloneDeep(params) + // 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率 + options.errorCorrectionLevel = options.errorCorrectionLevel || getErrorCorrectionLevel(content) + + return getOriginWidth(content, options).then((_width: number) => { + options.scale = width === 0 ? undefined : (width / _width) * 4 + return toCanvas(canvas, content, options) + }) +} + +// 得到原QrCode的大小,以便缩放得到正确的QrCode大小 +function getOriginWidth(content: ContentType, options: QRCodeRenderersOptions) { + const _canvas = document.createElement('canvas') + return toCanvas(_canvas, content, options).then(() => _canvas.width) +} + +// 对于内容少的QrCode,增大容错率 +function getErrorCorrectionLevel(content: ContentType) { + if (content.length > 36) { + return 'M' + } else if (content.length > 16) { + return 'Q' + } else { + return 'H' + } +} diff --git a/src/components/Qrcode/src/drawLogo.ts b/src/components/Qrcode/src/drawLogo.ts new file mode 100644 index 00000000..a7b8e8e4 --- /dev/null +++ b/src/components/Qrcode/src/drawLogo.ts @@ -0,0 +1,81 @@ +import { isString } from '@/utils/is' +import { RenderQrCodeParams, LogoType } from './typing' +export const drawLogo = ({ canvas, logo }: RenderQrCodeParams) => { + if (!logo) { + return new Promise((resolve) => { + resolve((canvas as HTMLCanvasElement).toDataURL()) + }) + } + const canvasWidth = (canvas as HTMLCanvasElement).width + const { logoSize = 0.15, bgColor = '#ffffff', borderSize = 0.05, crossOrigin, borderRadius = 8, logoRadius = 0 } = logo as LogoType + + const logoSrc: string = isString(logo) ? logo : logo.src + const logoWidth = canvasWidth * logoSize + const logoXY = (canvasWidth * (1 - logoSize)) / 2 + const logoBgWidth = canvasWidth * (logoSize + borderSize) + const logoBgXY = (canvasWidth * (1 - logoSize - borderSize)) / 2 + + const ctx = canvas.getContext('2d') + if (!ctx) return + + // logo 底色 + canvasRoundRect(ctx)(logoBgXY, logoBgXY, logoBgWidth, logoBgWidth, borderRadius) + ctx.fillStyle = bgColor + ctx.fill() + + // logo + const image = new Image() + if (crossOrigin || logoRadius) { + image.setAttribute('crossOrigin', crossOrigin || 'anonymous') + } + image.src = logoSrc + + // 使用image绘制可以避免某些跨域情况 + const drawLogoWithImage = (image: CanvasImageSource) => { + ctx.drawImage(image, logoXY, logoXY, logoWidth, logoWidth) + } + + // 使用canvas绘制以获得更多的功能 + const drawLogoWithCanvas = (image: HTMLImageElement) => { + const canvasImage = document.createElement('canvas') + canvasImage.width = logoXY + logoWidth + canvasImage.height = logoXY + logoWidth + const imageCanvas = canvasImage.getContext('2d') + if (!imageCanvas || !ctx) return + imageCanvas.drawImage(image, logoXY, logoXY, logoWidth, logoWidth) + + canvasRoundRect(ctx)(logoXY, logoXY, logoWidth, logoWidth, logoRadius) + if (!ctx) return + const fillStyle = ctx.createPattern(canvasImage, 'no-repeat') + if (fillStyle) { + ctx.fillStyle = fillStyle + ctx.fill() + } + } + + // 将 logo绘制到 canvas上 + return new Promise((resolve) => { + image.onload = () => { + logoRadius ? drawLogoWithCanvas(image) : drawLogoWithImage(image) + resolve((canvas as HTMLCanvasElement).toDataURL()) + } + }) +} + +// copy来的方法,用于绘制圆角 +function canvasRoundRect(ctx: CanvasRenderingContext2D) { + return (x: number, y: number, w: number, h: number, r: number) => { + const minSize = Math.min(w, h) + if (r > minSize / 2) { + r = minSize / 2 + } + ctx.beginPath() + ctx.moveTo(x + r, y) + ctx.arcTo(x + w, y, x + w, y + h, r) + ctx.arcTo(x + w, y + h, x, y + h, r) + ctx.arcTo(x, y + h, x, y, r) + ctx.arcTo(x, y, x + w, y, r) + ctx.closePath() + return ctx + } +} diff --git a/src/components/Qrcode/src/qrcodePlus.ts b/src/components/Qrcode/src/qrcodePlus.ts new file mode 100644 index 00000000..a661734e --- /dev/null +++ b/src/components/Qrcode/src/qrcodePlus.ts @@ -0,0 +1,4 @@ +// 参考 qr-code-with-logo 进行ts版本修改 +import { toCanvas } from './toCanvas' +export * from './typing' +export { toCanvas } diff --git a/src/components/Qrcode/src/toCanvas.ts b/src/components/Qrcode/src/toCanvas.ts new file mode 100644 index 00000000..afeafb96 --- /dev/null +++ b/src/components/Qrcode/src/toCanvas.ts @@ -0,0 +1,10 @@ +import { renderQrCode } from './drawCanvas' +import { drawLogo } from './drawLogo' +import { RenderQrCodeParams } from './typing' +export const toCanvas = (options: RenderQrCodeParams) => { + return renderQrCode(options) + .then(() => { + return options + }) + .then(drawLogo) as Promise +} diff --git a/src/components/Qrcode/src/typing.ts b/src/components/Qrcode/src/typing.ts new file mode 100644 index 00000000..ec9a9444 --- /dev/null +++ b/src/components/Qrcode/src/typing.ts @@ -0,0 +1,38 @@ +import type { QRCodeSegment, QRCodeRenderersOptions } from 'qrcode' + +export type ContentType = string | QRCodeSegment[] + +export type { QRCodeRenderersOptions } + +export type LogoType = { + src: string + logoSize: number + borderColor: string + bgColor: string + borderSize: number + crossOrigin: string + borderRadius: number + logoRadius: number +} + +export interface RenderQrCodeParams { + canvas: any + content: ContentType + width?: number + options?: QRCodeRenderersOptions + logo?: LogoType | string + image?: HTMLImageElement + downloadName?: string + download?: boolean | Fn +} + +export type ToCanvasFn = (options: RenderQrCodeParams) => Promise + +export interface QrCodeActionType { + download: (fileName?: string) => void +} + +export interface QrcodeDoneEventParams { + url: string + ctx?: CanvasRenderingContext2D | null +} diff --git a/src/components/Scrollbar/index.ts b/src/components/Scrollbar/index.ts new file mode 100644 index 00000000..1c567ff4 --- /dev/null +++ b/src/components/Scrollbar/index.ts @@ -0,0 +1,8 @@ +/** + * copy from element-ui + */ + +import Scrollbar from './src/Scrollbar.vue' + +export { Scrollbar } +export type { ScrollbarType } from './src/types' diff --git a/src/components/Scrollbar/src/Scrollbar.vue b/src/components/Scrollbar/src/Scrollbar.vue new file mode 100644 index 00000000..a83b02b4 --- /dev/null +++ b/src/components/Scrollbar/src/Scrollbar.vue @@ -0,0 +1,178 @@ + + + diff --git a/src/components/Scrollbar/src/bar.ts b/src/components/Scrollbar/src/bar.ts new file mode 100644 index 00000000..a1d9a391 --- /dev/null +++ b/src/components/Scrollbar/src/bar.ts @@ -0,0 +1,92 @@ +import { defineComponent, h, computed, ref, getCurrentInstance, onUnmounted, inject, Ref } from 'vue' +import { on, off } from '@/utils/domUtils' + +import { renderThumbStyle, BAR_MAP } from './util' + +export default defineComponent({ + name: 'Bar', + + props: { + vertical: Boolean, + size: String, + move: Number + }, + + setup(props) { + const instance = getCurrentInstance() + const thumb = ref() + const wrap = inject('scroll-bar-wrap', {} as Ref>) as any + const bar = computed(() => { + return BAR_MAP[props.vertical ? 'vertical' : 'horizontal'] + }) + const barStore = ref({}) + const cursorDown = ref() + const clickThumbHandler = (e: any) => { + // prevent click event of right button + if (e.ctrlKey || e.button === 2) { + return + } + window.getSelection()?.removeAllRanges() + startDrag(e) + barStore.value[bar.value.axis] = + e.currentTarget[bar.value.offset] - (e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction]) + } + + const clickTrackHandler = (e: any) => { + const offset = Math.abs(e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) + const thumbHalf = thumb.value[bar.value.offset] / 2 + const thumbPositionPercentage = ((offset - thumbHalf) * 100) / instance?.vnode.el?.[bar.value.offset] + + wrap.value[bar.value.scroll] = (thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100 + } + const startDrag = (e: any) => { + e.stopImmediatePropagation() + cursorDown.value = true + on(document, 'mousemove', mouseMoveDocumentHandler) + on(document, 'mouseup', mouseUpDocumentHandler) + document.onselectstart = () => false + } + + const mouseMoveDocumentHandler = (e: any) => { + if (cursorDown.value === false) return + const prevPage = barStore.value[bar.value.axis] + + if (!prevPage) return + + const offset = (instance?.vnode.el?.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) * -1 + const thumbClickPosition = thumb.value[bar.value.offset] - prevPage + const thumbPositionPercentage = ((offset - thumbClickPosition) * 100) / instance?.vnode.el?.[bar.value.offset] + wrap.value[bar.value.scroll] = (thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100 + } + + function mouseUpDocumentHandler() { + cursorDown.value = false + barStore.value[bar.value.axis] = 0 + off(document, 'mousemove', mouseMoveDocumentHandler) + document.onselectstart = null + } + + onUnmounted(() => { + off(document, 'mouseup', mouseUpDocumentHandler) + }) + + return () => + h( + 'div', + { + class: ['scrollbar__bar', 'is-' + bar.value.key], + onMousedown: clickTrackHandler + }, + h('div', { + ref: thumb, + class: 'scrollbar__thumb', + onMousedown: clickThumbHandler, + style: renderThumbStyle({ + size: props.size, + move: props.move, + bar: bar.value + }) + }) + ) + } +}) diff --git a/src/components/Scrollbar/src/types.d.ts b/src/components/Scrollbar/src/types.d.ts new file mode 100644 index 00000000..7d67ccf2 --- /dev/null +++ b/src/components/Scrollbar/src/types.d.ts @@ -0,0 +1,18 @@ +export interface BarMapItem { + offset: string + scroll: string + scrollSize: string + size: string + key: string + axis: string + client: string + direction: string +} +export interface BarMap { + vertical: BarMapItem + horizontal: BarMapItem +} + +export interface ScrollbarType { + wrap: ElRef +} diff --git a/src/components/Scrollbar/src/util.ts b/src/components/Scrollbar/src/util.ts new file mode 100644 index 00000000..ba6f864e --- /dev/null +++ b/src/components/Scrollbar/src/util.ts @@ -0,0 +1,49 @@ +import type { BarMap } from './types' +export const BAR_MAP: BarMap = { + vertical: { + offset: 'offsetHeight', + scroll: 'scrollTop', + scrollSize: 'scrollHeight', + size: 'height', + key: 'vertical', + axis: 'Y', + client: 'clientY', + direction: 'top' + }, + horizontal: { + offset: 'offsetWidth', + scroll: 'scrollLeft', + scrollSize: 'scrollWidth', + size: 'width', + key: 'horizontal', + axis: 'X', + client: 'clientX', + direction: 'left' + } +} + +export function renderThumbStyle({ move, size, bar }) { + const style = {} as any + const translate = `translate${bar.axis}(${move}%)` + + style[bar.size] = size + style.transform = translate + style.msTransform = translate + style.webkitTransform = translate + + return style +} + +function extend(to: T, _from: K): T & K { + return Object.assign(to as any, _from) +} + +export function toObject(arr: Array): Recordable { + const res = {} + for (let i = 0; i < arr.length; i++) { + if (arr[i]) { + extend(res, arr[i]) + } + } + return res +} diff --git a/src/components/SimpleMenu/index.ts b/src/components/SimpleMenu/index.ts new file mode 100644 index 00000000..7c0301bd --- /dev/null +++ b/src/components/SimpleMenu/index.ts @@ -0,0 +1,2 @@ +export { default as SimpleMenu } from './src/SimpleMenu.vue' +export { default as SimpleMenuTag } from './src/SimpleMenuTag.vue' diff --git a/src/components/SimpleMenu/src/SimpleMenu.vue b/src/components/SimpleMenu/src/SimpleMenu.vue new file mode 100644 index 00000000..560ff2c4 --- /dev/null +++ b/src/components/SimpleMenu/src/SimpleMenu.vue @@ -0,0 +1,133 @@ + + + diff --git a/src/components/SimpleMenu/src/SimpleMenuTag.vue b/src/components/SimpleMenu/src/SimpleMenuTag.vue new file mode 100644 index 00000000..03d6ae41 --- /dev/null +++ b/src/components/SimpleMenu/src/SimpleMenuTag.vue @@ -0,0 +1,57 @@ + + diff --git a/src/components/SimpleMenu/src/SimpleSubMenu.vue b/src/components/SimpleMenu/src/SimpleSubMenu.vue new file mode 100644 index 00000000..10bb5d9b --- /dev/null +++ b/src/components/SimpleMenu/src/SimpleSubMenu.vue @@ -0,0 +1,87 @@ + + diff --git a/src/components/SimpleMenu/src/components/Menu.vue b/src/components/SimpleMenu/src/components/Menu.vue new file mode 100644 index 00000000..2220cc71 --- /dev/null +++ b/src/components/SimpleMenu/src/components/Menu.vue @@ -0,0 +1,141 @@ + + + + diff --git a/src/components/SimpleMenu/src/components/MenuCollapseTransition.vue b/src/components/SimpleMenu/src/components/MenuCollapseTransition.vue new file mode 100644 index 00000000..be4e761d --- /dev/null +++ b/src/components/SimpleMenu/src/components/MenuCollapseTransition.vue @@ -0,0 +1,69 @@ + + diff --git a/src/components/SimpleMenu/src/components/MenuItem.vue b/src/components/SimpleMenu/src/components/MenuItem.vue new file mode 100644 index 00000000..28ac293e --- /dev/null +++ b/src/components/SimpleMenu/src/components/MenuItem.vue @@ -0,0 +1,100 @@ + + + diff --git a/src/components/SimpleMenu/src/components/SubMenuItem.vue b/src/components/SimpleMenu/src/components/SubMenuItem.vue new file mode 100644 index 00000000..019f32a8 --- /dev/null +++ b/src/components/SimpleMenu/src/components/SubMenuItem.vue @@ -0,0 +1,283 @@ + + diff --git a/src/components/SimpleMenu/src/components/menu.less b/src/components/SimpleMenu/src/components/menu.less new file mode 100644 index 00000000..0c4c5ca5 --- /dev/null +++ b/src/components/SimpleMenu/src/components/menu.less @@ -0,0 +1,309 @@ +@menu-prefix-cls: ~'@{namespace}-menu'; +@menu-popup-prefix-cls: ~'@{namespace}-menu-popup'; +@submenu-popup-prefix-cls: ~'@{namespace}-menu-submenu-popup'; + +@transition-time: 0.2s; +@menu-dark-subsidiary-color: rgba(255, 255, 255, 0.7); + +.light-border { + &::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + display: block; + width: 2px; + content: ''; + background-color: @primary-color; + } +} + +.@{menu-prefix-cls}-menu-popover { + .ant-popover-arrow { + display: none; + } + + .ant-popover-inner-content { + padding: 0; + } + + .@{menu-prefix-cls} { + &-opened > * > &-submenu-title-icon { + transform: translateY(-50%) rotate(90deg) !important; + } + + &-item, + &-submenu-title { + position: relative; + z-index: 1; + padding: 12px 20px; + color: @menu-dark-subsidiary-color; + cursor: pointer; + transition: all @transition-time @ease-in-out; + + &-icon { + position: absolute; + top: 50%; + right: 18px; + transition: transform @transition-time @ease-in-out; + transform: translateY(-50%) rotate(-90deg); + } + } + + &-dark { + .@{menu-prefix-cls}-item, + .@{menu-prefix-cls}-submenu-title { + color: @menu-dark-subsidiary-color; + // background: @menu-dark-active-bg; + + &:hover { + color: #fff; + } + + &-selected { + color: #fff; + background-color: @primary-color !important; + } + } + } + + &-light { + .@{menu-prefix-cls}-item, + .@{menu-prefix-cls}-submenu-title { + color: @text-color-base; + + &:hover { + color: @primary-color; + } + + &-selected { + z-index: 2; + color: @primary-color; + background-color: fade(@primary-color, 10); + + .light-border(); + } + } + } + } +} + +.content(); +.content() { + .@{menu-prefix-cls} { + position: relative; + display: block; + width: 100%; + padding: 0; + margin: 0; + font-size: @font-size-base; + color: @text-color-base; + list-style: none; + outline: none; + + // .collapse-transition { + // transition: @transition-time height ease-in-out, @transition-time padding-top ease-in-out, + // @transition-time padding-bottom ease-in-out; + // } + + &-light { + background-color: #fff; + + .@{menu-prefix-cls}-submenu-active { + color: @primary-color !important; + + &-border { + .light-border(); + } + } + } + + &-dark { + .@{menu-prefix-cls}-submenu-active { + color: #fff !important; + } + } + + &-item { + position: relative; + z-index: 1; + display: flex; + align-items: center; + font-size: @font-size-base; + color: inherit; + list-style: none; + cursor: pointer; + outline: none; + + &:hover, + &:active { + color: inherit; + } + } + + &-item > i { + margin-right: 6px; + } + + &-submenu-title > i, + &-submenu-title span > i { + margin-right: 8px; + } + + // vertical + &-vertical &-item, + &-vertical &-submenu-title { + position: relative; + z-index: 1; + padding: 14px 24px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + cursor: pointer; + + &:hover { + color: @primary-color; + } + + .@{menu-prefix-cls}-tooltip { + width: calc(100% - 0px); + padding: 12px 0; + text-align: center; + } + .@{menu-prefix-cls}-submenu-popup { + padding: 12px 0; + } + } + + &-vertical &-submenu-collapse { + .@{submenu-popup-prefix-cls} { + display: flex; + align-items: center; + justify-content: center; + } + .@{menu-prefix-cls}-submenu-collapsed-show-tit { + flex-direction: column; + } + } + + &-vertical&-collapse &-item, + &-vertical&-collapse &-submenu-title { + padding: 0; + } + + &-vertical &-submenu-title-icon { + position: absolute; + top: 50%; + right: 18px; + transform: translateY(-50%); + } + + &-submenu-title-icon { + transition: transform @transition-time @ease-in-out; + } + + &-vertical &-opened > * > &-submenu-title-icon { + transform: translateY(-50%) rotate(180deg); + } + + &-vertical &-submenu { + &-nested { + padding-left: 20px; + } + .@{menu-prefix-cls}-item { + padding-left: 43px; + } + } + + &-light&-vertical &-item { + &-active:not(.@{menu-prefix-cls}-submenu) { + z-index: 2; + color: @primary-color; + background-color: fade(@primary-color, 10); + + .light-border(); + } + &-active.@{menu-prefix-cls}-submenu { + color: @primary-color; + } + } + + &-light&-vertical&-collapse { + > li.@{menu-prefix-cls}-item-active, + .@{menu-prefix-cls}-submenu-active { + position: relative; + background-color: fade(@primary-color, 5); + + &::after { + display: none; + } + + &::before { + position: absolute; + top: 0; + left: 0; + width: 3px; + height: 100%; + content: ''; + background-color: @primary-color; + } + } + } + + &-dark&-vertical &-item, + &-dark&-vertical &-submenu-title { + color: @menu-dark-subsidiary-color; + &-active:not(.@{menu-prefix-cls}-submenu) { + color: #fff !important; + background-color: @primary-color !important; + } + + &:hover { + color: #fff; + } + } + + &-dark&-vertical&-collapse { + > li.@{menu-prefix-cls}-item-active, + .@{menu-prefix-cls}-submenu-active { + position: relative; + color: #fff !important; + background-color: @sider-dark-darken-bg-color !important; + + &::before { + position: absolute; + top: 0; + left: 0; + width: 3px; + height: 100%; + content: ''; + background-color: @primary-color; + } + + .@{menu-prefix-cls}-submenu-collapse { + background-color: transparent; + } + } + } + + &-dark&-vertical &-submenu &-item { + &-active, + &-active:hover { + color: #fff; + border-right: none; + } + } + + &-dark&-vertical &-child-item-active > &-submenu-title { + color: #fff; + } + + &-dark&-vertical &-opened { + .@{menu-prefix-cls}-submenu-has-parent-submenu { + .@{menu-prefix-cls}-submenu-title { + background-color: transparent; + } + } + } + } +} diff --git a/src/components/SimpleMenu/src/components/types.ts b/src/components/SimpleMenu/src/components/types.ts new file mode 100644 index 00000000..76de8771 --- /dev/null +++ b/src/components/SimpleMenu/src/components/types.ts @@ -0,0 +1,25 @@ +import { Ref } from 'vue' + +export interface Props { + theme: string + activeName?: string | number | undefined + openNames: string[] + accordion: boolean + width: string + collapsedWidth: string + indentSize: number + collapse: boolean + activeSubMenuNames: (string | number)[] +} + +export interface SubMenuProvider { + addSubMenu: (name: string | number, update?: boolean) => void + removeSubMenu: (name: string | number, update?: boolean) => void + removeAll: () => void + sliceIndex: (index: number) => void + isRemoveAllPopup: Ref + getOpenNames: () => (string | number)[] + handleMouseleave?: Fn + level: number + props: Props +} diff --git a/src/components/SimpleMenu/src/components/useMenu.ts b/src/components/SimpleMenu/src/components/useMenu.ts new file mode 100644 index 00000000..4ea23cb1 --- /dev/null +++ b/src/components/SimpleMenu/src/components/useMenu.ts @@ -0,0 +1,84 @@ +import { computed, ComponentInternalInstance, unref } from 'vue' +import type { CSSProperties } from 'vue' + +export function useMenuItem(instance: ComponentInternalInstance | null) { + const getParentMenu = computed(() => { + return findParentMenu(['Menu', 'SubMenu']) + }) + + const getParentRootMenu = computed(() => { + return findParentMenu(['Menu']) + }) + + const getParentSubMenu = computed(() => { + return findParentMenu(['SubMenu']) + }) + + const getItemStyle = computed((): CSSProperties => { + let parent = instance?.parent + if (!parent) return {} + const indentSize = (unref(getParentRootMenu)?.props.indentSize as number) ?? 20 + let padding = indentSize + + if (unref(getParentRootMenu)?.props.collapse) { + padding = indentSize + } else { + while (parent && parent.type.name !== 'Menu') { + if (parent.type.name === 'SubMenu') { + padding += indentSize + } + parent = parent.parent + } + } + return { paddingLeft: padding + 'px' } + }) + + function findParentMenu(name: string[]) { + let parent = instance?.parent + if (!parent) return null + while (parent && name.indexOf(parent.type.name!) === -1) { + parent = parent.parent + } + return parent + } + + function getParentList() { + let parent = instance + if (!parent) + return { + uidList: [], + list: [] + } + const ret: any[] = [] + while (parent && parent.type.name !== 'Menu') { + if (parent.type.name === 'SubMenu') { + ret.push(parent) + } + parent = parent.parent + } + return { + uidList: ret.map((item) => item.uid), + list: ret + } + } + + function getParentInstance(instance: ComponentInternalInstance, name = 'SubMenu') { + let parent = instance.parent + while (parent) { + if (parent.type.name !== name) { + return parent + } + parent = parent.parent + } + return parent + } + + return { + getParentMenu, + getParentInstance, + getParentRootMenu, + getParentList, + getParentSubMenu, + getItemStyle + } +} diff --git a/src/components/SimpleMenu/src/components/useSimpleMenuContext.ts b/src/components/SimpleMenu/src/components/useSimpleMenuContext.ts new file mode 100644 index 00000000..fa12aa78 --- /dev/null +++ b/src/components/SimpleMenu/src/components/useSimpleMenuContext.ts @@ -0,0 +1,18 @@ +import type { InjectionKey, Ref } from 'vue' +import type { Emitter } from '@/utils/mitt' +import { createContext, useContext } from '@/hooks/core/useContext' + +export interface SimpleRootMenuContextProps { + rootMenuEmitter: Emitter + activeName: Ref +} + +const key: InjectionKey = Symbol() + +export function createSimpleRootMenuContext(context: SimpleRootMenuContextProps) { + return createContext(context, key, { readonly: false, native: true }) +} + +export function useSimpleRootMenuContext() { + return useContext(key) +} diff --git a/src/components/SimpleMenu/src/index.less b/src/components/SimpleMenu/src/index.less new file mode 100644 index 00000000..4f9c9ce1 --- /dev/null +++ b/src/components/SimpleMenu/src/index.less @@ -0,0 +1,77 @@ +@simple-prefix-cls: ~'@{namespace}-simple-menu'; +@prefix-cls: ~'@{namespace}-menu'; + +.@{prefix-cls} { + &-dark&-vertical .@{simple-prefix-cls}__parent { + background-color: @sider-dark-bg-color; + > .@{prefix-cls}-submenu-title { + background-color: @sider-dark-bg-color; + } + } + + &-dark&-vertical .@{simple-prefix-cls}__children, + &-dark&-popup .@{simple-prefix-cls}__children { + background-color: @sider-dark-lighten-bg-color; + > .@{prefix-cls}-submenu-title { + background-color: @sider-dark-lighten-bg-color; + } + } + + .collapse-title { + overflow: hidden; + font-size: 12px; + text-overflow: ellipsis; + white-space: nowrap; + } +} + +.@{simple-prefix-cls} { + &-sub-title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + transition: all 0.3s; + } + + &-tag { + position: absolute; + top: calc(50% - 8px); + right: 30px; + display: inline-block; + padding: 2px 3px; + margin-right: 4px; + font-size: 10px; + line-height: 14px; + color: #fff; + border-radius: 2px; + + &--collapse { + top: 6px !important; + right: 2px; + } + + &--dot { + top: calc(50% - 2px); + width: 6px; + height: 6px; + padding: 0; + border-radius: 50%; + } + + &--primary { + background-color: @primary-color; + } + + &--error { + background-color: @error-color; + } + + &--success { + background-color: @success-color; + } + + &--warn { + background-color: @warning-color; + } + } +} diff --git a/src/components/SimpleMenu/src/types.ts b/src/components/SimpleMenu/src/types.ts new file mode 100644 index 00000000..1c7bbee4 --- /dev/null +++ b/src/components/SimpleMenu/src/types.ts @@ -0,0 +1,5 @@ +export interface MenuState { + activeName: string + openNames: string[] + activeSubMenuNames: string[] +} diff --git a/src/components/SimpleMenu/src/useOpenKeys.ts b/src/components/SimpleMenu/src/useOpenKeys.ts new file mode 100644 index 00000000..85d32ddb --- /dev/null +++ b/src/components/SimpleMenu/src/useOpenKeys.ts @@ -0,0 +1,50 @@ +import type { Menu as MenuType } from '@/router/types' +import type { MenuState } from './types' + +import { computed, Ref, toRaw } from 'vue' + +import { unref } from 'vue' +import { uniq } from 'lodash-es' +import { getAllParentPath } from '@/router/helper/menuHelper' + +import { useTimeoutFn } from '@/hooks/core/useTimeout' +import { useDebounceFn } from '@vueuse/core' + +export function useOpenKeys( + menuState: MenuState, + menus: Ref, + accordion: Ref, + mixSider: Ref, + collapse: Ref +) { + const debounceSetOpenKeys = useDebounceFn(setOpenKeys, 50) + async function setOpenKeys(path: string) { + const native = !mixSider.value + const menuList = toRaw(menus.value) + useTimeoutFn( + () => { + if (menuList?.length === 0) { + menuState.activeSubMenuNames = [] + menuState.openNames = [] + return + } + const keys = getAllParentPath(menuList, path) + + if (!unref(accordion)) { + menuState.openNames = uniq([...menuState.openNames, ...keys]) + } else { + menuState.openNames = keys + } + menuState.activeSubMenuNames = menuState.openNames + }, + 30, + native + ) + } + + const getOpenKeys = computed(() => { + return unref(collapse) ? [] : menuState.openNames + }) + + return { setOpenKeys: debounceSetOpenKeys, getOpenKeys } +} diff --git a/src/components/StrengthMeter/index.ts b/src/components/StrengthMeter/index.ts new file mode 100644 index 00000000..d615c713 --- /dev/null +++ b/src/components/StrengthMeter/index.ts @@ -0,0 +1,4 @@ +import { withInstall } from '@/utils' +import strengthMeter from './src/StrengthMeter.vue' + +export const StrengthMeter = withInstall(strengthMeter) diff --git a/src/components/StrengthMeter/src/StrengthMeter.vue b/src/components/StrengthMeter/src/StrengthMeter.vue new file mode 100644 index 00000000..56ad5651 --- /dev/null +++ b/src/components/StrengthMeter/src/StrengthMeter.vue @@ -0,0 +1,122 @@ + + + diff --git a/src/components/Table/index.ts b/src/components/Table/index.ts new file mode 100644 index 00000000..4d314ad8 --- /dev/null +++ b/src/components/Table/index.ts @@ -0,0 +1,11 @@ +export { default as BasicTable } from './src/BasicTable.vue' +export { default as TableAction } from './src/components/TableAction.vue' +export { default as EditTableHeaderIcon } from './src/components/EditTableHeaderIcon.vue' +export { default as TableImg } from './src/components/TableImg.vue' + +export * from './src/types/table' +export * from './src/types/pagination' +export * from './src/types/tableAction' +export { useTable } from './src/hooks/useTable' +export type { FormSchema, FormProps } from '@/components/Form/src/types/form' +export type { EditRecordRow } from './src/components/editable' diff --git a/src/components/Table/src/BasicTable.vue b/src/components/Table/src/BasicTable.vue new file mode 100644 index 00000000..4e92d90b --- /dev/null +++ b/src/components/Table/src/BasicTable.vue @@ -0,0 +1,404 @@ + + + diff --git a/src/components/Table/src/componentMap.ts b/src/components/Table/src/componentMap.ts new file mode 100644 index 00000000..a41ab656 --- /dev/null +++ b/src/components/Table/src/componentMap.ts @@ -0,0 +1,30 @@ +import type { Component } from 'vue' +import { Input, Select, Checkbox, InputNumber, Switch, DatePicker, TimePicker, AutoComplete, Radio } from 'ant-design-vue' +import type { ComponentType } from './types/componentType' +import { ApiSelect, ApiTreeSelect, RadioButtonGroup, ApiRadioGroup } from '@/components/Form' + +const componentMap = new Map() + +componentMap.set('Input', Input) +componentMap.set('InputNumber', InputNumber) +componentMap.set('Select', Select) +componentMap.set('ApiSelect', ApiSelect) +componentMap.set('AutoComplete', AutoComplete) +componentMap.set('ApiTreeSelect', ApiTreeSelect) +componentMap.set('Switch', Switch) +componentMap.set('Checkbox', Checkbox) +componentMap.set('DatePicker', DatePicker) +componentMap.set('TimePicker', TimePicker) +componentMap.set('RadioGroup', Radio.Group) +componentMap.set('RadioButtonGroup', RadioButtonGroup) +componentMap.set('ApiRadioGroup', ApiRadioGroup) + +export function add(compName: ComponentType, component: Component) { + componentMap.set(compName, component) +} + +export function del(compName: ComponentType) { + componentMap.delete(compName) +} + +export { componentMap } diff --git a/src/components/Table/src/components/EditTableHeaderIcon.vue b/src/components/Table/src/components/EditTableHeaderIcon.vue new file mode 100644 index 00000000..6f890bab --- /dev/null +++ b/src/components/Table/src/components/EditTableHeaderIcon.vue @@ -0,0 +1,12 @@ + + diff --git a/src/components/Table/src/components/HeaderCell.vue b/src/components/Table/src/components/HeaderCell.vue new file mode 100644 index 00000000..c845549a --- /dev/null +++ b/src/components/Table/src/components/HeaderCell.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/components/Table/src/components/TableAction.vue b/src/components/Table/src/components/TableAction.vue new file mode 100644 index 00000000..cfa5ba57 --- /dev/null +++ b/src/components/Table/src/components/TableAction.vue @@ -0,0 +1,185 @@ + + + diff --git a/src/components/Table/src/components/TableFooter.vue b/src/components/Table/src/components/TableFooter.vue new file mode 100644 index 00000000..5c4d69f2 --- /dev/null +++ b/src/components/Table/src/components/TableFooter.vue @@ -0,0 +1,88 @@ + + diff --git a/src/components/Table/src/components/TableHeader.vue b/src/components/Table/src/components/TableHeader.vue new file mode 100644 index 00000000..1bd43d23 --- /dev/null +++ b/src/components/Table/src/components/TableHeader.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/components/Table/src/components/TableImg.vue b/src/components/Table/src/components/TableImg.vue new file mode 100644 index 00000000..c5fda759 --- /dev/null +++ b/src/components/Table/src/components/TableImg.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/components/Table/src/components/TableTitle.vue b/src/components/Table/src/components/TableTitle.vue new file mode 100644 index 00000000..d3c87470 --- /dev/null +++ b/src/components/Table/src/components/TableTitle.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/components/Table/src/components/editable/CellComponent.ts b/src/components/Table/src/components/editable/CellComponent.ts new file mode 100644 index 00000000..930951d5 --- /dev/null +++ b/src/components/Table/src/components/editable/CellComponent.ts @@ -0,0 +1,38 @@ +import type { FunctionalComponent, defineComponent } from 'vue' +import type { ComponentType } from '../../types/componentType' +import { componentMap } from '@/components/Table/src/componentMap' + +import { Popover } from 'ant-design-vue' +import { h } from 'vue' + +export interface ComponentProps { + component: ComponentType + rule: boolean + popoverVisible: boolean + ruleMessage: string + getPopupContainer?: Fn +} + +export const CellComponent: FunctionalComponent = ( + { component = 'Input', rule = true, ruleMessage, popoverVisible, getPopupContainer }: ComponentProps, + { attrs } +) => { + const Comp = componentMap.get(component) as typeof defineComponent + + const DefaultComp = h(Comp, attrs) + if (!rule) { + return DefaultComp + } + return h( + Popover, + { + overlayClassName: 'edit-cell-rule-popover', + visible: !!popoverVisible, + ...(getPopupContainer ? { getPopupContainer } : {}) + }, + { + default: () => DefaultComp, + content: () => ruleMessage + } + ) +} diff --git a/src/components/Table/src/components/editable/EditableCell.vue b/src/components/Table/src/components/editable/EditableCell.vue new file mode 100644 index 00000000..1b369f47 --- /dev/null +++ b/src/components/Table/src/components/editable/EditableCell.vue @@ -0,0 +1,526 @@ + + diff --git a/src/components/Table/src/components/editable/helper.ts b/src/components/Table/src/components/editable/helper.ts new file mode 100644 index 00000000..ba109664 --- /dev/null +++ b/src/components/Table/src/components/editable/helper.ts @@ -0,0 +1,28 @@ +import { ComponentType } from '../../types/componentType' +import { useI18n } from '@/hooks/web/useI18n' + +const { t } = useI18n() + +/** + * @description: 生成placeholder + */ +export function createPlaceholderMessage(component: ComponentType) { + if (component.includes('Input') || component.includes('AutoComplete')) { + return t('common.inputText') + } + if (component.includes('Picker')) { + return t('common.chooseText') + } + + if ( + component.includes('Select') || + component.includes('Checkbox') || + component.includes('Radio') || + component.includes('Switch') || + component.includes('DatePicker') || + component.includes('TimePicker') + ) { + return t('common.chooseText') + } + return '' +} diff --git a/src/components/Table/src/components/editable/index.ts b/src/components/Table/src/components/editable/index.ts new file mode 100644 index 00000000..5f254521 --- /dev/null +++ b/src/components/Table/src/components/editable/index.ts @@ -0,0 +1,68 @@ +import type { BasicColumn } from '@/components/Table/src/types/table' + +import { h, Ref } from 'vue' + +import EditableCell from './EditableCell.vue' +import { isArray } from '@/utils/is' + +interface Params { + text: string + record: Recordable + index: number +} + +export function renderEditCell(column: BasicColumn) { + return ({ text: value, record, index }: Params) => { + record.onValid = async () => { + if (isArray(record?.validCbs)) { + const validFns = (record?.validCbs || []).map((fn) => fn()) + const res = await Promise.all(validFns) + return res.every((item) => !!item) + } else { + return false + } + } + + record.onEdit = async (edit: boolean, submit = false) => { + if (!submit) { + record.editable = edit + } + + if (!edit && submit) { + if (!(await record.onValid())) return false + const res = await record.onSubmitEdit?.() + if (res) { + record.editable = false + return true + } + return false + } + // cancel + if (!edit && !submit) { + record.onCancelEdit?.() + } + return true + } + + return h(EditableCell, { + value, + record, + column, + index + }) + } +} + +export type EditRecordRow = Partial< + { + onEdit: (editable: boolean, submit?: boolean) => Promise + onValid: () => Promise + editable: boolean + onCancel: Fn + onSubmit: Fn + submitCbs: Fn[] + cancelCbs: Fn[] + validCbs: Fn[] + editValueRefs: Recordable + } & T +> diff --git a/src/components/Table/src/components/settings/ColumnSetting.vue b/src/components/Table/src/components/settings/ColumnSetting.vue new file mode 100644 index 00000000..00493a37 --- /dev/null +++ b/src/components/Table/src/components/settings/ColumnSetting.vue @@ -0,0 +1,414 @@ + + + diff --git a/src/components/Table/src/components/settings/FullScreenSetting.vue b/src/components/Table/src/components/settings/FullScreenSetting.vue new file mode 100644 index 00000000..752cef5b --- /dev/null +++ b/src/components/Table/src/components/settings/FullScreenSetting.vue @@ -0,0 +1,20 @@ + + diff --git a/src/components/Table/src/components/settings/RedoSetting.vue b/src/components/Table/src/components/settings/RedoSetting.vue new file mode 100644 index 00000000..1d3501f9 --- /dev/null +++ b/src/components/Table/src/components/settings/RedoSetting.vue @@ -0,0 +1,21 @@ + + diff --git a/src/components/Table/src/components/settings/SizeSetting.vue b/src/components/Table/src/components/settings/SizeSetting.vue new file mode 100644 index 00000000..530975e5 --- /dev/null +++ b/src/components/Table/src/components/settings/SizeSetting.vue @@ -0,0 +1,46 @@ + + diff --git a/src/components/Table/src/components/settings/index.vue b/src/components/Table/src/components/settings/index.vue new file mode 100644 index 00000000..3e2e9d2c --- /dev/null +++ b/src/components/Table/src/components/settings/index.vue @@ -0,0 +1,56 @@ + + + diff --git a/src/components/Table/src/const.ts b/src/components/Table/src/const.ts new file mode 100644 index 00000000..dada4023 --- /dev/null +++ b/src/components/Table/src/const.ts @@ -0,0 +1,31 @@ +import componentSetting from '@/settings/componentSetting' + +const { table } = componentSetting + +const { pageSizeOptions, defaultPageSize, fetchSetting, defaultSize, defaultSortFn, defaultFilterFn } = table + +export const ROW_KEY = 'key' + +// Optional display number per page; +export const PAGE_SIZE_OPTIONS = pageSizeOptions + +// Number of items displayed per page +export const PAGE_SIZE = defaultPageSize + +// Common interface field settings +export const FETCH_SETTING = fetchSetting + +// Default Size +export const DEFAULT_SIZE = defaultSize + +// Configure general sort function +export const DEFAULT_SORT_FN = defaultSortFn + +export const DEFAULT_FILTER_FN = defaultFilterFn + +// Default layout of table cells +export const DEFAULT_ALIGN = 'center' + +export const INDEX_COLUMN_FLAG = 'INDEX' + +export const ACTION_COLUMN_FLAG = 'ACTION' diff --git a/src/components/Table/src/hooks/useColumns.ts b/src/components/Table/src/hooks/useColumns.ts new file mode 100644 index 00000000..fda18cd9 --- /dev/null +++ b/src/components/Table/src/hooks/useColumns.ts @@ -0,0 +1,313 @@ +import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table' +import type { PaginationProps } from '../types/pagination' +import type { ComputedRef } from 'vue' +import { computed, Ref, ref, reactive, toRaw, unref, watch } from 'vue' +import { renderEditCell } from '../components/editable' +import { usePermission } from '@/hooks/web/usePermission' +import { useI18n } from '@/hooks/web/useI18n' +import { isArray, isBoolean, isFunction, isMap, isString } from '@/utils/is' +import { cloneDeep, isEqual } from 'lodash-es' +import { formatToDate } from '@/utils/dateUtil' +import { ACTION_COLUMN_FLAG, DEFAULT_ALIGN, INDEX_COLUMN_FLAG, PAGE_SIZE } from '../const' + +function handleItem(item: BasicColumn, ellipsis: boolean) { + const { key, dataIndex, children } = item + item.align = item.align || DEFAULT_ALIGN + if (ellipsis) { + if (!key) { + item.key = dataIndex as any + } + if (!isBoolean(item.ellipsis)) { + Object.assign(item, { + ellipsis + }) + } + } + if (children && children.length) { + handleChildren(children, !!ellipsis) + } +} + +function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean) { + if (!children) return + children.forEach((item) => { + const { children } = item + handleItem(item, ellipsis) + handleChildren(children, ellipsis) + }) +} + +function handleIndexColumn( + propsRef: ComputedRef, + getPaginationRef: ComputedRef, + columns: BasicColumn[] +) { + const { t } = useI18n() + + const { showIndexColumn, indexColumnProps, isTreeTable } = unref(propsRef) + + let pushIndexColumns = false + if (unref(isTreeTable)) { + return + } + columns.forEach(() => { + const indIndex = columns.findIndex((column) => column.flag === INDEX_COLUMN_FLAG) + if (showIndexColumn) { + pushIndexColumns = indIndex === -1 + } else if (!showIndexColumn && indIndex !== -1) { + columns.splice(indIndex, 1) + } + }) + + if (!pushIndexColumns) return + + const isFixedLeft = columns.some((item) => item.fixed === 'left') + + columns.unshift({ + flag: INDEX_COLUMN_FLAG, + width: 50, + title: t('component.table.index'), + align: 'center', + customRender: ({ index }) => { + const getPagination = unref(getPaginationRef) + if (isBoolean(getPagination)) { + return `${index + 1}` + } + const { current = 1, pageSize = PAGE_SIZE } = getPagination + return ((current < 1 ? 1 : current) - 1) * pageSize + index + 1 + }, + ...(isFixedLeft + ? { + fixed: 'left' + } + : {}), + ...indexColumnProps + }) +} + +function handleActionColumn(propsRef: ComputedRef, columns: BasicColumn[]) { + const { actionColumn } = unref(propsRef) + if (!actionColumn) return + + const hasIndex = columns.findIndex((column) => column.flag === ACTION_COLUMN_FLAG) + if (hasIndex === -1) { + columns.push({ + ...columns[hasIndex], + fixed: 'right', + ...actionColumn, + flag: ACTION_COLUMN_FLAG + }) + } +} + +export function useColumns(propsRef: ComputedRef, getPaginationRef: ComputedRef) { + const columnsRef = ref(unref(propsRef).columns) as unknown as Ref + let cacheColumns = unref(propsRef).columns + + const getColumnsRef = computed(() => { + const columns = cloneDeep(unref(columnsRef)) + + handleIndexColumn(propsRef, getPaginationRef, columns) + handleActionColumn(propsRef, columns) + if (!columns) { + return [] + } + const { ellipsis } = unref(propsRef) + + columns.forEach((item) => { + const { customRender, slots } = item + + handleItem(item, Reflect.has(item, 'ellipsis') ? !!item.ellipsis : !!ellipsis && !customRender && !slots) + }) + return columns + }) + + function isIfShow(column: BasicColumn): boolean { + const ifShow = column.ifShow + + let isIfShow = true + + if (isBoolean(ifShow)) { + isIfShow = ifShow + } + if (isFunction(ifShow)) { + isIfShow = ifShow(column) + } + return isIfShow + } + const { hasPermission } = usePermission() + + const getViewColumns = computed(() => { + const viewColumns = sortFixedColumn(unref(getColumnsRef)) + + const mapFn = (column) => { + const { slots, customRender, format, edit, editRow, flag } = column + + if (!slots || !slots?.title) { + // column.slots = { title: `header-${dataIndex}`, ...(slots || {}) }; + column.customTitle = column.title + Reflect.deleteProperty(column, 'title') + } + const isDefaultAction = [INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG].includes(flag!) + if (!customRender && format && !edit && !isDefaultAction) { + column.customRender = ({ text, record, index }) => { + return formatCell(text, format, record, index) + } + } + + // edit table + if ((edit || editRow) && !isDefaultAction) { + column.customRender = renderEditCell(column) + } + return reactive(column) + } + + const columns = cloneDeep(viewColumns) + return columns + .filter((column) => hasPermission(column.auth) && isIfShow(column)) + .map((column) => { + // Support table multiple header editable + if (column.children?.length) { + column.children = column.children.map(mapFn) + } + + return mapFn(column) + }) + }) + + watch( + () => unref(propsRef).columns, + (columns) => { + columnsRef.value = columns + cacheColumns = columns?.filter((item) => !item.flag) ?? [] + } + ) + + function setCacheColumnsByField(dataIndex: string | undefined, value: Partial) { + if (!dataIndex || !value) { + return + } + cacheColumns.forEach((item) => { + if (item.dataIndex === dataIndex) { + Object.assign(item, value) + return + } + }) + } + /** + * set columns + * @param columnList key|column + */ + function setColumns(columnList: Partial[] | (string | string[])[]) { + const columns = cloneDeep(columnList) + if (!isArray(columns)) return + + if (columns.length <= 0) { + columnsRef.value = [] + return + } + + const firstColumn = columns[0] + + const cacheKeys = cacheColumns.map((item) => item.dataIndex) + + if (!isString(firstColumn) && !isArray(firstColumn)) { + columnsRef.value = columns as BasicColumn[] + } else { + const columnKeys = (columns as (string | string[])[]).map((m) => m.toString()) + const newColumns: BasicColumn[] = [] + cacheColumns.forEach((item) => { + newColumns.push({ + ...item, + defaultHidden: !columnKeys.includes(item.dataIndex?.toString() || (item.key as string)) + }) + }) + // Sort according to another array + if (!isEqual(cacheKeys, columns)) { + newColumns.sort((prev, next) => { + return columnKeys.indexOf(prev.dataIndex?.toString() as string) - columnKeys.indexOf(next.dataIndex?.toString() as string) + }) + } + columnsRef.value = newColumns + } + } + + function getColumns(opt?: GetColumnsParams) { + const { ignoreIndex, ignoreAction, sort } = opt || {} + let columns = toRaw(unref(getColumnsRef)) + if (ignoreIndex) { + columns = columns.filter((item) => item.flag !== INDEX_COLUMN_FLAG) + } + if (ignoreAction) { + columns = columns.filter((item) => item.flag !== ACTION_COLUMN_FLAG) + } + + if (sort) { + columns = sortFixedColumn(columns) + } + + return columns + } + function getCacheColumns() { + return cacheColumns + } + + return { + getColumnsRef, + getCacheColumns, + getColumns, + setColumns, + getViewColumns, + setCacheColumnsByField + } +} + +function sortFixedColumn(columns: BasicColumn[]) { + const fixedLeftColumns: BasicColumn[] = [] + const fixedRightColumns: BasicColumn[] = [] + const defColumns: BasicColumn[] = [] + for (const column of columns) { + if (column.fixed === 'left') { + fixedLeftColumns.push(column) + continue + } + if (column.fixed === 'right') { + fixedRightColumns.push(column) + continue + } + defColumns.push(column) + } + return [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter((item) => !item.defaultHidden) +} + +// format cell +export function formatCell(text: string, format: CellFormat, record: Recordable, index: number) { + if (!format) { + return text + } + + // custom function + if (isFunction(format)) { + return format(text, record, index) + } + + try { + // date type + const DATE_FORMAT_PREFIX = 'date|' + if (isString(format) && format.startsWith(DATE_FORMAT_PREFIX) && text) { + const dateFormat = format.replace(DATE_FORMAT_PREFIX, '') + + if (!dateFormat) { + return text + } + return formatToDate(text, dateFormat) + } + + // Map + if (isMap(format)) { + return format.get(text) + } + } catch (error) { + return text + } +} diff --git a/src/components/Table/src/hooks/useCustomRow.ts b/src/components/Table/src/hooks/useCustomRow.ts new file mode 100644 index 00000000..77f2fd44 --- /dev/null +++ b/src/components/Table/src/hooks/useCustomRow.ts @@ -0,0 +1,94 @@ +import type { ComputedRef } from 'vue' +import type { BasicTableProps } from '../types/table' +import { unref } from 'vue' +import { ROW_KEY } from '../const' +import { isString, isFunction } from '@/utils/is' + +interface Options { + setSelectedRowKeys: (keys: string[]) => void + getSelectRowKeys: () => string[] + clearSelectedRowKeys: () => void + emit: EmitType + getAutoCreateKey: ComputedRef +} + +function getKey(record: Recordable, rowKey: string | ((record: Record) => string) | undefined, autoCreateKey?: boolean) { + if (!rowKey || autoCreateKey) { + return record[ROW_KEY] + } + if (isString(rowKey)) { + return record[rowKey] + } + if (isFunction(rowKey)) { + return record[rowKey(record)] + } + return null +} + +export function useCustomRow( + propsRef: ComputedRef, + { setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options +) { + const customRow = (record: Recordable, index: number) => { + return { + onClick: (e: Event) => { + e?.stopPropagation() + function handleClick() { + const { rowSelection, rowKey, clickToRowSelect } = unref(propsRef) + if (!rowSelection || !clickToRowSelect) return + const keys = getSelectRowKeys() || [] + const key = getKey(record, rowKey, unref(getAutoCreateKey)) + if (!key) return + + const isCheckbox = rowSelection.type === 'checkbox' + if (isCheckbox) { + // 找到tr + const tr: HTMLElement = (e as MouseEvent).composedPath?.().find((dom: HTMLElement) => dom.tagName === 'TR') as HTMLElement + if (!tr) return + // 找到Checkbox,检查是否为disabled + const checkBox = tr.querySelector('input[type=checkbox]') + if (!checkBox || checkBox.hasAttribute('disabled')) return + if (!keys.includes(key)) { + setSelectedRowKeys([...keys, key]) + return + } + const keyIndex = keys.findIndex((item) => item === key) + keys.splice(keyIndex, 1) + setSelectedRowKeys(keys) + return + } + + const isRadio = rowSelection.type === 'radio' + if (isRadio) { + if (!keys.includes(key)) { + if (keys.length) { + clearSelectedRowKeys() + } + setSelectedRowKeys([key]) + return + } + clearSelectedRowKeys() + } + } + handleClick() + emit('row-click', record, index, e) + }, + onDblclick: (event: Event) => { + emit('row-dbClick', record, index, event) + }, + onContextmenu: (event: Event) => { + emit('row-contextmenu', record, index, event) + }, + onMouseenter: (event: Event) => { + emit('row-mouseenter', record, index, event) + }, + onMouseleave: (event: Event) => { + emit('row-mouseleave', record, index, event) + } + } + } + + return { + customRow + } +} diff --git a/src/components/Table/src/hooks/useDataSource.ts b/src/components/Table/src/hooks/useDataSource.ts new file mode 100644 index 00000000..50601262 --- /dev/null +++ b/src/components/Table/src/hooks/useDataSource.ts @@ -0,0 +1,349 @@ +import type { BasicTableProps, FetchParams, SorterResult } from '../types/table' +import type { PaginationProps } from '../types/pagination' +import { ref, unref, ComputedRef, computed, onMounted, watch, reactive, Ref, watchEffect } from 'vue' +import { useTimeoutFn } from '@/hooks/core/useTimeout' +import { buildUUID } from '@/utils/uuid' +import { isFunction, isBoolean, isObject } from '@/utils/is' +import { get, cloneDeep, merge } from 'lodash-es' +import { FETCH_SETTING, ROW_KEY, PAGE_SIZE } from '../const' + +interface ActionType { + getPaginationInfo: ComputedRef + setPagination: (info: Partial) => void + setLoading: (loading: boolean) => void + getFieldsValue: () => Recordable + clearSelectedRowKeys: () => void + tableData: Ref +} + +interface SearchState { + sortInfo: Recordable + filterInfo: Record +} +export function useDataSource( + propsRef: ComputedRef, + { getPaginationInfo, setPagination, setLoading, getFieldsValue, clearSelectedRowKeys, tableData }: ActionType, + emit: EmitType +) { + const searchState = reactive({ + sortInfo: {}, + filterInfo: {} + }) + const dataSourceRef = ref([]) + const rawDataSourceRef = ref({}) + + watchEffect(() => { + tableData.value = unref(dataSourceRef) + }) + + watch( + () => unref(propsRef).dataSource, + () => { + const { dataSource, api } = unref(propsRef) + !api && dataSource && (dataSourceRef.value = dataSource) + }, + { + immediate: true + } + ) + + function handleTableChange(pagination: PaginationProps, filters: Partial>, sorter: SorterResult) { + const { clearSelectOnPageChange, sortFn, filterFn } = unref(propsRef) + if (clearSelectOnPageChange) { + clearSelectedRowKeys() + } + setPagination(pagination) + + const params: Recordable = {} + if (sorter && isFunction(sortFn)) { + const sortInfo = sortFn(sorter) + searchState.sortInfo = sortInfo + params.sortInfo = sortInfo + } + + if (filters && isFunction(filterFn)) { + const filterInfo = filterFn(filters) + searchState.filterInfo = filterInfo + params.filterInfo = filterInfo + } + fetch(params) + } + + function setTableKey(items: any[]) { + if (!items || !Array.isArray(items)) return + items.forEach((item) => { + if (!item[ROW_KEY]) { + item[ROW_KEY] = buildUUID() + } + if (item.children && item.children.length) { + setTableKey(item.children) + } + }) + } + + const getAutoCreateKey = computed(() => { + return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey + }) + + const getRowKey = computed(() => { + const { rowKey } = unref(propsRef) + return unref(getAutoCreateKey) ? ROW_KEY : rowKey + }) + + const getDataSourceRef = computed(() => { + const dataSource = unref(dataSourceRef) + if (!dataSource || dataSource.length === 0) { + return unref(dataSourceRef) + } + if (unref(getAutoCreateKey)) { + const firstItem = dataSource[0] + const lastItem = dataSource[dataSource.length - 1] + + if (firstItem && lastItem) { + if (!firstItem[ROW_KEY] || !lastItem[ROW_KEY]) { + const data = cloneDeep(unref(dataSourceRef)) + data.forEach((item) => { + if (!item[ROW_KEY]) { + item[ROW_KEY] = buildUUID() + } + if (item.children && item.children.length) { + setTableKey(item.children) + } + }) + dataSourceRef.value = data + } + } + } + return unref(dataSourceRef) + }) + + async function updateTableData(index: number, key: string, value: any) { + const record = dataSourceRef.value[index] + if (record) { + dataSourceRef.value[index][key] = value + } + return dataSourceRef.value[index] + } + + function updateTableDataRecord(rowKey: string | number, record: Recordable): Recordable | undefined { + const row = findTableDataRecord(rowKey) + + if (row) { + for (const field in row) { + if (Reflect.has(record, field)) row[field] = record[field] + } + return row + } + } + + function deleteTableDataRecord(rowKey: string | number | string[] | number[]) { + if (!dataSourceRef.value || dataSourceRef.value.length == 0) return + const rowKeyName = unref(getRowKey) + if (!rowKeyName) return + const rowKeys = !Array.isArray(rowKey) ? [rowKey] : rowKey + + function deleteRow(data, key) { + const row: { index: number; data: [] } = findRow(data, key) + if (row === null || row.index === -1) { + return + } + row.data.splice(row.index, 1) + + function findRow(data, key) { + if (data === null || data === undefined) { + return null + } + for (let i = 0; i < data.length; i++) { + const row = data[i] + let targetKeyName: string = rowKeyName as string + if (isFunction(rowKeyName)) { + targetKeyName = rowKeyName(row) + } + if (row[targetKeyName] === key) { + return { index: i, data } + } + if (row.children?.length > 0) { + const result = findRow(row.children, key) + if (result != null) { + return result + } + } + } + return null + } + } + + for (const key of rowKeys) { + deleteRow(dataSourceRef.value, key) + deleteRow(unref(propsRef).dataSource, key) + } + setPagination({ + total: unref(propsRef).dataSource?.length + }) + } + + function insertTableDataRecord(record: Recordable | Recordable[], index: number): Recordable[] | undefined { + // if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; + index = index ?? dataSourceRef.value?.length + const _record = isObject(record) ? [record as Recordable] : (record as Recordable[]) + unref(dataSourceRef).splice(index, 0, ..._record) + return unref(dataSourceRef) + } + + function findTableDataRecord(rowKey: string | number) { + if (!dataSourceRef.value || dataSourceRef.value.length == 0) return + + const rowKeyName = unref(getRowKey) + if (!rowKeyName) return + + const { childrenColumnName = 'children' } = unref(propsRef) + + const findRow = (array: any[]) => { + let ret + array.some(function iter(r) { + if (typeof rowKeyName === 'function') { + if ((rowKeyName(r) as string) === rowKey) { + ret = r + return true + } + } else { + if (Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey) { + ret = r + return true + } + } + return r[childrenColumnName] && r[childrenColumnName].some(iter) + }) + return ret + } + + // const row = dataSourceRef.value.find(r => { + // if (typeof rowKeyName === 'function') { + // return (rowKeyName(r) as string) === rowKey + // } else { + // return Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey + // } + // }) + return findRow(dataSourceRef.value) + } + + async function fetch(opt?: FetchParams) { + const { api, searchInfo, defSort, fetchSetting, beforeFetch, afterFetch, useSearchForm, pagination } = unref(propsRef) + if (!api || !isFunction(api)) return + try { + setLoading(true) + const { pageField, sizeField, listField, totalField } = Object.assign({}, FETCH_SETTING, fetchSetting) + let pageParams: Recordable = {} + + const { current = 1, pageSize = PAGE_SIZE } = unref(getPaginationInfo) as PaginationProps + + if ((isBoolean(pagination) && !pagination) || isBoolean(getPaginationInfo)) { + pageParams = {} + } else { + pageParams[pageField] = (opt && opt.page) || current + pageParams[sizeField] = pageSize + } + + const { sortInfo = {}, filterInfo } = searchState + + let params: Recordable = merge( + pageParams, + useSearchForm ? getFieldsValue() : {}, + searchInfo, + opt?.searchInfo ?? {}, + defSort, + sortInfo, + filterInfo, + opt?.sortInfo ?? {}, + opt?.filterInfo ?? {} + ) + if (beforeFetch && isFunction(beforeFetch)) { + params = (await beforeFetch(params)) || params + } + + const res = await api(params) + rawDataSourceRef.value = res + + const isArrayResult = Array.isArray(res) + + let resultItems: Recordable[] = isArrayResult ? res : get(res, listField) + const resultTotal: number = isArrayResult ? res.length : get(res, totalField) + + // 假如数据变少,导致总页数变少并小于当前选中页码,通过getPaginationRef获取到的页码是不正确的,需获取正确的页码再次执行 + if (Number(resultTotal)) { + const currentTotalPage = Math.ceil(resultTotal / pageSize) + if (current > currentTotalPage) { + setPagination({ + current: currentTotalPage + }) + return await fetch(opt) + } + } + + if (afterFetch && isFunction(afterFetch)) { + resultItems = (await afterFetch(resultItems)) || resultItems + } + dataSourceRef.value = resultItems + setPagination({ + total: resultTotal || 0 + }) + if (opt && opt.page) { + setPagination({ + current: opt.page || 1 + }) + } + emit('fetch-success', { + items: unref(resultItems), + total: resultTotal + }) + return resultItems + } catch (error) { + emit('fetch-error', error) + dataSourceRef.value = [] + setPagination({ + total: 0 + }) + } finally { + setLoading(false) + } + } + + function setTableData[]>>(values: T[]) { + dataSourceRef.value = values + } + + function getDataSource() { + return getDataSourceRef.value as T[] + } + + function getRawDataSource() { + return rawDataSourceRef.value as T + } + + async function reload(opt?: FetchParams) { + return await fetch(opt) + } + + onMounted(() => { + useTimeoutFn(() => { + unref(propsRef).immediate && fetch() + }, 16) + }) + + return { + getDataSourceRef, + getDataSource, + getRawDataSource, + getRowKey, + setTableData, + getAutoCreateKey, + fetch, + reload, + updateTableData, + updateTableDataRecord, + deleteTableDataRecord, + insertTableDataRecord, + findTableDataRecord, + handleTableChange + } +} diff --git a/src/components/Table/src/hooks/useLoading.ts b/src/components/Table/src/hooks/useLoading.ts new file mode 100644 index 00000000..ab5316c5 --- /dev/null +++ b/src/components/Table/src/hooks/useLoading.ts @@ -0,0 +1,21 @@ +import { ref, ComputedRef, unref, computed, watch } from 'vue' +import type { BasicTableProps } from '../types/table' + +export function useLoading(props: ComputedRef) { + const loadingRef = ref(unref(props).loading) + + watch( + () => unref(props).loading, + (loading) => { + loadingRef.value = loading + } + ) + + const getLoading = computed(() => unref(loadingRef)) + + function setLoading(loading: boolean) { + loadingRef.value = loading + } + + return { getLoading, setLoading } +} diff --git a/src/components/Table/src/hooks/usePagination.tsx b/src/components/Table/src/hooks/usePagination.tsx new file mode 100644 index 00000000..d33eb95e --- /dev/null +++ b/src/components/Table/src/hooks/usePagination.tsx @@ -0,0 +1,85 @@ +import type { PaginationProps } from '../types/pagination' +import type { BasicTableProps } from '../types/table' +import { computed, unref, ref, ComputedRef, watch } from 'vue' +import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue' +import { isBoolean } from '@/utils/is' +import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const' +import { useI18n } from '@/hooks/web/useI18n' + +interface ItemRender { + page: number + type: 'page' | 'prev' | 'next' + originalElement: any +} + +function itemRender({ page, type, originalElement }: ItemRender) { + if (type === 'prev') { + return page === 0 ? null : + } else if (type === 'next') { + return page === 1 ? null : + } + return originalElement +} + +export function usePagination(refProps: ComputedRef) { + const { t } = useI18n() + + const configRef = ref({}) + const show = ref(true) + + watch( + () => unref(refProps).pagination, + (pagination) => { + if (!isBoolean(pagination) && pagination) { + configRef.value = { + ...unref(configRef), + ...(pagination ?? {}) + } + } + } + ) + + const getPaginationInfo = computed((): PaginationProps | boolean => { + const { pagination } = unref(refProps) + + if (!unref(show) || (isBoolean(pagination) && !pagination)) { + return false + } + + return { + current: 1, + pageSize: PAGE_SIZE, + size: 'small', + defaultPageSize: PAGE_SIZE, + showTotal: (total) => t('component.table.total', { total }), + showSizeChanger: true, + pageSizeOptions: PAGE_SIZE_OPTIONS, + itemRender: itemRender, + showQuickJumper: true, + ...(isBoolean(pagination) ? {} : pagination), + ...unref(configRef) + } + }) + + function setPagination(info: Partial) { + const paginationInfo = unref(getPaginationInfo) + configRef.value = { + ...(!isBoolean(paginationInfo) ? paginationInfo : {}), + ...info + } + } + + function getPagination() { + return unref(getPaginationInfo) + } + + function getShowPagination() { + return unref(show) + } + + async function setShowPagination(flag: boolean) { + show.value = flag + } + + return { getPagination, getPaginationInfo, setShowPagination, getShowPagination, setPagination } +} diff --git a/src/components/Table/src/hooks/useRowSelection.ts b/src/components/Table/src/hooks/useRowSelection.ts new file mode 100644 index 00000000..edf73241 --- /dev/null +++ b/src/components/Table/src/hooks/useRowSelection.ts @@ -0,0 +1,118 @@ +import { isFunction } from '@/utils/is' +import type { BasicTableProps, TableRowSelection } from '../types/table' +import { computed, ComputedRef, nextTick, Ref, ref, toRaw, unref, watch } from 'vue' +import { ROW_KEY } from '../const' +import { omit } from 'lodash-es' +import { findNodeAll } from '@/utils/helper/treeHelper' + +export function useRowSelection(propsRef: ComputedRef, tableData: Ref, emit: EmitType) { + const selectedRowKeysRef = ref([]) + const selectedRowRef = ref([]) + + const getRowSelectionRef = computed((): TableRowSelection | null => { + const { rowSelection } = unref(propsRef) + if (!rowSelection) { + return null + } + + return { + selectedRowKeys: unref(selectedRowKeysRef), + onChange: (selectedRowKeys: string[]) => { + setSelectedRowKeys(selectedRowKeys) + }, + ...omit(rowSelection, ['onChange']) + } + }) + + watch( + () => unref(propsRef).rowSelection?.selectedRowKeys, + (v: string[]) => { + setSelectedRowKeys(v) + } + ) + + watch( + () => unref(selectedRowKeysRef), + () => { + nextTick(() => { + const { rowSelection } = unref(propsRef) + if (rowSelection) { + const { onChange } = rowSelection + if (onChange && isFunction(onChange)) onChange(getSelectRowKeys(), getSelectRows()) + } + emit('selection-change', { + keys: getSelectRowKeys(), + rows: getSelectRows() + }) + }) + }, + { deep: true } + ) + + const getAutoCreateKey = computed(() => { + return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey + }) + + const getRowKey = computed(() => { + const { rowKey } = unref(propsRef) + return unref(getAutoCreateKey) ? ROW_KEY : rowKey + }) + + function setSelectedRowKeys(rowKeys: string[]) { + selectedRowKeysRef.value = rowKeys + const allSelectedRows = findNodeAll( + toRaw(unref(tableData)).concat(toRaw(unref(selectedRowRef))), + (item) => rowKeys?.includes(item[unref(getRowKey) as string]), + { + children: propsRef.value.childrenColumnName ?? 'children' + } + ) + const trueSelectedRows: any[] = [] + rowKeys?.forEach((key: string) => { + const found = allSelectedRows.find((item) => item[unref(getRowKey) as string] === key) + found && trueSelectedRows.push(found) + }) + selectedRowRef.value = trueSelectedRows + } + + function setSelectedRows(rows: Recordable[]) { + selectedRowRef.value = rows + } + + function clearSelectedRowKeys() { + selectedRowRef.value = [] + selectedRowKeysRef.value = [] + } + + function deleteSelectRowByKey(key: string) { + const selectedRowKeys = unref(selectedRowKeysRef) + const index = selectedRowKeys.findIndex((item) => item === key) + if (index !== -1) { + unref(selectedRowKeysRef).splice(index, 1) + } + } + + function getSelectRowKeys() { + return unref(selectedRowKeysRef) + } + + function getSelectRows() { + // const ret = toRaw(unref(selectedRowRef)).map((item) => toRaw(item)); + return unref(selectedRowRef) as T[] + } + + function getRowSelection() { + return unref(getRowSelectionRef)! + } + + return { + getRowSelection, + getRowSelectionRef, + getSelectRows, + getSelectRowKeys, + setSelectedRowKeys, + clearSelectedRowKeys, + deleteSelectRowByKey, + setSelectedRows + } +} diff --git a/src/components/Table/src/hooks/useScrollTo.ts b/src/components/Table/src/hooks/useScrollTo.ts new file mode 100644 index 00000000..f009b735 --- /dev/null +++ b/src/components/Table/src/hooks/useScrollTo.ts @@ -0,0 +1,50 @@ +import type { ComputedRef, Ref } from 'vue' +import { nextTick, unref } from 'vue' +import { warn } from '@/utils/log' + +export function useTableScrollTo(tableElRef: Ref, getDataSourceRef: ComputedRef) { + let bodyEl: HTMLElement | null + + async function findTargetRowToScroll(targetRowData: Recordable) { + const { id } = targetRowData + const targetRowEl: HTMLElement | null | undefined = bodyEl?.querySelector(`[data-row-key="${id}"]`) + //Add a delay to get new dataSource + await nextTick() + bodyEl?.scrollTo({ + top: targetRowEl?.offsetTop ?? 0, + behavior: 'smooth' + }) + } + + function scrollTo(pos: string): void { + const table = unref(tableElRef) + if (!table) return + + const tableEl: Element = table.$el + if (!tableEl) return + + if (!bodyEl) { + bodyEl = tableEl.querySelector('.ant-table-body') + if (!bodyEl) return + } + + const dataSource = unref(getDataSourceRef) + if (!dataSource) return + + // judge pos type + if (pos === 'top') { + findTargetRowToScroll(dataSource[0]) + } else if (pos === 'bottom') { + findTargetRowToScroll(dataSource[dataSource.length - 1]) + } else { + const targetRowData = dataSource.find((data) => data.id === pos) + if (targetRowData) { + findTargetRowToScroll(targetRowData) + } else { + warn(`id: ${pos} doesn't exist`) + } + } + } + + return { scrollTo } +} diff --git a/src/components/Table/src/hooks/useTable.ts b/src/components/Table/src/hooks/useTable.ts new file mode 100644 index 00000000..74ca26b4 --- /dev/null +++ b/src/components/Table/src/hooks/useTable.ts @@ -0,0 +1,168 @@ +import type { BasicTableProps, TableActionType, FetchParams, BasicColumn } from '../types/table' +import type { PaginationProps } from '../types/pagination' +import type { DynamicProps } from '@/types/utils' +import type { FormActionType } from '@/components/Form' +import type { WatchStopHandle } from 'vue' +import { getDynamicProps } from '@/utils' +import { ref, onUnmounted, unref, watch, toRaw } from 'vue' +import { isProdMode } from '@/utils/env' +import { error } from '@/utils/log' + +type Props = Partial> + +type UseTableMethod = TableActionType & { + getForm: () => FormActionType +} + +export function useTable(tableProps?: Props): [ + (instance: TableActionType, formInstance: UseTableMethod) => void, + TableActionType & { + getForm: () => FormActionType + } +] { + const tableRef = ref>(null) + const loadedRef = ref>(false) + const formRef = ref>(null) + + let stopWatch: WatchStopHandle + + function register(instance: TableActionType, formInstance: UseTableMethod) { + isProdMode() && + onUnmounted(() => { + tableRef.value = null + loadedRef.value = null + }) + + if (unref(loadedRef) && isProdMode() && instance === unref(tableRef)) return + + tableRef.value = instance + formRef.value = formInstance + tableProps && instance.setProps(getDynamicProps(tableProps)) + loadedRef.value = true + + stopWatch?.() + + stopWatch = watch( + () => tableProps, + () => { + tableProps && instance.setProps(getDynamicProps(tableProps)) + }, + { + immediate: true, + deep: true + } + ) + } + + function getTableInstance(): TableActionType { + const table = unref(tableRef) + if (!table) { + error('The table instance has not been obtained yet, please make sure the table is presented when performing the table operation!') + } + return table as TableActionType + } + + const methods: TableActionType & { + getForm: () => FormActionType + } = { + reload: async (opt?: FetchParams) => { + return await getTableInstance().reload(opt) + }, + setProps: (props: Partial) => { + getTableInstance().setProps(props) + }, + redoHeight: () => { + getTableInstance().redoHeight() + }, + setSelectedRows: (rows: Recordable[]) => { + return toRaw(getTableInstance().setSelectedRows(rows)) + }, + setLoading: (loading: boolean) => { + getTableInstance().setLoading(loading) + }, + getDataSource: () => { + return getTableInstance().getDataSource() + }, + getRawDataSource: () => { + return getTableInstance().getRawDataSource() + }, + getColumns: ({ ignoreIndex = false }: { ignoreIndex?: boolean } = {}) => { + const columns = getTableInstance().getColumns({ ignoreIndex }) || [] + return toRaw(columns) + }, + setColumns: (columns: BasicColumn[]) => { + getTableInstance().setColumns(columns) + }, + setTableData: (values: any[]) => { + return getTableInstance().setTableData(values) + }, + setPagination: (info: Partial) => { + return getTableInstance().setPagination(info) + }, + deleteSelectRowByKey: (key: string) => { + getTableInstance().deleteSelectRowByKey(key) + }, + getSelectRowKeys: () => { + return toRaw(getTableInstance().getSelectRowKeys()) + }, + getSelectRows: () => { + return toRaw(getTableInstance().getSelectRows()) + }, + clearSelectedRowKeys: () => { + getTableInstance().clearSelectedRowKeys() + }, + setSelectedRowKeys: (keys: string[] | number[]) => { + getTableInstance().setSelectedRowKeys(keys) + }, + getPaginationRef: () => { + return getTableInstance().getPaginationRef() + }, + getSize: () => { + return toRaw(getTableInstance().getSize()) + }, + updateTableData: (index: number, key: string, value: any) => { + return getTableInstance().updateTableData(index, key, value) + }, + deleteTableDataRecord: (rowKey: string | number | string[] | number[]) => { + return getTableInstance().deleteTableDataRecord(rowKey) + }, + insertTableDataRecord: (record: Recordable | Recordable[], index?: number) => { + return getTableInstance().insertTableDataRecord(record, index) + }, + updateTableDataRecord: (rowKey: string | number, record: Recordable) => { + return getTableInstance().updateTableDataRecord(rowKey, record) + }, + findTableDataRecord: (rowKey: string | number) => { + return getTableInstance().findTableDataRecord(rowKey) + }, + getRowSelection: () => { + return toRaw(getTableInstance().getRowSelection()) + }, + getCacheColumns: () => { + return toRaw(getTableInstance().getCacheColumns()) + }, + getForm: () => { + return unref(formRef) as unknown as FormActionType + }, + setShowPagination: async (show: boolean) => { + getTableInstance().setShowPagination(show) + }, + getShowPagination: () => { + return toRaw(getTableInstance().getShowPagination()) + }, + expandAll: () => { + getTableInstance().expandAll() + }, + expandRows: (keys: string[]) => { + getTableInstance().expandRows(keys) + }, + collapseAll: () => { + getTableInstance().collapseAll() + }, + scrollTo: (pos: string) => { + getTableInstance().scrollTo(pos) + } + } + + return [register, methods] +} diff --git a/src/components/Table/src/hooks/useTableContext.ts b/src/components/Table/src/hooks/useTableContext.ts new file mode 100644 index 00000000..b40e734e --- /dev/null +++ b/src/components/Table/src/hooks/useTableContext.ts @@ -0,0 +1,22 @@ +import type { Ref } from 'vue' +import type { BasicTableProps, TableActionType } from '../types/table' +import { provide, inject, ComputedRef } from 'vue' + +const key = Symbol('basic-table') + +type Instance = TableActionType & { + wrapRef: Ref> + getBindValues: ComputedRef +} + +type RetInstance = Omit & { + getBindValues: ComputedRef +} + +export function createTableContext(instance: Instance) { + provide(key, instance) +} + +export function useTableContext(): RetInstance { + return inject(key) as RetInstance +} diff --git a/src/components/Table/src/hooks/useTableExpand.ts b/src/components/Table/src/hooks/useTableExpand.ts new file mode 100644 index 00000000..05e22f80 --- /dev/null +++ b/src/components/Table/src/hooks/useTableExpand.ts @@ -0,0 +1,61 @@ +import type { ComputedRef, Ref } from 'vue' +import type { BasicTableProps } from '../types/table' +import { computed, unref, ref, toRaw } from 'vue' +import { ROW_KEY } from '../const' + +export function useTableExpand(propsRef: ComputedRef, tableData: Ref, emit: EmitType) { + const expandedRowKeys = ref([]) + + const getAutoCreateKey = computed(() => { + return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey + }) + + const getRowKey = computed(() => { + const { rowKey } = unref(propsRef) + return unref(getAutoCreateKey) ? ROW_KEY : rowKey + }) + + const getExpandOption = computed(() => { + const { isTreeTable } = unref(propsRef) + if (!isTreeTable) return {} + + return { + expandedRowKeys: unref(expandedRowKeys), + onExpandedRowsChange: (keys: string[]) => { + expandedRowKeys.value = keys + emit('expanded-rows-change', keys) + } + } + }) + + function expandAll() { + const keys = getAllKeys() + expandedRowKeys.value = keys + } + + function expandRows(keys: string[]) { + // use row ID expands the specified table row + const { isTreeTable } = unref(propsRef) + if (!isTreeTable) return + expandedRowKeys.value = [...expandedRowKeys.value, ...keys] + } + + function getAllKeys(data?: Recordable[]) { + const keys: string[] = [] + const { childrenColumnName } = unref(propsRef) + toRaw(data || unref(tableData)).forEach((item) => { + keys.push(item[unref(getRowKey) as string]) + const children = item[childrenColumnName || 'children'] + if (children?.length) { + keys.push(...getAllKeys(children)) + } + }) + return keys + } + + function collapseAll() { + expandedRowKeys.value = [] + } + + return { getExpandOption, expandAll, expandRows, collapseAll } +} diff --git a/src/components/Table/src/hooks/useTableFooter.ts b/src/components/Table/src/hooks/useTableFooter.ts new file mode 100644 index 00000000..ca327e64 --- /dev/null +++ b/src/components/Table/src/hooks/useTableFooter.ts @@ -0,0 +1,52 @@ +import type { ComputedRef, Ref } from 'vue' +import type { BasicTableProps } from '../types/table' +import { unref, computed, h, nextTick, watchEffect } from 'vue' +import TableFooter from '../components/TableFooter.vue' +import { useEventListener } from '@/hooks/event/useEventListener' + +export function useTableFooter( + propsRef: ComputedRef, + scrollRef: ComputedRef<{ + x: string | number | true + y: string | number | null + scrollToFirstRowOnChange: boolean + }>, + tableElRef: Ref, + getDataSourceRef: ComputedRef +) { + const getIsEmptyData = computed(() => { + return (unref(getDataSourceRef) || []).length === 0 + }) + + const getFooterProps = computed((): Recordable | undefined => { + const { summaryFunc, showSummary, summaryData } = unref(propsRef) + return showSummary && !unref(getIsEmptyData) ? () => h(TableFooter, { summaryFunc, summaryData, scroll: unref(scrollRef) }) : undefined + }) + + watchEffect(() => { + handleSummary() + }) + + function handleSummary() { + const { showSummary } = unref(propsRef) + if (!showSummary || unref(getIsEmptyData)) return + + nextTick(() => { + const tableEl = unref(tableElRef) + if (!tableEl) return + const bodyDom = tableEl.$el.querySelector('.ant-table-content') + useEventListener({ + el: bodyDom, + name: 'scroll', + listener: () => { + const footerBodyDom = tableEl.$el.querySelector('.ant-table-footer .ant-table-content') as HTMLDivElement + if (!footerBodyDom || !bodyDom) return + footerBodyDom.scrollLeft = bodyDom.scrollLeft + }, + wait: 0, + options: true + }) + }) + } + return { getFooterProps } +} diff --git a/src/components/Table/src/hooks/useTableForm.ts b/src/components/Table/src/hooks/useTableForm.ts new file mode 100644 index 00000000..1af5b499 --- /dev/null +++ b/src/components/Table/src/hooks/useTableForm.ts @@ -0,0 +1,48 @@ +import type { ComputedRef, Slots } from 'vue' +import type { BasicTableProps, FetchParams } from '../types/table' +import { unref, computed } from 'vue' +import type { FormProps } from '@/components/Form' +import { isFunction } from '@/utils/is' + +export function useTableForm( + propsRef: ComputedRef, + slots: Slots, + fetch: (opt?: FetchParams | undefined) => Promise, + getLoading: ComputedRef +) { + const getFormProps = computed((): Partial => { + const { formConfig } = unref(propsRef) + const { submitButtonOptions } = formConfig || {} + return { + showAdvancedButton: true, + ...formConfig, + submitButtonOptions: { loading: unref(getLoading), ...submitButtonOptions }, + compact: true + } + }) + + const getFormSlotKeys: ComputedRef = computed(() => { + const keys = Object.keys(slots) + return keys.map((item) => (item.startsWith('form-') ? item : null)).filter((item) => !!item) as string[] + }) + + function replaceFormSlotKey(key: string) { + if (!key) return '' + return key?.replace?.(/form\-/, '') ?? '' + } + + function handleSearchInfoChange(info: Recordable) { + const { handleSearchInfoFn } = unref(propsRef) + if (handleSearchInfoFn && isFunction(handleSearchInfoFn)) { + info = handleSearchInfoFn(info) || info + } + fetch({ searchInfo: info, page: 1 }) + } + + return { + getFormProps, + replaceFormSlotKey, + getFormSlotKeys, + handleSearchInfoChange + } +} diff --git a/src/components/Table/src/hooks/useTableHeader.ts b/src/components/Table/src/hooks/useTableHeader.ts new file mode 100644 index 00000000..e1015731 --- /dev/null +++ b/src/components/Table/src/hooks/useTableHeader.ts @@ -0,0 +1,50 @@ +import type { ComputedRef, Slots } from 'vue' +import type { BasicTableProps, InnerHandlers } from '../types/table' +import { unref, computed, h } from 'vue' +import TableHeader from '../components/TableHeader.vue' +import { isString } from '@/utils/is' +import { getSlot } from '@/utils/helper/tsxHelper' + +export function useTableHeader(propsRef: ComputedRef, slots: Slots, handlers: InnerHandlers) { + const getHeaderProps = computed((): Recordable => { + const { title, showTableSetting, titleHelpMessage, tableSetting } = unref(propsRef) + const hideTitle = !slots.tableTitle && !title && !slots.toolbar && !showTableSetting + if (hideTitle && !isString(title)) { + return {} + } + + return { + title: hideTitle + ? null + : () => + h( + TableHeader, + { + title, + titleHelpMessage, + showTableSetting, + tableSetting, + onColumnsChange: handlers.onColumnsChange + } as Recordable, + { + ...(slots.toolbar + ? { + toolbar: () => getSlot(slots, 'toolbar') + } + : {}), + ...(slots.tableTitle + ? { + tableTitle: () => getSlot(slots, 'tableTitle') + } + : {}), + ...(slots.headerTop + ? { + headerTop: () => getSlot(slots, 'headerTop') + } + : {}) + } + ) + } + }) + return { getHeaderProps } +} diff --git a/src/components/Table/src/hooks/useTableScroll.ts b/src/components/Table/src/hooks/useTableScroll.ts new file mode 100644 index 00000000..235a2ab1 --- /dev/null +++ b/src/components/Table/src/hooks/useTableScroll.ts @@ -0,0 +1,209 @@ +import type { BasicTableProps, TableRowSelection, BasicColumn } from '../types/table' +import { Ref, ComputedRef, ref } from 'vue' +import { computed, unref, nextTick, watch } from 'vue' +import { getViewportOffset } from '@/utils/domUtils' +import { isBoolean } from '@/utils/is' +import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn' +import { useModalContext } from '@/components/Modal' +import { onMountedOrActivated } from '@/hooks/core/onMountedOrActivated' +import { useDebounceFn } from '@vueuse/core' + +export function useTableScroll( + propsRef: ComputedRef, + tableElRef: Ref, + columnsRef: ComputedRef, + rowSelectionRef: ComputedRef, + getDataSourceRef: ComputedRef, + wrapRef: Ref, + formRef: Ref +) { + const tableHeightRef: Ref> = ref(167) + const modalFn = useModalContext() + + // Greater than animation time 280 + const debounceRedoHeight = useDebounceFn(redoHeight, 100) + + const getCanResize = computed(() => { + const { canResize, scroll } = unref(propsRef) + return canResize && !(scroll || {}).y + }) + + watch( + () => [unref(getCanResize), unref(getDataSourceRef)?.length], + () => { + debounceRedoHeight() + }, + { + flush: 'post' + } + ) + + function redoHeight() { + nextTick(() => { + calcTableHeight() + }) + } + + function setHeight(height: number) { + tableHeightRef.value = height + // Solve the problem of modal adaptive height calculation when the form is placed in the modal + modalFn?.redoModalHeight?.() + } + + // No need to repeat queries + let paginationEl: HTMLElement | null + let footerEl: HTMLElement | null + let bodyEl: HTMLElement | null + + async function calcTableHeight() { + const { resizeHeightOffset, pagination, maxHeight, isCanResizeParent, useSearchForm } = unref(propsRef) + const tableData = unref(getDataSourceRef) + + const table = unref(tableElRef) + if (!table) return + + const tableEl: Element = table.$el + if (!tableEl) return + + if (!bodyEl) { + bodyEl = tableEl.querySelector('.ant-table-body') + if (!bodyEl) return + } + + const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight + const hasScrollBarX = bodyEl.scrollWidth > bodyEl.clientWidth + + if (hasScrollBarY) { + tableEl.classList.contains('hide-scrollbar-y') && tableEl.classList.remove('hide-scrollbar-y') + } else { + !tableEl.classList.contains('hide-scrollbar-y') && tableEl.classList.add('hide-scrollbar-y') + } + + if (hasScrollBarX) { + tableEl.classList.contains('hide-scrollbar-x') && tableEl.classList.remove('hide-scrollbar-x') + } else { + !tableEl.classList.contains('hide-scrollbar-x') && tableEl.classList.add('hide-scrollbar-x') + } + + bodyEl!.style.height = 'unset' + + if (!unref(getCanResize) || !unref(tableData) || tableData.length === 0) return + + await nextTick() + // Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight + + const headEl = tableEl.querySelector('.ant-table-thead ') + + if (!headEl) return + + // Table height from bottom height-custom offset + let paddingHeight = 32 + // Pager height + let paginationHeight = 2 + if (!isBoolean(pagination)) { + paginationEl = tableEl.querySelector('.ant-pagination') as HTMLElement + if (paginationEl) { + const offsetHeight = paginationEl.offsetHeight + paginationHeight += offsetHeight || 0 + } else { + // TODO First fix 24 + paginationHeight += 24 + } + } else { + paginationHeight = -8 + } + + let footerHeight = 0 + if (!isBoolean(pagination)) { + if (!footerEl) { + footerEl = tableEl.querySelector('.ant-table-footer') as HTMLElement + } else { + const offsetHeight = footerEl.offsetHeight + footerHeight += offsetHeight || 0 + } + } + + let headerHeight = 0 + if (headEl) { + headerHeight = (headEl as HTMLElement).offsetHeight + } + + let bottomIncludeBody = 0 + if (unref(wrapRef) && isCanResizeParent) { + const tablePadding = 12 + const formMargin = 16 + let paginationMargin = 10 + const wrapHeight = unref(wrapRef)?.offsetHeight ?? 0 + + let formHeight = unref(formRef)?.$el.offsetHeight ?? 0 + if (formHeight) { + formHeight += formMargin + } + if (isBoolean(pagination) && !pagination) { + paginationMargin = 0 + } + if (isBoolean(useSearchForm) && !useSearchForm) { + paddingHeight = 0 + } + + const headerCellHeight = (tableEl.querySelector('.ant-table-title') as HTMLElement)?.offsetHeight ?? 0 + + console.log(wrapHeight - formHeight - headerCellHeight - tablePadding - paginationMargin) + bottomIncludeBody = wrapHeight - formHeight - headerCellHeight - tablePadding - paginationMargin + } else { + // Table height from bottom + bottomIncludeBody = getViewportOffset(headEl).bottomIncludeBody + } + + let height = bottomIncludeBody - (resizeHeightOffset || 0) - paddingHeight - paginationHeight - footerHeight - headerHeight + height = (height > maxHeight! ? (maxHeight as number) : height) ?? height + setHeight(height) + + bodyEl!.style.height = `${height}px` + } + useWindowSizeFn(calcTableHeight, 280) + onMountedOrActivated(() => { + calcTableHeight() + nextTick(() => { + debounceRedoHeight() + }) + }) + + const getScrollX = computed(() => { + let width = 0 + if (unref(rowSelectionRef)) { + width += 60 + } + + // TODO props ?? 0; + const NORMAL_WIDTH = 150 + + const columns = unref(columnsRef).filter((item) => !item.defaultHidden) + columns.forEach((item) => { + width += Number.parseFloat(item.width as string) || 0 + }) + const unsetWidthColumns = columns.filter((item) => !Reflect.has(item, 'width')) + + const len = unsetWidthColumns.length + if (len !== 0) { + width += len * NORMAL_WIDTH + } + + const table = unref(tableElRef) + const tableWidth = table?.$el?.offsetWidth ?? 0 + return tableWidth > width ? '100%' : width + }) + + const getScrollRef = computed(() => { + const tableHeight = unref(tableHeightRef) + const { canResize, scroll } = unref(propsRef) + return { + x: unref(getScrollX), + y: canResize ? tableHeight : null, + scrollToFirstRowOnChange: false, + ...scroll + } + }) + + return { getScrollRef, redoHeight } +} diff --git a/src/components/Table/src/hooks/useTableStyle.ts b/src/components/Table/src/hooks/useTableStyle.ts new file mode 100644 index 00000000..f090d433 --- /dev/null +++ b/src/components/Table/src/hooks/useTableStyle.ts @@ -0,0 +1,20 @@ +import type { ComputedRef } from 'vue' +import type { BasicTableProps, TableCustomRecord } from '../types/table' +import { unref } from 'vue' +import { isFunction } from '@/utils/is' + +export function useTableStyle(propsRef: ComputedRef, prefixCls: string) { + function getRowClassName(record: TableCustomRecord, index: number) { + const { striped, rowClassName } = unref(propsRef) + const classNames: string[] = [] + if (striped) { + classNames.push((index || 0) % 2 === 1 ? `${prefixCls}-row__striped` : '') + } + if (rowClassName && isFunction(rowClassName)) { + classNames.push(rowClassName(record, index)) + } + return classNames.filter((cls) => !!cls).join(' ') + } + + return { getRowClassName } +} diff --git a/src/components/Table/src/props.ts b/src/components/Table/src/props.ts new file mode 100644 index 00000000..fb1f712a --- /dev/null +++ b/src/components/Table/src/props.ts @@ -0,0 +1,135 @@ +import type { PaginationProps } from './types/pagination' +import type { BasicColumn, FetchSetting, TableSetting, SorterResult, TableCustomRecord, TableRowSelection, SizeType } from './types/table' +import type { FormProps } from '@/components/Form' + +import { DEFAULT_FILTER_FN, DEFAULT_SORT_FN, FETCH_SETTING, DEFAULT_SIZE } from './const' +import { propTypes } from '@/utils/propTypes' + +export const basicProps = { + clickToRowSelect: { type: Boolean, default: true }, + isTreeTable: Boolean, + tableSetting: propTypes.shape({}), + inset: Boolean, + sortFn: { + type: Function as PropType<(sortInfo: SorterResult) => any>, + default: DEFAULT_SORT_FN + }, + filterFn: { + type: Function as PropType<(data: Partial>) => any>, + default: DEFAULT_FILTER_FN + }, + showTableSetting: Boolean, + autoCreateKey: { type: Boolean, default: true }, + striped: { type: Boolean, default: true }, + showSummary: Boolean, + summaryFunc: { + type: [Function, Array] as PropType<(...arg: any[]) => any[]>, + default: null + }, + summaryData: { + type: Array as PropType, + default: null + }, + indentSize: propTypes.number.def(24), + canColDrag: { type: Boolean, default: true }, + api: { + type: Function as PropType<(...arg: any[]) => Promise>, + default: null + }, + beforeFetch: { + type: Function as PropType, + default: null + }, + afterFetch: { + type: Function as PropType, + default: null + }, + handleSearchInfoFn: { + type: Function as PropType, + default: null + }, + fetchSetting: { + type: Object as PropType, + default: () => { + return FETCH_SETTING + } + }, + // 立即请求接口 + immediate: { type: Boolean, default: true }, + emptyDataIsShowTable: { type: Boolean, default: true }, + // 额外的请求参数 + searchInfo: { + type: Object as PropType, + default: null + }, + // 默认的排序参数 + defSort: { + type: Object as PropType, + default: null + }, + // 使用搜索表单 + useSearchForm: propTypes.bool, + // 表单配置 + formConfig: { + type: Object as PropType>, + default: null + }, + columns: { + type: Array as PropType, + default: () => [] + }, + showIndexColumn: { type: Boolean, default: true }, + indexColumnProps: { + type: Object as PropType, + default: null + }, + actionColumn: { + type: Object as PropType, + default: null + }, + ellipsis: { type: Boolean, default: true }, + isCanResizeParent: { type: Boolean, default: false }, + canResize: { type: Boolean, default: true }, + clearSelectOnPageChange: propTypes.bool, + resizeHeightOffset: propTypes.number.def(0), + rowSelection: { + type: Object as PropType, + default: null + }, + title: { + type: [String, Function] as PropType string)>, + default: null + }, + titleHelpMessage: { + type: [String, Array] as PropType + }, + maxHeight: propTypes.number, + dataSource: { + type: Array as PropType, + default: null + }, + rowKey: { + type: [String, Function] as PropType string)>, + default: '' + }, + bordered: propTypes.bool, + pagination: { + type: [Object, Boolean] as PropType, + default: null + }, + loading: propTypes.bool, + rowClassName: { + type: Function as PropType<(record: TableCustomRecord, index: number) => string> + }, + scroll: { + type: Object as PropType<{ x: number | string | true; y: number | string }>, + default: null + }, + beforeEditSubmit: { + type: Function as PropType<(data: { record: Recordable; index: number; key: string | number; value: any }) => Promise> + }, + size: { + type: String as PropType, + default: DEFAULT_SIZE + } +} diff --git a/src/components/Table/src/types/column.ts b/src/components/Table/src/types/column.ts new file mode 100644 index 00000000..337bd10c --- /dev/null +++ b/src/components/Table/src/types/column.ts @@ -0,0 +1,195 @@ +import { VNodeChild } from 'vue' + +export interface ColumnFilterItem { + text?: string + value?: string + children?: any +} + +export declare type SortOrder = 'ascend' | 'descend' + +export interface RecordProps { + text: any + record: T + index: number +} + +export interface FilterDropdownProps { + prefixCls?: string + setSelectedKeys?: (selectedKeys: string[]) => void + selectedKeys?: string[] + confirm?: () => void + clearFilters?: () => void + filters?: ColumnFilterItem[] + getPopupContainer?: (triggerNode: HTMLElement) => HTMLElement + visible?: boolean +} + +export declare type CustomRenderFunction = (record: RecordProps) => VNodeChild | JSX.Element + +export interface ColumnProps { + /** + * specify how content is aligned + * @default 'left' + * @type string + */ + align?: 'left' | 'right' | 'center' + + /** + * ellipsize cell content, not working with sorter and filters for now. + * tableLayout would be fixed when ellipsis is true. + * @default false + * @type boolean + */ + ellipsis?: boolean + + /** + * Span of this column's title + * @type number + */ + colSpan?: number + + /** + * Display field of the data record, could be set like a.b.c + * @type string + */ + dataIndex?: string + + /** + * Default filtered values + * @type string[] + */ + defaultFilteredValue?: string[] + + /** + * Default order of sorted values: 'ascend' 'descend' null + * @type string + */ + defaultSortOrder?: SortOrder + + /** + * Customized filter overlay + * @type any (slot) + */ + filterDropdown?: VNodeChild | JSX.Element | ((props: FilterDropdownProps) => VNodeChild | JSX.Element) + + /** + * Whether filterDropdown is visible + * @type boolean + */ + filterDropdownVisible?: boolean + + /** + * Whether the dataSource is filtered + * @default false + * @type boolean + */ + filtered?: boolean + + /** + * Controlled filtered value, filter icon will highlight + * @type string[] + */ + filteredValue?: string[] + + /** + * Customized filter icon + * @default false + * @type any + */ + filterIcon?: boolean | VNodeChild | JSX.Element + + /** + * Whether multiple filters can be selected + * @default true + * @type boolean + */ + filterMultiple?: boolean + + /** + * Filter menu config + * @type object[] + */ + filters?: ColumnFilterItem[] + + /** + * Set column to be fixed: true(same as left) 'left' 'right' + * @default false + * @type boolean | string + */ + fixed?: boolean | 'left' | 'right' + + /** + * Unique key of this column, you can ignore this prop if you've set a unique dataIndex + * @type string + */ + key?: string + + /** + * Renderer of the table cell. The return value should be a VNode, or an object for colSpan/rowSpan config + * @type Function | ScopedSlot + */ + customRender?: CustomRenderFunction | VNodeChild | JSX.Element + + /** + * Sort function for local sort, see Array.sort's compareFunction. If you need sort buttons only, set to true + * @type boolean | Function + */ + sorter?: boolean | Function + + /** + * Order of sorted values: 'ascend' 'descend' false + * @type boolean | string + */ + sortOrder?: boolean | SortOrder + + /** + * supported sort way, could be 'ascend', 'descend' + * @default ['ascend', 'descend'] + * @type string[] + */ + sortDirections?: SortOrder[] + + /** + * Title of this column + * @type any (string | slot) + */ + title?: VNodeChild | JSX.Element + + /** + * Width of this column + * @type string | number + */ + width?: string | number + + /** + * Set props on per cell + * @type Function + */ + customCell?: (record: T, rowIndex: number) => object + + /** + * Set props on per header cell + * @type object + */ + customHeaderCell?: (column: ColumnProps) => object + + /** + * Callback executed when the confirm filter button is clicked, Use as a filter event when using template or jsx + * @type Function + */ + onFilter?: (value: any, record: T) => boolean + + /** + * Callback executed when filterDropdownVisible is changed, Use as a filterDropdownVisible event when using template or jsx + * @type Function + */ + onFilterDropdownVisibleChange?: (visible: boolean) => void + + /** + * When using columns, you can setting this property to configure the properties that support the slot, + * such as slots: { filterIcon: 'XXX'} + * @type object + */ + slots?: Recordable +} diff --git a/src/components/Table/src/types/componentType.ts b/src/components/Table/src/types/componentType.ts new file mode 100644 index 00000000..293f07c3 --- /dev/null +++ b/src/components/Table/src/types/componentType.ts @@ -0,0 +1,14 @@ +export type ComponentType = + | 'Input' + | 'InputNumber' + | 'Select' + | 'ApiSelect' + | 'AutoComplete' + | 'ApiTreeSelect' + | 'Checkbox' + | 'Switch' + | 'DatePicker' + | 'TimePicker' + | 'RadioGroup' + | 'RadioButtonGroup' + | 'ApiRadioGroup' diff --git a/src/components/Table/src/types/pagination.ts b/src/components/Table/src/types/pagination.ts new file mode 100644 index 00000000..97ae9be0 --- /dev/null +++ b/src/components/Table/src/types/pagination.ts @@ -0,0 +1,109 @@ +import Pagination from 'ant-design-vue/lib/pagination' +import { VNodeChild } from 'vue' + +interface PaginationRenderProps { + page: number + type: 'page' | 'prev' | 'next' + originalElement: any +} + +type PaginationPositon = 'topLeft' | 'topCenter' | 'topRight' | 'bottomLeft' | 'bottomCenter' | 'bottomRight' + +export declare class PaginationConfig extends Pagination { + position?: PaginationPositon[] +} + +export interface PaginationProps { + /** + * total number of data items + * @default 0 + * @type number + */ + total?: number + + /** + * default initial page number + * @default 1 + * @type number + */ + defaultCurrent?: number + + /** + * current page number + * @type number + */ + current?: number + + /** + * default number of data items per page + * @default 10 + * @type number + */ + defaultPageSize?: number + + /** + * number of data items per page + * @type number + */ + pageSize?: number + + /** + * Whether to hide pager on single page + * @default false + * @type boolean + */ + hideOnSinglePage?: boolean + + /** + * determine whether pageSize can be changed + * @default false + * @type boolean + */ + showSizeChanger?: boolean + + /** + * specify the sizeChanger options + * @default ['10', '20', '30', '40'] + * @type string[] + */ + pageSizeOptions?: string[] + + /** + * determine whether you can jump to pages directly + * @default false + * @type boolean + */ + showQuickJumper?: boolean | object + + /** + * to display the total number and range + * @type Function + */ + showTotal?: (total: number, range: [number, number]) => any + + /** + * specify the size of Pagination, can be set to small + * @default '' + * @type string + */ + size?: string + + /** + * whether to setting simple mode + * @type boolean + */ + simple?: boolean + + /** + * to customize item innerHTML + * @type Function + */ + itemRender?: (props: PaginationRenderProps) => VNodeChild | JSX.Element + + /** + * specify the position of Pagination + * @default ['bottomRight'] + * @type string[] + */ + position?: PaginationPositon[] +} diff --git a/src/components/Table/src/types/table.ts b/src/components/Table/src/types/table.ts new file mode 100644 index 00000000..bcc380d6 --- /dev/null +++ b/src/components/Table/src/types/table.ts @@ -0,0 +1,464 @@ +import type { VNodeChild } from 'vue' +import type { PaginationProps } from './pagination' +import type { FormProps } from '@/components/Form' +import type { TableRowSelection as ITableRowSelection } from 'ant-design-vue/lib/table/interface' +import type { ColumnProps } from 'ant-design-vue/lib/table' + +import { ComponentType } from './componentType' +import { VueNode } from '@/utils/propTypes' +import { RoleEnum } from '@/enums/roleEnum' + +export declare type SortOrder = 'ascend' | 'descend' + +export interface TableCurrentDataSource { + currentDataSource: T[] +} + +export interface TableRowSelection extends ITableRowSelection { + /** + * Callback executed when selected rows change + * @type Function + */ + onChange?: (selectedRowKeys: string[] | number[], selectedRows: T[]) => any + + /** + * Callback executed when select/deselect one row + * @type Function + */ + onSelect?: (record: T, selected: boolean, selectedRows: Object[], nativeEvent: Event) => any + + /** + * Callback executed when select/deselect all rows + * @type Function + */ + onSelectAll?: (selected: boolean, selectedRows: T[], changeRows: T[]) => any + + /** + * Callback executed when row selection is inverted + * @type Function + */ + onSelectInvert?: (selectedRows: string[] | number[]) => any +} + +export interface TableCustomRecord { + record?: T + index?: number +} + +export interface ExpandedRowRenderRecord extends TableCustomRecord { + indent?: number + expanded?: boolean +} +export interface ColumnFilterItem { + text?: string + value?: string + children?: any +} + +export interface TableCustomRecord { + record?: T + index?: number +} + +export interface SorterResult { + column: ColumnProps + order: SortOrder + field: string + columnKey: string +} + +export interface FetchParams { + searchInfo?: Recordable + page?: number + sortInfo?: Recordable + filterInfo?: Recordable +} + +export interface GetColumnsParams { + ignoreIndex?: boolean + ignoreAction?: boolean + sort?: boolean +} + +export type SizeType = 'default' | 'middle' | 'small' | 'large' + +export interface TableActionType { + reload: (opt?: FetchParams) => Promise + setSelectedRows: (rows: Recordable[]) => void + getSelectRows: () => T[] + clearSelectedRowKeys: () => void + expandAll: () => void + expandRows: (keys: string[] | number[]) => void + collapseAll: () => void + scrollTo: (pos: string) => void // pos: id | "top" | "bottom" + getSelectRowKeys: () => string[] + deleteSelectRowByKey: (key: string) => void + setPagination: (info: Partial) => void + setTableData: (values: T[]) => void + updateTableDataRecord: (rowKey: string | number, record: Recordable) => Recordable | void + deleteTableDataRecord: (rowKey: string | number | string[] | number[]) => void + insertTableDataRecord: (record: Recordable | Recordable[], index?: number) => Recordable[] | void + findTableDataRecord: (rowKey: string | number) => Recordable | void + getColumns: (opt?: GetColumnsParams) => BasicColumn[] + setColumns: (columns: BasicColumn[] | string[]) => void + getDataSource: () => T[] + getRawDataSource: () => T + setLoading: (loading: boolean) => void + setProps: (props: Partial) => void + redoHeight: () => void + setSelectedRowKeys: (rowKeys: string[] | number[]) => void + getPaginationRef: () => PaginationProps | boolean + getSize: () => SizeType + getRowSelection: () => TableRowSelection + getCacheColumns: () => BasicColumn[] + emit?: EmitType + updateTableData: (index: number, key: string, value: any) => Recordable + setShowPagination: (show: boolean) => Promise + getShowPagination: () => boolean + setCacheColumnsByField?: (dataIndex: string | undefined, value: BasicColumn) => void +} + +export interface FetchSetting { + // 请求接口当前页数 + pageField: string + // 每页显示多少条 + sizeField: string + // 请求结果列表字段 支持 a.b.c + listField: string + // 请求结果总数字段 支持 a.b.c + totalField: string +} + +export interface TableSetting { + redo?: boolean + size?: boolean + setting?: boolean + fullScreen?: boolean +} + +export interface BasicTableProps { + // 点击行选中 + clickToRowSelect?: boolean + isTreeTable?: boolean + // 自定义排序方法 + sortFn?: (sortInfo: SorterResult) => any + // 排序方法 + filterFn?: (data: Partial>) => any + // 取消表格的默认padding + inset?: boolean + // 显示表格设置 + showTableSetting?: boolean + tableSetting?: TableSetting + // 斑马纹 + striped?: boolean + // 是否自动生成key + autoCreateKey?: boolean + // 计算合计行的方法 + summaryFunc?: (...arg: any) => Recordable[] + // 自定义合计表格内容 + summaryData?: Recordable[] + // 是否显示合计行 + showSummary?: boolean + // 是否可拖拽列 + canColDrag?: boolean + // 接口请求对象 + api?: (...arg: any) => Promise + // 请求之前处理参数 + beforeFetch?: Fn + // 自定义处理接口返回参数 + afterFetch?: Fn + // 查询条件请求之前处理 + handleSearchInfoFn?: Fn + // 请求接口配置 + fetchSetting?: Partial + // 立即请求接口 + immediate?: boolean + // 在开起搜索表单的时候,如果没有数据是否显示表格 + emptyDataIsShowTable?: boolean + // 额外的请求参数 + searchInfo?: Recordable + // 默认的排序参数 + defSort?: Recordable + // 使用搜索表单 + useSearchForm?: boolean + // 表单配置 + formConfig?: Partial + // 列配置 + columns: BasicColumn[] + // 是否显示序号列 + showIndexColumn?: boolean + // 序号列配置 + indexColumnProps?: BasicColumn + actionColumn?: BasicColumn + // 文本超过宽度是否显示。。。 + ellipsis?: boolean + // 是否继承父级高度(父级高度-表单高度-padding高度) + isCanResizeParent?: boolean + // 是否可以自适应高度 + canResize?: boolean + // 自适应高度偏移, 计算结果-偏移量 + resizeHeightOffset?: number + + // 在分页改变的时候清空选项 + clearSelectOnPageChange?: boolean + // + rowKey?: string | ((record: Recordable) => string) + // 数据 + dataSource?: Recordable[] + // 标题右侧提示 + titleHelpMessage?: string | string[] + // 表格滚动最大高度 + maxHeight?: number + // 是否显示边框 + bordered?: boolean + // 分页配置 + pagination?: PaginationProps | boolean + // loading加载 + loading?: boolean + + /** + * The column contains children to display + * @default 'children' + * @type string | string[] + */ + childrenColumnName?: string + + /** + * Override default table elements + * @type object + */ + components?: object + + /** + * Expand all rows initially + * @default false + * @type boolean + */ + defaultExpandAllRows?: boolean + + /** + * Initial expanded row keys + * @type string[] + */ + defaultExpandedRowKeys?: string[] + + /** + * Current expanded row keys + * @type string[] + */ + expandedRowKeys?: string[] + + /** + * Expanded container render for each row + * @type Function + */ + expandedRowRender?: (record?: ExpandedRowRenderRecord) => VNodeChild | JSX.Element + + /** + * Customize row expand Icon. + * @type Function | VNodeChild + */ + expandIcon?: Function | VNodeChild | JSX.Element + + /** + * Whether to expand row by clicking anywhere in the whole row + * @default false + * @type boolean + */ + expandRowByClick?: boolean + + /** + * The index of `expandIcon` which column will be inserted when `expandIconAsCell` is false. default 0 + */ + expandIconColumnIndex?: number + + /** + * Table footer renderer + * @type Function | VNodeChild + */ + footer?: Function | VNodeChild | JSX.Element + + /** + * Indent size in pixels of tree data + * @default 15 + * @type number + */ + indentSize?: number + + /** + * i18n text including filter, sort, empty text, etc + * @default { filterConfirm: 'Ok', filterReset: 'Reset', emptyText: 'No Data' } + * @type object + */ + locale?: object + + /** + * Row's className + * @type Function + */ + rowClassName?: (record: TableCustomRecord, index: number) => string + + /** + * Row selection config + * @type object + */ + rowSelection?: TableRowSelection + + /** + * Set horizontal or vertical scrolling, can also be used to specify the width and height of the scroll area. + * It is recommended to set a number for x, if you want to set it to true, + * you need to add style .ant-table td { white-space: nowrap; }. + * @type object + */ + scroll?: { x?: number | string | true; y?: number | string } + + /** + * Whether to show table header + * @default true + * @type boolean + */ + showHeader?: boolean + + /** + * Size of table + * @default 'default' + * @type string + */ + size?: SizeType + + /** + * Table title renderer + * @type Function | ScopedSlot + */ + title?: VNodeChild | JSX.Element | string | ((data: Recordable) => string) + + /** + * Set props on per header row + * @type Function + */ + customHeaderRow?: (column: ColumnProps, index: number) => object + + /** + * Set props on per row + * @type Function + */ + customRow?: (record: T, index: number) => object + + /** + * `table-layout` attribute of table element + * `fixed` when header/columns are fixed, or using `column.ellipsis` + * + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout + * @version 1.5.0 + */ + tableLayout?: 'auto' | 'fixed' | string + + /** + * the render container of dropdowns in table + * @param triggerNode + * @version 1.5.0 + */ + getPopupContainer?: (triggerNode?: HTMLElement) => HTMLElement + + /** + * Data can be changed again before rendering. + * The default configuration of general user empty data. + * You can configured globally through [ConfigProvider](https://antdv.com/components/config-provider-cn/) + * + * @version 1.5.4 + */ + transformCellText?: Function + + /** + * Callback executed before editable cell submit value, not for row-editor + * + * The cell will not submit data while callback return false + */ + beforeEditSubmit?: (data: { record: Recordable; index: number; key: string | number; value: any }) => Promise + + /** + * Callback executed when pagination, filters or sorter is changed + * @param pagination + * @param filters + * @param sorter + * @param currentDataSource + */ + onChange?: (pagination: any, filters: any, sorter: any, extra: any) => void + + /** + * Callback executed when the row expand icon is clicked + * + * @param expanded + * @param record + */ + onExpand?: (expande: boolean, record: T) => void + + /** + * Callback executed when the expanded rows change + * @param expandedRows + */ + onExpandedRowsChange?: (expandedRows: string[] | number[]) => void + + onColumnsChange?: (data: ColumnChangeParam[]) => void +} + +export type CellFormat = string | ((text: string, record: Recordable, index: number) => string | number) | Map + +// @ts-ignore +export interface BasicColumn extends ColumnProps { + children?: BasicColumn[] + filters?: { + text: string + value: string + children?: unknown[] | (((props: Record) => unknown[]) & (() => unknown[]) & (() => unknown[])) + }[] + + // + flag?: 'INDEX' | 'DEFAULT' | 'CHECKBOX' | 'RADIO' | 'ACTION' + customTitle?: VueNode + + slots?: Recordable + + // Whether to hide the column by default, it can be displayed in the column configuration + defaultHidden?: boolean + + // Help text for table column header + helpMessage?: string | string[] + + format?: CellFormat + + // Editable + edit?: boolean + editRow?: boolean + editable?: boolean + editComponent?: ComponentType + editComponentProps?: + | ((opt: { text: string | number | boolean | Recordable; record: Recordable; column: BasicColumn; index: number }) => Recordable) + | Recordable + editRule?: boolean | ((text: string, record: Recordable) => Promise) + editValueMap?: (value: any) => string + onEditRow?: () => void + // 权限编码控制是否显示 + auth?: RoleEnum | RoleEnum[] | string | string[] + // 业务控制是否显示 + ifShow?: boolean | ((column: BasicColumn) => boolean) + // 自定义修改后显示的内容 + editRender?: (opt: { + text: string | number | boolean | Recordable + record: Recordable + column: BasicColumn + index: number + }) => VNodeChild | JSX.Element + // 动态 Disabled + editDynamicDisabled?: boolean | ((record: Recordable) => boolean) +} + +export type ColumnChangeParam = { + dataIndex: string + fixed: boolean | 'left' | 'right' | undefined + visible: boolean +} + +export interface InnerHandlers { + onColumnsChange: (data: ColumnChangeParam[]) => void +} diff --git a/src/components/Table/src/types/tableAction.ts b/src/components/Table/src/types/tableAction.ts new file mode 100644 index 00000000..77b98078 --- /dev/null +++ b/src/components/Table/src/types/tableAction.ts @@ -0,0 +1,39 @@ +import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes' +import { TooltipProps } from 'ant-design-vue/es/tooltip/Tooltip' +import { RoleEnum } from '@/enums/roleEnum' +export interface ActionItem extends ButtonProps { + onClick?: Fn + label?: string + color?: 'success' | 'error' | 'warning' + icon?: string + popConfirm?: PopConfirm + disabled?: boolean + divider?: boolean + // 权限编码控制是否显示 + auth?: RoleEnum | RoleEnum[] | string | string[] + // 业务控制是否显示 + ifShow?: boolean | ((action: ActionItem) => boolean) + tooltip?: string | TooltipProps +} + +export interface PopConfirm { + title: string + okText?: string + cancelText?: string + confirm: Fn + cancel?: Fn + icon?: string + placement?: + | 'top' + | 'left' + | 'right' + | 'bottom' + | 'topLeft' + | 'topRight' + | 'leftTop' + | 'leftBottom' + | 'rightTop' + | 'rightBottom' + | 'bottomLeft' + | 'bottomRight' +} diff --git a/src/components/Tinymce/index.ts b/src/components/Tinymce/index.ts new file mode 100644 index 00000000..b4ca8569 --- /dev/null +++ b/src/components/Tinymce/index.ts @@ -0,0 +1,4 @@ +import { withInstall } from '@/utils' +import tinymce from './src/Editor.vue' + +export const Tinymce = withInstall(tinymce) diff --git a/src/components/Tinymce/src/Editor.vue b/src/components/Tinymce/src/Editor.vue new file mode 100644 index 00000000..71aff26c --- /dev/null +++ b/src/components/Tinymce/src/Editor.vue @@ -0,0 +1,302 @@ + + + + + + + diff --git a/src/components/Tinymce/src/ImgUpload.vue b/src/components/Tinymce/src/ImgUpload.vue new file mode 100644 index 00000000..bd50a920 --- /dev/null +++ b/src/components/Tinymce/src/ImgUpload.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/components/Tinymce/src/helper.ts b/src/components/Tinymce/src/helper.ts new file mode 100644 index 00000000..363d39d7 --- /dev/null +++ b/src/components/Tinymce/src/helper.ts @@ -0,0 +1,81 @@ +const validEvents = [ + 'onActivate', + 'onAddUndo', + 'onBeforeAddUndo', + 'onBeforeExecCommand', + 'onBeforeGetContent', + 'onBeforeRenderUI', + 'onBeforeSetContent', + 'onBeforePaste', + 'onBlur', + 'onChange', + 'onClearUndos', + 'onClick', + 'onContextMenu', + 'onCopy', + 'onCut', + 'onDblclick', + 'onDeactivate', + 'onDirty', + 'onDrag', + 'onDragDrop', + 'onDragEnd', + 'onDragGesture', + 'onDragOver', + 'onDrop', + 'onExecCommand', + 'onFocus', + 'onFocusIn', + 'onFocusOut', + 'onGetContent', + 'onHide', + 'onInit', + 'onKeyDown', + 'onKeyPress', + 'onKeyUp', + 'onLoadContent', + 'onMouseDown', + 'onMouseEnter', + 'onMouseLeave', + 'onMouseMove', + 'onMouseOut', + 'onMouseOver', + 'onMouseUp', + 'onNodeChange', + 'onObjectResizeStart', + 'onObjectResized', + 'onObjectSelected', + 'onPaste', + 'onPostProcess', + 'onPostRender', + 'onPreProcess', + 'onProgressState', + 'onRedo', + 'onRemove', + 'onReset', + 'onSaveContent', + 'onSelectionChange', + 'onSetAttrib', + 'onSetContent', + 'onShow', + 'onSubmit', + 'onUndo', + 'onVisualAid' +] + +const isValidKey = (key: string) => validEvents.indexOf(key) !== -1 + +export const bindHandlers = (initEvent: Event, listeners: any, editor: any): void => { + Object.keys(listeners) + .filter(isValidKey) + .forEach((key: string) => { + const handler = listeners[key] + if (typeof handler === 'function') { + if (key === 'onInit') { + handler(initEvent, editor) + } else { + editor.on(key.substring(2), (e: any) => handler(e, editor)) + } + } + }) +} diff --git a/src/components/Tinymce/src/tinymce.ts b/src/components/Tinymce/src/tinymce.ts new file mode 100644 index 00000000..0f3dec02 --- /dev/null +++ b/src/components/Tinymce/src/tinymce.ts @@ -0,0 +1,13 @@ +// Any plugins you want to setting has to be imported +// Detail plugins list see https://www.tinymce.com/docs/plugins/ +// Custom builds see https://www.tinymce.com/download/custom-builds/ +// colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration + +export const plugins = [ + 'advlist anchor autolink autosave code codesample directionality fullscreen hr insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus template textpattern visualblocks visualchars wordcount' +] + +export const toolbar = [ + 'fontsizeselect lineheight searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', + 'hr bullist numlist link preview anchor pagebreak insertdatetime media forecolor backcolor fullscreen' +] diff --git a/src/components/Transition/index.ts b/src/components/Transition/index.ts new file mode 100644 index 00000000..dd0c4b81 --- /dev/null +++ b/src/components/Transition/index.ts @@ -0,0 +1,21 @@ +import { createSimpleTransition, createJavascriptTransition } from './src/CreateTransition' + +import ExpandTransitionGenerator from './src/ExpandTransition' + +export { default as CollapseTransition } from './src/CollapseTransition.vue' + +export const FadeTransition = createSimpleTransition('fade-transition') +export const ScaleTransition = createSimpleTransition('scale-transition') +export const SlideYTransition = createSimpleTransition('slide-y-transition') +export const ScrollYTransition = createSimpleTransition('scroll-y-transition') +export const SlideYReverseTransition = createSimpleTransition('slide-y-reverse-transition') +export const ScrollYReverseTransition = createSimpleTransition('scroll-y-reverse-transition') +export const SlideXTransition = createSimpleTransition('slide-x-transition') +export const ScrollXTransition = createSimpleTransition('scroll-x-transition') +export const SlideXReverseTransition = createSimpleTransition('slide-x-reverse-transition') +export const ScrollXReverseTransition = createSimpleTransition('scroll-x-reverse-transition') +export const ScaleRotateTransition = createSimpleTransition('scale-rotate-transition') + +export const ExpandXTransition = createJavascriptTransition('expand-x-transition', ExpandTransitionGenerator('', true)) + +export const ExpandTransition = createJavascriptTransition('expand-transition', ExpandTransitionGenerator('')) diff --git a/src/components/Transition/src/CollapseTransition.vue b/src/components/Transition/src/CollapseTransition.vue new file mode 100644 index 00000000..afc0eb14 --- /dev/null +++ b/src/components/Transition/src/CollapseTransition.vue @@ -0,0 +1,69 @@ + + diff --git a/src/components/Transition/src/CreateTransition.tsx b/src/components/Transition/src/CreateTransition.tsx new file mode 100644 index 00000000..e7c66495 --- /dev/null +++ b/src/components/Transition/src/CreateTransition.tsx @@ -0,0 +1,67 @@ +import { defineComponent, Transition, TransitionGroup } from 'vue' +import { getSlot } from '@/utils/helper/tsxHelper' + +type Mode = 'in-out' | 'out-in' | 'default' | undefined + +export function createSimpleTransition(name: string, origin = 'top center 0', mode?: Mode) { + return defineComponent({ + name, + props: { + group: { + type: Boolean as PropType, + default: false + }, + mode: { + type: String as PropType, + default: mode + }, + origin: { + type: String as PropType, + default: origin + } + }, + setup(props, { slots, attrs }) { + const onBeforeEnter = (el: HTMLElement) => { + el.style.transformOrigin = props.origin + } + + return () => { + const Tag = !props.group ? Transition : TransitionGroup + return ( + + {() => getSlot(slots)} + + ) + } + } + }) +} +export function createJavascriptTransition(name: string, functions: Recordable, mode: Mode = 'in-out') { + return defineComponent({ + name, + props: { + mode: { + type: String as PropType, + default: mode + } + }, + setup(props, { attrs, slots }) { + return () => { + return ( + + {() => getSlot(slots)} + + ) + } + } + }) +} diff --git a/src/components/Transition/src/ExpandTransition.ts b/src/components/Transition/src/ExpandTransition.ts new file mode 100644 index 00000000..a9c1112d --- /dev/null +++ b/src/components/Transition/src/ExpandTransition.ts @@ -0,0 +1,89 @@ +/** + * Makes the first character of a string uppercase + */ +export function upperFirst(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1) +} + +interface HTMLExpandElement extends HTMLElement { + _parent?: (Node & ParentNode & HTMLElement) | null + _initialStyle: { + transition: string + overflow: string | null + height?: string | null + width?: string | null + } +} + +export default function (expandedParentClass = '', x = false) { + const sizeProperty = x ? 'width' : ('height' as 'width' | 'height') + const offsetProperty = `offset${upperFirst(sizeProperty)}` as 'offsetHeight' | 'offsetWidth' + + return { + beforeEnter(el: HTMLExpandElement) { + el._parent = el.parentNode as (Node & ParentNode & HTMLElement) | null + el._initialStyle = { + transition: el.style.transition, + overflow: el.style.overflow, + [sizeProperty]: el.style[sizeProperty] + } + }, + + enter(el: HTMLExpandElement) { + const initialStyle = el._initialStyle + + el.style.setProperty('transition', 'none', 'important') + el.style.overflow = 'hidden' + // const offset = `${el[offsetProperty]}px`; + + // el.style[sizeProperty] = '0'; + + void el.offsetHeight // force reflow + + el.style.transition = initialStyle.transition + + if (expandedParentClass && el._parent) { + el._parent.classList.add(expandedParentClass) + } + + requestAnimationFrame(() => { + // el.style[sizeProperty] = offset; + }) + }, + + afterEnter: resetStyles, + enterCancelled: resetStyles, + + leave(el: HTMLExpandElement) { + el._initialStyle = { + transition: '', + overflow: el.style.overflow, + [sizeProperty]: el.style[sizeProperty] + } + + el.style.overflow = 'hidden' + el.style[sizeProperty] = `${el[offsetProperty]}px` + /* eslint-disable-next-line */ + void el.offsetHeight // force reflow + + requestAnimationFrame(() => (el.style[sizeProperty] = '0')) + }, + + afterLeave, + leaveCancelled: afterLeave + } + + function afterLeave(el: HTMLExpandElement) { + if (expandedParentClass && el._parent) { + el._parent.classList.remove(expandedParentClass) + } + resetStyles(el) + } + + function resetStyles(el: HTMLExpandElement) { + const size = el._initialStyle[sizeProperty] + el.style.overflow = el._initialStyle.overflow! + if (size != null) el.style[sizeProperty] = size + Reflect.deleteProperty(el, '_initialStyle') + } +} diff --git a/src/components/Tree/index.ts b/src/components/Tree/index.ts new file mode 100644 index 00000000..5abf924d --- /dev/null +++ b/src/components/Tree/index.ts @@ -0,0 +1,6 @@ +import BasicTree from './src/BasicTree.vue' +import './style' + +export { BasicTree } +export type { ContextMenuItem } from '@/hooks/web/useContextMenu' +export * from './src/types/tree' diff --git a/src/components/Tree/src/BasicTree.vue b/src/components/Tree/src/BasicTree.vue new file mode 100644 index 00000000..a291209c --- /dev/null +++ b/src/components/Tree/src/BasicTree.vue @@ -0,0 +1,421 @@ + diff --git a/src/components/Tree/src/TreeIcon.ts b/src/components/Tree/src/TreeIcon.ts new file mode 100644 index 00000000..0c6b57b8 --- /dev/null +++ b/src/components/Tree/src/TreeIcon.ts @@ -0,0 +1,13 @@ +import type { VNode, FunctionalComponent } from 'vue' + +import { h } from 'vue' +import { isString } from '@/utils/is' +import { Icon } from '@/components/Icon' + +export const TreeIcon: FunctionalComponent = ({ icon }: { icon: VNode | string }) => { + if (!icon) return null + if (isString(icon)) { + return h(Icon, { icon, class: 'mr-1' }) + } + return Icon +} diff --git a/src/components/Tree/src/components/TreeHeader.vue b/src/components/Tree/src/components/TreeHeader.vue new file mode 100644 index 00000000..b7fb6abc --- /dev/null +++ b/src/components/Tree/src/components/TreeHeader.vue @@ -0,0 +1,162 @@ + + diff --git a/src/components/Tree/src/hooks/useTree.ts b/src/components/Tree/src/hooks/useTree.ts new file mode 100644 index 00000000..bf00a432 --- /dev/null +++ b/src/components/Tree/src/hooks/useTree.ts @@ -0,0 +1,209 @@ +import type { InsertNodeParams, KeyType, FieldNames, TreeItem } from '../types/tree' +import type { Ref, ComputedRef } from 'vue' +import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree' + +import { cloneDeep } from 'lodash-es' +import { unref } from 'vue' +import { forEach } from '@/utils/helper/treeHelper' + +export function useTree(treeDataRef: Ref, getFieldNames: ComputedRef) { + function getAllKeys(list?: TreeDataItem[]) { + const keys: string[] = [] + const treeData = list || unref(treeDataRef) + const { key: keyField, children: childrenField } = unref(getFieldNames) + if (!childrenField || !keyField) return keys + + for (let index = 0; index < treeData.length; index++) { + const node = treeData[index] + keys.push(node[keyField]!) + const children = node[childrenField] + if (children && children.length) { + keys.push(...(getAllKeys(children) as string[])) + } + } + return keys as KeyType[] + } + + // get keys that can be checked and selected + function getEnabledKeys(list?: TreeDataItem[]) { + const keys: string[] = [] + const treeData = list || unref(treeDataRef) + const { key: keyField, children: childrenField } = unref(getFieldNames) + if (!childrenField || !keyField) return keys + + for (let index = 0; index < treeData.length; index++) { + const node = treeData[index] + node.disabled !== true && node.selectable !== false && keys.push(node[keyField]!) + const children = node[childrenField] + if (children && children.length) { + keys.push(...(getEnabledKeys(children) as string[])) + } + } + return keys as KeyType[] + } + + function getChildrenKeys(nodeKey: string | number, list?: TreeDataItem[]) { + const keys: KeyType[] = [] + const treeData = list || unref(treeDataRef) + const { key: keyField, children: childrenField } = unref(getFieldNames) + if (!childrenField || !keyField) return keys + for (let index = 0; index < treeData.length; index++) { + const node = treeData[index] + const children = node[childrenField] + if (nodeKey === node[keyField]) { + keys.push(node[keyField]!) + if (children && children.length) { + keys.push(...(getAllKeys(children) as string[])) + } + } else { + if (children && children.length) { + keys.push(...getChildrenKeys(nodeKey, children)) + } + } + } + return keys as KeyType[] + } + + // Update node + function updateNodeByKey(key: string, node: TreeDataItem, list?: TreeDataItem[]) { + if (!key) return + const treeData = list || unref(treeDataRef) + const { key: keyField, children: childrenField } = unref(getFieldNames) + + if (!childrenField || !keyField) return + + for (let index = 0; index < treeData.length; index++) { + const element: any = treeData[index] + const children = element[childrenField] + + if (element[keyField] === key) { + treeData[index] = { ...treeData[index], ...node } + break + } else if (children && children.length) { + updateNodeByKey(key, node, element[childrenField]) + } + } + } + + // Expand the specified level + function filterByLevel(level = 1, list?: TreeDataItem[], currentLevel = 1) { + if (!level) { + return [] + } + const res: (string | number)[] = [] + const data = list || unref(treeDataRef) || [] + for (let index = 0; index < data.length; index++) { + const item = data[index] + + const { key: keyField, children: childrenField } = unref(getFieldNames) + const key = keyField ? item[keyField] : '' + const children = childrenField ? item[childrenField] : [] + res.push(key) + if (children && children.length && currentLevel < level) { + currentLevel += 1 + res.push(...filterByLevel(level, children, currentLevel)) + } + } + return res as string[] | number[] + } + + /** + * 添加节点 + */ + function insertNodeByKey({ parentKey = null, node, push = 'push' }: InsertNodeParams) { + const treeData: any = cloneDeep(unref(treeDataRef)) + if (!parentKey) { + treeData[push](node) + treeDataRef.value = treeData + return + } + const { key: keyField, children: childrenField } = unref(getFieldNames) + if (!childrenField || !keyField) return + + forEach(treeData, (treeItem) => { + if (treeItem[keyField] === parentKey) { + treeItem[childrenField] = treeItem[childrenField] || [] + treeItem[childrenField][push](node) + return true + } + }) + treeDataRef.value = treeData + } + /** + * 批量添加节点 + */ + function insertNodesByKey({ parentKey = null, list, push = 'push' }: InsertNodeParams) { + const treeData: any = cloneDeep(unref(treeDataRef)) + if (!list || list.length < 1) { + return + } + if (!parentKey) { + for (let i = 0; i < list.length; i++) { + treeData[push](list[i]) + } + } else { + const { key: keyField, children: childrenField } = unref(getFieldNames) + if (!childrenField || !keyField) return + + forEach(treeData, (treeItem) => { + if (treeItem[keyField] === parentKey) { + treeItem[childrenField] = treeItem[childrenField] || [] + for (let i = 0; i < list.length; i++) { + treeItem[childrenField][push](list[i]) + } + treeDataRef.value = treeData + return true + } + }) + } + } + // Delete node + function deleteNodeByKey(key: string, list?: TreeDataItem[]) { + if (!key) return + const treeData = list || unref(treeDataRef) + const { key: keyField, children: childrenField } = unref(getFieldNames) + if (!childrenField || !keyField) return + + for (let index = 0; index < treeData.length; index++) { + const element: any = treeData[index] + const children = element[childrenField] + + if (element[keyField] === key) { + treeData.splice(index, 1) + break + } else if (children && children.length) { + deleteNodeByKey(key, element[childrenField]) + } + } + } + + // Get selected node + function getSelectedNode(key: KeyType, list?: TreeItem[], selectedNode?: TreeItem | null) { + if (!key && key !== 0) return null + const treeData = list || unref(treeDataRef) + const { key: keyField, children: childrenField } = unref(getFieldNames) + if (!keyField) return + treeData.forEach((item) => { + if (selectedNode?.key || selectedNode?.key === 0) return selectedNode + if (item[keyField] === key) { + selectedNode = item + return + } + if (item[childrenField!] && item[childrenField!].length) { + selectedNode = getSelectedNode(key, item[childrenField!], selectedNode) + } + }) + return selectedNode || null + } + return { + deleteNodeByKey, + insertNodeByKey, + insertNodesByKey, + filterByLevel, + updateNodeByKey, + getAllKeys, + getChildrenKeys, + getEnabledKeys, + getSelectedNode + } +} diff --git a/src/components/Tree/src/types/tree.ts b/src/components/Tree/src/types/tree.ts new file mode 100644 index 00000000..6513dffe --- /dev/null +++ b/src/components/Tree/src/types/tree.ts @@ -0,0 +1,180 @@ +import type { ExtractPropTypes } from 'vue' +import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree' + +import { buildProps } from '@/utils/props' + +export enum ToolbarEnum { + SELECT_ALL, + UN_SELECT_ALL, + EXPAND_ALL, + UN_EXPAND_ALL, + CHECK_STRICTLY, + CHECK_UN_STRICTLY +} + +export const treeEmits = ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'change', 'check', 'update:searchValue'] + +export interface TreeState { + expandedKeys: KeyType[] + selectedKeys: KeyType[] + checkedKeys: CheckKeys + checkStrictly: boolean +} + +export interface FieldNames { + children?: string + title?: string + key?: string +} + +export type KeyType = string | number + +export type CheckKeys = KeyType[] | { checked: string[] | number[]; halfChecked: string[] | number[] } + +export const treeProps = buildProps({ + value: { + type: [Object, Array] as PropType + }, + + renderIcon: { + type: Function as PropType<(params: Recordable) => string> + }, + + helpMessage: { + type: [String, Array] as PropType, + default: '' + }, + + title: { + type: String, + default: '' + }, + toolbar: Boolean, + search: Boolean, + searchValue: { + type: String, + default: '' + }, + checkStrictly: Boolean, + clickRowToExpand: { + type: Boolean, + default: false + }, + checkable: Boolean, + defaultExpandLevel: { + type: [String, Number] as PropType, + default: '' + }, + defaultExpandAll: Boolean, + + fieldNames: { + type: Object as PropType + }, + + treeData: { + type: Array as PropType + }, + + actionList: { + type: Array as PropType, + default: () => [] + }, + + expandedKeys: { + type: Array as PropType, + default: () => [] + }, + + selectedKeys: { + type: Array as PropType, + default: () => [] + }, + + checkedKeys: { + type: [Array, Object] as PropType, + default: () => [] + }, + + beforeRightClick: { + type: Function as PropType<(...arg: any) => ContextMenuItem[] | ContextMenuOptions>, + default: undefined + }, + + rightMenuList: { + type: Array as PropType + }, + // 自定义数据过滤判断方法(注: 不是整个过滤方法,而是内置过滤的判断方法,用于增强原本仅能通过title进行过滤的方式) + filterFn: { + type: Function as PropType<(searchValue: any, node: TreeItem, fieldNames: FieldNames) => boolean>, + default: undefined + }, + // 高亮搜索值,仅高亮具体匹配值(通过title)值为true时使用默认色值,值为#xxx时使用此值替代且高亮开启 + highlight: { + type: [Boolean, String] as PropType, + default: false + }, + // 搜索完成时自动展开结果 + expandOnSearch: Boolean, + // 搜索完成自动选中所有结果,当且仅当 checkable===true 时生效 + checkOnSearch: Boolean, + // 搜索完成自动select所有结果 + selectedOnSearch: Boolean, + loading: { + type: Boolean, + default: false + }, + treeWrapperClassName: String +}) + +export type TreeProps = ExtractPropTypes + +export interface ContextMenuItem { + label: string + icon?: string + hidden?: boolean + disabled?: boolean + handler?: Fn + divider?: boolean + children?: ContextMenuItem[] +} + +export interface ContextMenuOptions { + icon?: string + styles?: any + items?: ContextMenuItem[] +} + +export interface TreeItem extends TreeDataItem { + icon?: any +} + +export interface TreeActionItem { + render: (record: Recordable) => any + show?: boolean | ((record: Recordable) => boolean) +} + +export interface InsertNodeParams { + parentKey: string | null + node: TreeDataItem + list?: TreeDataItem[] + push?: 'push' | 'unshift' +} + +export interface TreeActionType { + checkAll: (checkAll: boolean) => void + expandAll: (expandAll: boolean) => void + setExpandedKeys: (keys: KeyType[]) => void + getExpandedKeys: () => KeyType[] + setSelectedKeys: (keys: KeyType[]) => void + getSelectedKeys: () => KeyType[] + setCheckedKeys: (keys: CheckKeys) => void + getCheckedKeys: () => CheckKeys + filterByLevel: (level: number) => void + insertNodeByKey: (opt: InsertNodeParams) => void + insertNodesByKey: (opt: InsertNodeParams) => void + deleteNodeByKey: (key: string) => void + updateNodeByKey: (key: string, node: Omit) => void + setSearchValue: (value: string) => void + getSearchValue: () => string + getSelectedNode: (key: KeyType, treeList?: TreeItem[], selectNode?: TreeItem | null) => TreeItem | null +} diff --git a/src/components/Tree/style/index.less b/src/components/Tree/style/index.less new file mode 100644 index 00000000..472d4caa --- /dev/null +++ b/src/components/Tree/style/index.less @@ -0,0 +1,52 @@ +@tree-prefix-cls: ~'@{namespace}-tree'; + +.@{tree-prefix-cls} { + background-color: @component-background; + + .ant-tree-node-content-wrapper { + position: relative; + + .ant-tree-title { + position: absolute; + left: 0; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + &__title { + position: relative; + display: flex; + align-items: center; + width: 100%; + padding-right: 10px; + + &:hover { + .@{tree-prefix-cls}__action { + visibility: visible; + } + } + } + + &__content { + overflow: hidden; + } + + &__actions { + position: absolute; + //top: 2px; + right: 3px; + display: flex; + } + + &__action { + margin-left: 4px; + visibility: hidden; + } + + &-header { + border-bottom: 1px solid @border-color-base; + } +} diff --git a/src/components/Tree/style/index.ts b/src/components/Tree/style/index.ts new file mode 100644 index 00000000..1526e4df --- /dev/null +++ b/src/components/Tree/style/index.ts @@ -0,0 +1 @@ +import './index.less' diff --git a/src/components/Upload/index.ts b/src/components/Upload/index.ts new file mode 100644 index 00000000..110621df --- /dev/null +++ b/src/components/Upload/index.ts @@ -0,0 +1,4 @@ +import { withInstall } from '@/utils' +import basicUpload from './src/BasicUpload.vue' + +export const BasicUpload = withInstall(basicUpload) diff --git a/src/components/Upload/src/BasicUpload.vue b/src/components/Upload/src/BasicUpload.vue new file mode 100644 index 00000000..07cd6858 --- /dev/null +++ b/src/components/Upload/src/BasicUpload.vue @@ -0,0 +1,103 @@ + + diff --git a/src/components/Upload/src/FileList.vue b/src/components/Upload/src/FileList.vue new file mode 100644 index 00000000..813e1236 --- /dev/null +++ b/src/components/Upload/src/FileList.vue @@ -0,0 +1,102 @@ + + diff --git a/src/components/Upload/src/ThumbUrl.vue b/src/components/Upload/src/ThumbUrl.vue new file mode 100644 index 00000000..b7a27fd1 --- /dev/null +++ b/src/components/Upload/src/ThumbUrl.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/components/Upload/src/UploadModal.vue b/src/components/Upload/src/UploadModal.vue new file mode 100644 index 00000000..f225a97c --- /dev/null +++ b/src/components/Upload/src/UploadModal.vue @@ -0,0 +1,277 @@ + + + diff --git a/src/components/Upload/src/UploadPreviewModal.vue b/src/components/Upload/src/UploadPreviewModal.vue new file mode 100644 index 00000000..625bbf2d --- /dev/null +++ b/src/components/Upload/src/UploadPreviewModal.vue @@ -0,0 +1,88 @@ + + + diff --git a/src/components/Upload/src/data.tsx b/src/components/Upload/src/data.tsx new file mode 100644 index 00000000..23e5e7bc --- /dev/null +++ b/src/components/Upload/src/data.tsx @@ -0,0 +1,147 @@ +import type { BasicColumn, ActionItem } from '@/components/Table' +import { FileItem, PreviewFileItem, UploadResultStatus } from './typing' +import { + // checkImgType, + isImgTypeByName +} from './helper' +import { Progress, Tag } from 'ant-design-vue' +import TableAction from '@/components/Table/src/components/TableAction.vue' +import ThumbUrl from './ThumbUrl.vue' +import { useI18n } from '@/hooks/web/useI18n' + +const { t } = useI18n() + +// 文件上传列表 +export function createTableColumns(): BasicColumn[] { + return [ + { + dataIndex: 'thumbUrl', + title: t('component.upload.legend'), + width: 100, + customRender: ({ record }) => { + const { thumbUrl } = (record as FileItem) || {} + return thumbUrl && + } + }, + { + dataIndex: 'name', + title: t('component.upload.fileName'), + align: 'left', + customRender: ({ text, record }) => { + const { percent, status: uploadStatus } = (record as FileItem) || {} + let status: 'normal' | 'exception' | 'active' | 'success' = 'normal' + if (uploadStatus === UploadResultStatus.ERROR) { + status = 'exception' + } else if (uploadStatus === UploadResultStatus.UPLOADING) { + status = 'active' + } else if (uploadStatus === UploadResultStatus.SUCCESS) { + status = 'success' + } + return ( + +

+ {text} +

+ +
+ ) + } + }, + { + dataIndex: 'size', + title: t('component.upload.fileSize'), + width: 100, + customRender: ({ text = 0 }) => { + return text && (text / 1024).toFixed(2) + 'KB' + } + }, + // { + // dataIndex: 'type', + // title: '文件类型', + // width: 100, + // }, + { + dataIndex: 'status', + title: t('component.upload.fileStatue'), + width: 100, + customRender: ({ text }) => { + if (text === UploadResultStatus.SUCCESS) { + return {() => t('component.upload.uploadSuccess')} + } else if (text === UploadResultStatus.ERROR) { + return {() => t('component.upload.uploadError')} + } else if (text === UploadResultStatus.UPLOADING) { + return {() => t('component.upload.uploading')} + } + + return text + } + } + ] +} +export function createActionColumn(handleRemove: Function): BasicColumn { + return { + width: 120, + title: t('component.upload.operating'), + dataIndex: 'action', + fixed: false, + customRender: ({ record }) => { + const actions: ActionItem[] = [ + { + label: t('component.upload.del'), + color: 'error', + onClick: handleRemove.bind(null, record) + } + ] + // if (checkImgType(record)) { + // actions.unshift({ + // label: t('component.upload.preview'), + // onClick: handlePreview.bind(null, record), + // }); + // } + return + } + } +} +// 文件预览列表 +export function createPreviewColumns(): BasicColumn[] { + return [ + { + dataIndex: 'url', + title: t('component.upload.legend'), + width: 100, + customRender: ({ record }) => { + const { url } = (record as PreviewFileItem) || {} + return isImgTypeByName(url) && + } + }, + { + dataIndex: 'name', + title: t('component.upload.fileName'), + align: 'left' + } + ] +} + +export function createPreviewActionColumn({ handleRemove, handleDownload }: { handleRemove: Fn; handleDownload: Fn }): BasicColumn { + return { + width: 160, + title: t('component.upload.operating'), + dataIndex: 'action', + fixed: false, + customRender: ({ record }) => { + const actions: ActionItem[] = [ + { + label: t('component.upload.del'), + color: 'error', + onClick: handleRemove.bind(null, record) + }, + { + label: t('component.upload.download'), + onClick: handleDownload.bind(null, record) + } + ] + + return + } + } +} diff --git a/src/components/Upload/src/helper.ts b/src/components/Upload/src/helper.ts new file mode 100644 index 00000000..b5dae124 --- /dev/null +++ b/src/components/Upload/src/helper.ts @@ -0,0 +1,27 @@ +export function checkFileType(file: File, accepts: string[]) { + const newTypes = accepts.join('|') + // const reg = /\.(jpg|jpeg|png|gif|txt|doc|docx|xls|xlsx|xml)$/i; + const reg = new RegExp('\\.(' + newTypes + ')$', 'i') + + return reg.test(file.name) +} + +export function checkImgType(file: File) { + return isImgTypeByName(file.name) +} + +export function isImgTypeByName(name: string) { + return /\.(jpg|jpeg|png|gif|webp)$/i.test(name) +} + +export function getBase64WithFile(file: File) { + return new Promise<{ + result: string + file: File + }>((resolve, reject) => { + const reader = new FileReader() + reader.readAsDataURL(file) + reader.onload = () => resolve({ result: reader.result as string, file }) + reader.onerror = (error) => reject(error) + }) +} diff --git a/src/components/Upload/src/props.ts b/src/components/Upload/src/props.ts new file mode 100644 index 00000000..325b2454 --- /dev/null +++ b/src/components/Upload/src/props.ts @@ -0,0 +1,82 @@ +import { FileBasicColumn } from './typing' + +export const basicProps = { + helpText: { + type: String as PropType, + default: '' + }, + // 文件最大多少MB + maxSize: { + type: Number as PropType, + default: 2 + }, + // 最大数量的文件,Infinity不限制 + maxNumber: { + type: Number as PropType, + default: Infinity + }, + // 根据后缀,或者其他 + accept: { + type: Array as PropType, + default: () => [] + }, + multiple: { + type: Boolean as PropType, + default: true + }, + uploadParams: { + type: Object as PropType, + default: () => ({}) + }, + api: { + type: Function as PropType, + default: null, + required: true + }, + name: { + type: String as PropType, + default: 'file' + }, + filename: { + type: String as PropType, + default: null + } +} + +export const uploadContainerProps = { + value: { + type: Array as PropType, + default: () => [] + }, + ...basicProps, + showPreviewNumber: { + type: Boolean as PropType, + default: true + }, + emptyHidePreview: { + type: Boolean as PropType, + default: false + } +} + +export const previewProps = { + value: { + type: Array as PropType, + default: () => [] + } +} + +export const fileListProps = { + columns: { + type: Array as PropType, + default: null + }, + actionColumn: { + type: Object as PropType, + default: null + }, + dataSource: { + type: Array as PropType, + default: null + } +} diff --git a/src/components/Upload/src/typing.ts b/src/components/Upload/src/typing.ts new file mode 100644 index 00000000..869cb2e3 --- /dev/null +++ b/src/components/Upload/src/typing.ts @@ -0,0 +1,55 @@ +import { UploadApiResult } from '@/api/sys/model/uploadModel' + +export enum UploadResultStatus { + SUCCESS = 'success', + ERROR = 'error', + UPLOADING = 'uploading' +} + +export interface FileItem { + thumbUrl?: string + name: string + size: string | number + type?: string + percent: number + file: File + status?: UploadResultStatus + responseData?: UploadApiResult + uuid: string +} + +export interface PreviewFileItem { + url: string + name: string + type: string +} + +export interface FileBasicColumn { + /** + * Renderer of the table cell. The return value should be a VNode, or an object for colSpan/rowSpan config + * @type Function | ScopedSlot + */ + customRender?: Function + /** + * Title of this column + * @type any (string | slot) + */ + title: string + + /** + * Width of this column + * @type string | number + */ + width?: number + /** + * Display field of the data record, could be set like a.b.c + * @type string + */ + dataIndex: string + /** + * specify how content is aligned + * @default 'left' + * @type string + */ + align?: 'left' | 'right' | 'center' +} diff --git a/src/components/Upload/src/useUpload.ts b/src/components/Upload/src/useUpload.ts new file mode 100644 index 00000000..6ada3952 --- /dev/null +++ b/src/components/Upload/src/useUpload.ts @@ -0,0 +1,60 @@ +import { Ref, unref, computed } from 'vue' +import { useI18n } from '@/hooks/web/useI18n' +const { t } = useI18n() +export function useUploadType({ + acceptRef, + helpTextRef, + maxNumberRef, + maxSizeRef +}: { + acceptRef: Ref + helpTextRef: Ref + maxNumberRef: Ref + maxSizeRef: Ref +}) { + // 文件类型限制 + const getAccept = computed(() => { + const accept = unref(acceptRef) + if (accept && accept.length > 0) { + return accept + } + return [] + }) + const getStringAccept = computed(() => { + return unref(getAccept) + .map((item) => { + if (item.indexOf('/') > 0 || item.startsWith('.')) { + return item + } else { + return `.${item}` + } + }) + .join(',') + }) + + // 支持jpg、jpeg、png格式,不超过2M,最多可选择10张图片,。 + const getHelpText = computed(() => { + const helpText = unref(helpTextRef) + if (helpText) { + return helpText + } + const helpTexts: string[] = [] + + const accept = unref(acceptRef) + if (accept.length > 0) { + helpTexts.push(t('component.upload.accept', [accept.join(',')])) + } + + const maxSize = unref(maxSizeRef) + if (maxSize) { + helpTexts.push(t('component.upload.maxSize', [maxSize])) + } + + const maxNumber = unref(maxNumberRef) + if (maxNumber && maxNumber !== Infinity) { + helpTexts.push(t('component.upload.maxNumber', [maxNumber])) + } + return helpTexts.join(',') + }) + return { getAccept, getStringAccept, getHelpText } +} diff --git a/src/components/Verify/index.ts b/src/components/Verify/index.ts new file mode 100644 index 00000000..98654f27 --- /dev/null +++ b/src/components/Verify/index.ts @@ -0,0 +1,7 @@ +import { withInstall } from '@/utils' +import basicDragVerify from './src/DragVerify.vue' +import rotateDragVerify from './src/ImgRotate.vue' + +export const BasicDragVerify = withInstall(basicDragVerify) +export const RotateDragVerify = withInstall(rotateDragVerify) +export * from './src/typing' diff --git a/src/components/Verify/src/DragVerify.vue b/src/components/Verify/src/DragVerify.vue new file mode 100644 index 00000000..f699930f --- /dev/null +++ b/src/components/Verify/src/DragVerify.vue @@ -0,0 +1,365 @@ + + diff --git a/src/components/Verify/src/ImgRotate.vue b/src/components/Verify/src/ImgRotate.vue new file mode 100644 index 00000000..fa24cc68 --- /dev/null +++ b/src/components/Verify/src/ImgRotate.vue @@ -0,0 +1,214 @@ + + diff --git a/src/components/Verify/src/props.ts b/src/components/Verify/src/props.ts new file mode 100644 index 00000000..9122e1f8 --- /dev/null +++ b/src/components/Verify/src/props.ts @@ -0,0 +1,86 @@ +import { useI18n } from '@/hooks/web/useI18n' + +const { t } = useI18n() +export const basicProps = { + value: { + type: Boolean as PropType, + default: false + }, + + isSlot: { + type: Boolean as PropType, + default: false + }, + + text: { + type: [String] as PropType, + default: t('component.verify.dragText') + }, + successText: { + type: [String] as PropType, + default: t('component.verify.successText') + }, + height: { + type: [Number, String] as PropType, + default: 40 + }, + + width: { + type: [Number, String] as PropType, + default: 220 + }, + + circle: { + type: Boolean as PropType, + default: false + }, + + wrapStyle: { + type: Object as PropType, + default: () => ({}) + }, + contentStyle: { + type: Object as PropType, + default: () => ({}) + }, + barStyle: { + type: Object as PropType, + default: () => ({}) + }, + actionStyle: { + type: Object as PropType, + default: () => ({}) + } +} + +export const rotateProps = { + ...basicProps, + src: { + type: String as PropType + }, + + imgWidth: { + type: Number as PropType, + default: 260 + }, + + imgWrapStyle: { + type: Object as PropType, + default: () => ({}) + }, + + minDegree: { + type: Number as PropType, + default: 90 + }, + + maxDegree: { + type: Number as PropType, + default: 270 + }, + + diffDegree: { + type: Number as PropType, + default: 20 + } +} diff --git a/src/components/Verify/src/typing.ts b/src/components/Verify/src/typing.ts new file mode 100644 index 00000000..6ff148e4 --- /dev/null +++ b/src/components/Verify/src/typing.ts @@ -0,0 +1,14 @@ +export interface DragVerifyActionType { + resume: () => void +} + +export interface PassingData { + isPassing: boolean + time: number +} + +export interface MoveData { + event: MouseEvent | TouchEvent + moveDistance: number + moveX: number +} diff --git a/src/components/VirtualScroll/index.ts b/src/components/VirtualScroll/index.ts new file mode 100644 index 00000000..f53d0b50 --- /dev/null +++ b/src/components/VirtualScroll/index.ts @@ -0,0 +1,4 @@ +import { withInstall } from '@/utils' +import vScroll from './src/VirtualScroll.vue' + +export const VScroll = withInstall(vScroll) diff --git a/src/components/VirtualScroll/src/VirtualScroll.vue b/src/components/VirtualScroll/src/VirtualScroll.vue new file mode 100644 index 00000000..965915ef --- /dev/null +++ b/src/components/VirtualScroll/src/VirtualScroll.vue @@ -0,0 +1,180 @@ + + diff --git a/src/components/registerGlobComp.ts b/src/components/registerGlobComp.ts new file mode 100644 index 00000000..2b14c462 --- /dev/null +++ b/src/components/registerGlobComp.ts @@ -0,0 +1,7 @@ +import type { App } from 'vue' +import { Button } from './Button' +import { Input, Layout } from 'ant-design-vue' + +export function registerGlobComp(app: App) { + app.use(Input).use(Button).use(Layout) +} diff --git a/src/design/ant/btn.less b/src/design/ant/btn.less new file mode 100644 index 00000000..f310a0be --- /dev/null +++ b/src/design/ant/btn.less @@ -0,0 +1,286 @@ +// button reset +.ant-btn { + &-link:hover, + &-link:focus, + &-link:active { + border-color: transparent !important; + } + + &-primary { + color: @white; + background-color: @button-primary-color; + + &:hover, + &:focus { + color: @white; + background-color: @button-primary-hover-color; + } + } + + /* stylelint-disable-next-line selector-not-notation */ + &-primary:not(&-background-ghost):not([disabled]) { + color: @white; + } + + &-default { + color: @button-cancel-color; + background-color: @button-cancel-bg-color; + border-color: @button-cancel-border-color; + + &:hover, + &:focus { + color: @button-cancel-hover-color; + background-color: @button-cancel-hover-bg-color; + border-color: @button-cancel-hover-border-color; + } + // + //&[disabled], + //&[disabled]:hover { + // color: fade(@button-cancel-color, 40%) !important; + // background: fade(@button-cancel-bg-color, 40%) !important; + // border-color: fade(@button-cancel-border-color, 40%) !important; + //} + } + + [data-theme='light'] &.ant-btn-link.is-disabled { + color: rgb(0 0 0 / 25%); + text-shadow: none; + cursor: not-allowed !important; + background-color: transparent !important; + border-color: transparent !important; + box-shadow: none; + } + + [data-theme='dark'] &.ant-btn-link.is-disabled { + color: rgb(255 255 255 / 25%) !important; + text-shadow: none; + cursor: not-allowed !important; + background-color: transparent !important; + border-color: transparent !important; + box-shadow: none; + } + + // color: @white; + + &-success.ant-btn-link:not([disabled='disabled']) { + color: @button-success-color; + + &:hover, + &:focus { + color: @button-success-hover-color; + border-color: transparent; + } + + &:active { + color: @button-success-active-color; + } + } + + &-success.ant-btn-link.ant-btn-loading, + &-warning.ant-btn-link.ant-btn-loading, + &-error.ant-btn-link.ant-btn-loading, + &-background-ghost.ant-btn-link.ant-btn-loading, + &.ant-btn-link.ant-btn-loading { + &::before { + background: transparent; + } + } + + &-success:not(.ant-btn-link, .is-disabled) { + color: @white; + background-color: @button-success-color; + border-color: @button-success-color; + //border-width: 0; + + &:hover, + &:focus { + color: @white; + background-color: @button-success-hover-color; + border-color: @button-success-hover-color; + } + + &:active { + background-color: @button-success-active-color; + border-color: @button-success-active-color; + } + } + + &-warning.ant-btn-link:not([disabled='disabled']) { + color: @button-warn-color; + + &:hover, + &:focus { + color: @button-warn-hover-color; + border-color: transparent; + } + + &:active { + color: @button-warn-active-color; + } + } + + &-warning:not(.ant-btn-link, .is-disabled) { + color: @white; + background-color: @button-warn-color; + border-color: @button-warn-color; + //border-width: 0; + + &:hover, + &:focus { + color: @white; + background-color: @button-warn-hover-color; + border-color: @button-warn-hover-color; + } + + &:active { + background-color: @button-warn-active-color; + border-color: @button-warn-active-color; + } + + //&[disabled], + //&[disabled]:hover { + // color: @white; + // background-color: fade(@button-warn-color, 40%); + // border-color: fade(@button-warn-color, 40%); + //} + } + + &-error.ant-btn-link:not([disabled='disabled']) { + color: @button-error-color; + + &:hover, + &:focus { + color: @button-error-hover-color; + border-color: transparent; + } + + &:active { + color: @button-error-active-color; + } + } + + &-error:not(.ant-btn-link, .is-disabled) { + color: @white; + background-color: @button-error-color; + border-color: @button-error-color; + //border-width: 0; + + &:hover, + &:focus { + color: @white; + background-color: @button-error-hover-color; + border-color: @button-error-hover-color; + } + + &:active { + background-color: @button-error-active-color; + border-color: @button-error-active-color; + } + + //&[disabled], + //&[disabled]:hover { + // color: @white; + // background-color: fade(@button-error-color, 40%); + // border-color: fade(@button-error-color, 40%); + //} + } + + &-background-ghost { + border-width: 1px; + background-color: transparent !important; + + &[disabled], + &[disabled]:hover { + color: fade(@white, 40%) !important; + background-color: transparent !important; + border-color: fade(@white, 40%) !important; + } + } + + &-dashed&-background-ghost, + &-default&-background-ghost { + color: @button-ghost-color; + border-color: @button-ghost-color; + + &:hover, + &:focus { + color: @button-ghost-hover-color; + border-color: @button-ghost-hover-color; + } + + &:active { + color: @button-ghost-active-color; + border-color: @button-ghost-active-color; + } + + &[disabled], + &[disabled]:hover { + color: fade(@white, 40%) !important; + border-color: fade(@white, 40%) !important; + } + } + + &-background-ghost&-success:not(.ant-btn-link) { + color: @button-success-color; + background-color: transparent; + border-color: @button-success-color; + border-width: 1px; + + &:hover, + &:focus { + color: @button-success-hover-color !important; + border-color: @button-success-hover-color; + } + + &:active { + color: @button-success-active-color; + border-color: @button-success-active-color; + } + } + + &-background-ghost&-warning:not(.ant-btn-link) { + color: @button-warn-color; + background-color: transparent; + border-color: @button-warn-color; + border-width: 1px; + + &:hover, + &:focus { + color: @button-warn-hover-color !important; + border-color: @button-warn-hover-color; + } + + &:active { + color: @button-warn-active-color; + border-color: @button-warn-active-color; + } + } + + &-background-ghost&-error:not(.ant-btn-link) { + color: @button-error-color; + background-color: transparent; + border-color: @button-error-color; + border-width: 1px; + + &:hover, + &:focus { + color: @button-error-hover-color !important; + border-color: @button-error-hover-color; + } + + &:active { + color: @button-error-active-color; + border-color: @button-error-active-color; + } + } + + &-ghost.ant-btn-link:not([disabled='disabled']) { + color: @button-ghost-color; + + &:hover, + &:focus { + color: @button-ghost-hover-color; + border-color: transparent; + } + } +} diff --git a/src/design/ant/index.less b/src/design/ant/index.less new file mode 100644 index 00000000..0021d09e --- /dev/null +++ b/src/design/ant/index.less @@ -0,0 +1,68 @@ +/* stylelint-disable import-notation */ +@import './pagination.less'; +@import './input.less'; +@import './btn.less'; + +.ant-image-preview-root { + img { + display: unset; + } +} + +span.anticon:not(.app-iconify) { + vertical-align: 0.125em !important; +} + +.ant-back-top { + right: 20px; + bottom: 20px; +} + +.collapse-container__body { + > .ant-descriptions { + margin-left: 6px; + } +} + +.ant-image-preview-operations { + background-color: rgb(0 0 0 / 30%); +} + +.ant-popover { + &-content { + box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%); + } +} + +// ================================= +// ==============modal message====== +// ================================= +.modal-icon-warning { + color: @warning-color !important; +} + +.modal-icon-success { + color: @success-color !important; +} + +.modal-icon-error { + color: @error-color !important; +} + +.modal-icon-info { + color: @primary-color !important; +} + +.ant-checkbox-checked .ant-checkbox-inner::after, +.ant-tree-checkbox-checked .ant-tree-checkbox-inner::after { + border-top: 0 !important; + border-left: 0 !important; +} + +.ant-form-item-control-input-content { + > div { + > div { + max-width: 100%; + } + } +} diff --git a/src/design/ant/input.less b/src/design/ant/input.less new file mode 100644 index 00000000..5a8bc9b0 --- /dev/null +++ b/src/design/ant/input.less @@ -0,0 +1,27 @@ +@import (reference) '../color.less'; + +// input +.ant-input { + &-number, + &-number-group-wrapper { + min-width: 110px; + width: 100% !important; + max-width: 100%; + } +} + +.ant-input-affix-wrapper .ant-input-suffix { + right: 9px; +} + +.ant-input-clear-icon { + margin-right: 5px; +} + +.ant-input-affix-wrapper-textarea-with-clear-btn { + padding: 0 !important; + + textarea.ant-input { + padding: 4px; + } +} diff --git a/src/design/ant/pagination.less b/src/design/ant/pagination.less new file mode 100644 index 00000000..b3580f95 --- /dev/null +++ b/src/design/ant/pagination.less @@ -0,0 +1,96 @@ +html[data-theme='dark'] { + .ant-pagination { + &.mini { + .ant-pagination-prev, + .ant-pagination-next, + .ant-pagination-item { + background-color: rgb(255 255 255 / 4%) !important; + + a { + color: #8b949e !important; + } + } + + .ant-select-arrow { + color: @text-color-secondary !important; + } + + .ant-pagination-item-active { + background-color: @primary-color !important; + border: none; + border-radius: none !important; + + a { + color: @white !important; + } + } + } + } +} + +.ant-pagination { + &.mini { + .ant-pagination-prev, + .ant-pagination-next { + font-size: 12px; + color: @text-color-base; + border: 1px solid; + } + + .ant-pagination-prev:hover, + .ant-pagination-next:hover, + .ant-pagination-item:focus, + .ant-pagination-item:hover { + a { + color: @primary-color; + } + } + + .ant-pagination-prev, + .ant-pagination-next, + .ant-pagination-item { + margin: 0 4px !important; + background-color: #f4f4f5 !important; + border: none; + border-radius: none !important; + + a { + margin-top: 1px; + color: #606266; + } + + &:last-child { + margin-right: 0 !important; + } + } + + .ant-pagination-item-active { + background-color: @primary-color !important; + border: none; + border-radius: none !important; + + a { + color: @white !important; + } + } + + .ant-pagination-options { + margin-left: 12px; + } + + .ant-pagination-options-quick-jumper input { + height: 22px; + margin: 0 6px; + line-height: 22px; + text-align: center; + } + + .ant-select-arrow { + color: @border-color-shallow-dark; + } + } + + &-disabled { + display: none !important; + } +} diff --git a/src/design/ant/table.less b/src/design/ant/table.less new file mode 100644 index 00000000..ee62b998 --- /dev/null +++ b/src/design/ant/table.less @@ -0,0 +1,76 @@ +@prefix-cls: ~'@{namespace}-basic-table'; + +// fix table unnecessary scrollbar +.@{prefix-cls} { + .hide-scrollbar-y { + .ant-spin-nested-loading { + .ant-spin-container { + .ant-table { + .ant-table-content { + .ant-table-scroll { + .ant-table-hide-scrollbar { + overflow-y: auto !important; + } + + .ant-table-body { + overflow-y: auto !important; + } + } + + .ant-table-fixed-right { + .ant-table-body-outer { + .ant-table-body-inner { + overflow-y: auto !important; + } + } + } + + .ant-table-fixed-left { + .ant-table-body-outer { + .ant-table-body-inner { + overflow-y: auto !important; + } + } + } + } + } + } + } + } + + .hide-scrollbar-x { + .ant-spin-nested-loading { + .ant-spin-container { + .ant-table { + .ant-table-content { + .ant-table-scroll { + .ant-table-hide-scrollbar { + //overflow-x: auto !important; + } + + .ant-table-body { + overflow: auto !important; + } + } + + .ant-table-fixed-right { + .ant-table-body-outer { + .ant-table-body-inner { + overflow-x: auto !important; + } + } + } + + .ant-table-fixed-left { + .ant-table-body-outer { + .ant-table-body-inner { + overflow-x: auto !important; + } + } + } + } + } + } + } + } +} diff --git a/src/design/color.less b/src/design/color.less new file mode 100644 index 00000000..9d2138c4 --- /dev/null +++ b/src/design/color.less @@ -0,0 +1,138 @@ +html { + // header + --header-bg-color: #394664; + --header-bg-hover-color: #273352; + --header-active-menu-bg-color: #273352; + + // sider + --sider-dark-bg-color: #273352; + --sider-dark-darken-bg-color: #273352; + --sider-dark-lighten-bg-color: #273352; +} + +@white: #fff; + +@content-bg: #f4f7f9; + +// :export { +// name: "less"; +// mainColor: @mainColor; +// fontSize: @fontSize; +// } +@iconify-bg-color: #5551; + +// ================================= +// ==============border-color======= +// ================================= + +// Dark-dark +@border-color-dark: #b6b7b9; + +// Dark-light +@border-color-shallow-dark: #cececd; + +// Light-dark +@border-color-light: @border-color-base; + +// ================================= +// ==============message============== +// ================================= + +// success-bg-color +@success-background-color: #f1f9ec; +// info-bg-color +@info-background-color: #e8eff8; +// warn-bg-color +@warning-background-color: #fdf6ed; +// danger-bg-color +@danger-background-color: #fef0f0; + +// ================================= +// ==============Header============= +// ================================= + +@header-dark-bg-color: var(--header-bg-color); +@header-dark-bg-hover-color: var(--header-bg-hover-color); +@header-light-bg-hover-color: #f6f6f6; +@header-light-desc-color: #7c8087; +@header-light-bottom-border-color: #eee; +// top-menu +@top-menu-active-bg-color: var(--header-active-menu-bg-color); + +// ================================= +// ==============Menu============ +// ================================= + +// let -menu +@sider-dark-bg-color: var(--sider-dark-bg-color); +@sider-dark-darken-bg-color: var(--sider-dark-darken-bg-color); +@sider-dark-lighten-bg-color: var(--sider-dark-lighten-bg-color); + +// trigger +@trigger-dark-hover-bg-color: rgba(255, 255, 255, 0.2); +@trigger-dark-bg-color: rgba(255, 255, 255, 0.1); + +// ================================= +// ==============tree============ +// ================================= +// tree item hover background +@tree-hover-background-color: #f5f7fa; +// tree item hover font color +@tree-hover-font-color: #f5f7fa; + +// ================================= +// ==============link============ +// ================================= +@link-hover-color: @primary-color; +@link-active-color: darken(@primary-color, 10%); + +// ================================= +// ==============Text color-============= +// ================================= + +// Main text color +@text-color-base: @text-color; + +// Label color +@text-color-call-out: #606266; + +// Auxiliary information color-dark +@text-color-help-dark: #909399; + +// ================================= +// ==============breadcrumb========= +// ================================= +@breadcrumb-item-normal-color: #999; +// ================================= +// ==============button============= +// ================================= + +@button-primary-color: @primary-color; +@button-primary-hover-color: lighten(@primary-color, 5%); +@button-primary-active-color: darken(@primary-color, 5%); + +@button-ghost-color: @white; +@button-ghost-hover-color: lighten(@white, 10%); +@button-ghost-hover-bg-color: #e1ebf6; +@button-ghost-active-color: darken(@white, 10%); + +@button-success-color: @success-color; +@button-success-hover-color: lighten(@success-color, 10%); +@button-success-active-color: darken(@success-color, 10%); + +@button-warn-color: @warning-color; +@button-warn-hover-color: lighten(@warning-color, 10%); +@button-warn-active-color: darken(@warning-color, 10%); + +@button-error-color: @error-color; +@button-error-hover-color: lighten(@error-color, 10%); +@button-error-active-color: darken(@error-color, 10%); + +@button-cancel-color: @text-color-call-out; +@button-cancel-bg-color: @white; +@button-cancel-border-color: @border-color-shallow-dark; + +// Mouse over +@button-cancel-hover-color: @primary-color; +@button-cancel-hover-bg-color: @white; +@button-cancel-hover-border-color: @primary-color; diff --git a/src/design/config.less b/src/design/config.less new file mode 100644 index 00000000..64c33f6f --- /dev/null +++ b/src/design/config.less @@ -0,0 +1,2 @@ +@import (reference) 'color.less'; +@import (reference) 'var/index.less'; diff --git a/src/design/index.less b/src/design/index.less new file mode 100644 index 00000000..4b595acc --- /dev/null +++ b/src/design/index.less @@ -0,0 +1,45 @@ +/* stylelint-disable import-notation */ +@import 'transition/index.less'; +@import 'var/index.less'; +@import 'public.less'; +@import 'ant/index.less'; +@import './theme.less'; + +input:-webkit-autofill { + box-shadow: 0 0 0 1000px white inset !important; +} + +:-webkit-autofill { + transition: background-color 5000s ease-in-out 0s !important; +} + +html { + overflow: hidden; + text-size-adjust: 100%; +} + +html, +body { + width: 100%; + height: 100%; + overflow: visible; + overflow-x: hidden; + + &.color-weak { + filter: invert(80%); + } + + &.gray-mode { + filter: grayscale(100%); + filter: progid:dximagetransform.microsoft.basicimage(grayscale=1); + } +} + +a:focus, +a:active, +button, +div, +svg, +span { + outline: none; +} diff --git a/src/design/public.less b/src/design/public.less new file mode 100644 index 00000000..644bfab2 --- /dev/null +++ b/src/design/public.less @@ -0,0 +1,51 @@ +#app { + width: 100%; + height: 100%; +} + +// ================================= +// ==============scrollbar========== +// ================================= + +::-webkit-scrollbar { + width: 7px; + height: 8px; +} + +// ::-webkit-scrollbar-track { +// background: transparent; +// } + +::-webkit-scrollbar-track { + background-color: rgb(0 0 0 / 5%); +} + +::-webkit-scrollbar-thumb { + // background: rgba(0, 0, 0, 0.6); + background-color: rgb(144 147 153 / 30%); + // background-color: rgba(144, 147, 153, 0.3); + border-radius: 2px; + box-shadow: inset 0 0 6px rgb(0 0 0 / 20%); +} + +::-webkit-scrollbar-thumb:hover { + background-color: @border-color-dark; +} + +// ================================= +// ==============nprogress========== +// ================================= +#nprogress { + pointer-events: none; + + .bar { + position: fixed; + top: 0; + left: 0; + z-index: 99999; + width: 100%; + height: 2px; + background-color: @primary-color; + opacity: 0.75; + } +} diff --git a/src/design/theme.less b/src/design/theme.less new file mode 100644 index 00000000..33075b68 --- /dev/null +++ b/src/design/theme.less @@ -0,0 +1,54 @@ +/* stylelint-disable declaration-colon-newline-after */ +/* stylelint-disable value-list-comma-newline-after */ +.bg-white { + background-color: @component-background !important; +} + +html[data-theme='light'] { + .text-secondary { + color: rgb(0 0 0 / 45%); + } + + .ant-alert-success { + background-color: #f6ffed; + border: 1px solid #b7eb8f; + } + + .ant-alert-error { + background-color: #fff2f0; + border: 1px solid #ffccc7; + } + + .ant-alert-warning { + background-color: #fffbe6; + border: 1px solid #ffe58f; + } + + :not(:root):fullscreen::backdrop { + background-color: @layout-body-background !important; + } +} + +[data-theme='dark'] { + .text-secondary { + color: #8b949e; + } + + .ant-card-grid-hoverable:hover { + box-shadow: 0 3px 6px -4px rgb(0 0 0 / 48%), 0 6px 16px 0 rgb(0 0 0 / 32%), + 0 9px 28px 8px rgb(0 0 0 / 20%); + } + + .ant-card-grid { + box-shadow: 1px 0 0 0 #434343, 0 1px 0 0 #434343, 1px 1px 0 0 #434343, 1px 0 0 0 #434343 inset, + 0 1px 0 0 #434343 inset; + } + + .ant-calendar-selected-day .ant-calendar-date { + color: rgb(0 0 0 / 80%); + } + + .ant-select-tree li .ant-select-tree-node-content-wrapper.ant-select-tree-node-selected { + color: rgb(0 0 0 / 90%); + } +} diff --git a/src/design/transition/base.less b/src/design/transition/base.less new file mode 100644 index 00000000..7944c8bb --- /dev/null +++ b/src/design/transition/base.less @@ -0,0 +1,18 @@ +.transition-default() { + &-enter-active, + &-leave-active { + transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1) !important; + } + + &-move { + transition: transform 0.4s; + } +} + +.expand-transition { + .transition-default(); +} + +.expand-x-transition { + .transition-default(); +} diff --git a/src/design/transition/fade.less b/src/design/transition/fade.less new file mode 100644 index 00000000..6ed17713 --- /dev/null +++ b/src/design/transition/fade.less @@ -0,0 +1,93 @@ +.fade-transition { + &-enter-active, + &-leave-active { + transition: opacity 0.2s ease-in-out; + } + + &-enter-from, + &-leave-to { + opacity: 0; + } +} + +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.2s ease-in-out; +} + +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} + +/* fade-slide */ +.fade-slide-leave-active, +.fade-slide-enter-active { + transition: all 0.3s; +} + +.fade-slide-enter-from { + opacity: 0; + transform: translateX(-30px); +} + +.fade-slide-leave-to { + opacity: 0; + transform: translateX(30px); +} + +// /////////////////////////////////////////////// +// Fade Bottom +// /////////////////////////////////////////////// + +// Speed: 1x +.fade-bottom-enter-active, +.fade-bottom-leave-active { + transition: opacity 0.25s, transform 0.3s; +} + +.fade-bottom-enter-from { + opacity: 0; + transform: translateY(-10%); +} + +.fade-bottom-leave-to { + opacity: 0; + transform: translateY(10%); +} + +// fade-scale +.fade-scale-leave-active, +.fade-scale-enter-active { + transition: all 0.28s; +} + +.fade-scale-enter-from { + opacity: 0; + transform: scale(1.2); +} + +.fade-scale-leave-to { + opacity: 0; + transform: scale(0.8); +} + +// /////////////////////////////////////////////// +// Fade Top +// /////////////////////////////////////////////// + +// Speed: 1x +.fade-top-enter-active, +.fade-top-leave-active { + transition: opacity 0.2s, transform 0.25s; +} + +.fade-top-enter-from { + opacity: 0; + transform: translateY(8%); +} + +.fade-top-leave-to { + opacity: 0; + transform: translateY(-8%); +} diff --git a/src/design/transition/index.less b/src/design/transition/index.less new file mode 100644 index 00000000..96602115 --- /dev/null +++ b/src/design/transition/index.less @@ -0,0 +1,11 @@ +/* stylelint-disable import-notation */ +@import './base.less'; +@import './fade.less'; +@import './scale.less'; +@import './slide.less'; +@import './scroll.less'; +@import './zoom.less'; + +.collapse-transition { + transition: 0.2s height ease-in-out, 0.2s padding-top ease-in-out, 0.2s padding-bottom ease-in-out; +} diff --git a/src/design/transition/scale.less b/src/design/transition/scale.less new file mode 100644 index 00000000..c9654935 --- /dev/null +++ b/src/design/transition/scale.less @@ -0,0 +1,21 @@ +.scale-transition { + .transition-default(); + + &-enter-from, + &-leave, + &-leave-to { + opacity: 0; + transform: scale(0); + } +} + +.scale-rotate-transition { + .transition-default(); + + &-enter-from, + &-leave, + &-leave-to { + opacity: 0; + transform: scale(0) rotate(-45deg); + } +} diff --git a/src/design/transition/scroll.less b/src/design/transition/scroll.less new file mode 100644 index 00000000..a5f45e4c --- /dev/null +++ b/src/design/transition/scroll.less @@ -0,0 +1,67 @@ +.scroll-y-transition { + .transition-default(); + + &-enter-from, + &-leave-to { + opacity: 0; + } + + &-enter-from { + transform: translateY(-15px); + } + + &-leave-to { + transform: translateY(15px); + } +} + +.scroll-y-reverse-transition { + .transition-default(); + + &-enter-from, + &-leave-to { + opacity: 0; + } + + &-enter-from { + transform: translateY(15px); + } + + &-leave-to { + transform: translateY(-15px); + } +} + +.scroll-x-transition { + .transition-default(); + + &-enter-from, + &-leave-to { + opacity: 0; + } + + &-enter-from { + transform: translateX(-15px); + } + + &-leave-to { + transform: translateX(15px); + } +} + +.scroll-x-reverse-transition { + .transition-default(); + + &-enter-from, + &-leave-to { + opacity: 0; + } + + &-enter-from { + transform: translateX(15px); + } + + &-leave-to { + transform: translateX(-15px); + } +} diff --git a/src/design/transition/slide.less b/src/design/transition/slide.less new file mode 100644 index 00000000..79b00df8 --- /dev/null +++ b/src/design/transition/slide.less @@ -0,0 +1,39 @@ +.slide-y-transition { + .transition-default(); + + &-enter-from, + &-leave-to { + opacity: 0; + transform: translateY(-15px); + } +} + +.slide-y-reverse-transition { + .transition-default(); + + &-enter-from, + &-leave-to { + opacity: 0; + transform: translateY(15px); + } +} + +.slide-x-transition { + .transition-default(); + + &-enter-from, + &-leave-to { + opacity: 0; + transform: translateX(-15px); + } +} + +.slide-x-reverse-transition { + .transition-default(); + + &-enter-from, + &-leave-to { + opacity: 0; + transform: translateX(15px); + } +} diff --git a/src/design/transition/zoom.less b/src/design/transition/zoom.less new file mode 100644 index 00000000..2ea378ca --- /dev/null +++ b/src/design/transition/zoom.less @@ -0,0 +1,27 @@ +// zoom-out +.zoom-out-enter-active, +.zoom-out-leave-active { + transition: opacity 0.1 ease-in-out, transform 0.15s ease-out; +} + +.zoom-out-enter-from, +.zoom-out-leave-to { + opacity: 0; + transform: scale(0); +} + +// zoom-fade +.zoom-fade-enter-active, +.zoom-fade-leave-active { + transition: transform 0.2s, opacity 0.3s ease-out; +} + +.zoom-fade-enter-from { + opacity: 0; + transform: scale(0.92); +} + +.zoom-fade-leave-to { + opacity: 0; + transform: scale(1.06); +} diff --git a/src/design/var/breakpoint.less b/src/design/var/breakpoint.less new file mode 100644 index 00000000..793e826a --- /dev/null +++ b/src/design/var/breakpoint.less @@ -0,0 +1,33 @@ +// ================================= +// ==============屏幕断点============ +// ================================= + +// Extra small screen / phone +@screen-xs: 480px; +@screen-xs-min: @screen-xs; + +// Small screen / tablet +@screen-sm: 576px; +@screen-sm-min: @screen-sm; + +// Medium screen / desktop +@screen-md: 768px; +@screen-md-min: @screen-md; + +// Large screen / wide desktop +@screen-lg: 992px; +@screen-lg-min: @screen-lg; + +// Extra large screen / full hd +@screen-xl: 1200px; +@screen-xl-min: @screen-xl; + +// Extra extra large screen / large desktop +@screen-2xl: 1600px; +@screen-2xl-min: @screen-2xl; + +@screen-xs-max: (@screen-sm-min - 1px); +@screen-sm-max: (@screen-md-min - 1px); +@screen-md-max: (@screen-lg-min - 1px); +@screen-lg-max: (@screen-xl-min - 1px); +@screen-xl-max: (@screen-2xl-min - 1px); diff --git a/src/design/var/easing.less b/src/design/var/easing.less new file mode 100644 index 00000000..e19735f6 --- /dev/null +++ b/src/design/var/easing.less @@ -0,0 +1,18 @@ +// ================================= +// ==============动画函数-=========== +// ================================= + +@ease-base-out: cubic-bezier(0.7, 0.3, 0.1, 1); +@ease-base-in: cubic-bezier(0.9, 0, 0.3, 0.7); +@ease-out: cubic-bezier(0.215, 0.61, 0.355, 1); +@ease-in: cubic-bezier(0.55, 0.055, 0.675, 0.19); +@ease-in-out: cubic-bezier(0.645, 0.045, 0.355, 1); +@ease-out-back: cubic-bezier(0.12, 0.4, 0.29, 1.46); +@ease-in-back: cubic-bezier(0.71, -0.46, 0.88, 0.6); +@ease-in-out-back: cubic-bezier(0.71, -0.46, 0.29, 1.46); +@ease-out-circ: cubic-bezier(0.08, 0.82, 0.17, 1); +@ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.34); +@ease-in-out-circ: cubic-bezier(0.78, 0.14, 0.15, 0.86); +@ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1); +@ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06); +@ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1); diff --git a/src/design/var/index.less b/src/design/var/index.less new file mode 100644 index 00000000..d24c68e7 --- /dev/null +++ b/src/design/var/index.less @@ -0,0 +1,40 @@ +/* stylelint-disable import-notation */ +@import (reference) '../color.less'; +@import 'easing'; +@import 'breakpoint'; + +@namespace: vben; + +// tabs +@multiple-height: 30px; + +// headers +@header-height: 48px; + +// logo width +@logo-width: 32px; + +// +@side-drag-z-index: 200; + +@page-loading-z-index: 10000; + +@lock-page-z-index: 3000; + +@layout-header-fixed-z-index: 500; + +@multiple-tab-fixed-z-index: 505; + +@layout-sider-fixed-z-index: 510; + +@layout-mix-sider-fixed-z-index: 550; + +@preview-comp-z-index: 1000; + +@page-footer-z-index: 99; + +.bem(@n; @content) { + @{namespace}-@{n} { + @content(); + } +} diff --git a/src/directives/clickOutside.ts b/src/directives/clickOutside.ts new file mode 100644 index 00000000..a39c0d0d --- /dev/null +++ b/src/directives/clickOutside.ts @@ -0,0 +1,78 @@ +import { on } from '@/utils/domUtils' +import { isServer } from '@/utils/is' +import type { ComponentPublicInstance, DirectiveBinding, ObjectDirective } from 'vue' + +type DocumentHandler = (mouseup: T, mousedown: T) => void + +type FlushList = Map< + HTMLElement, + { + documentHandler: DocumentHandler + bindingFn: (...args: unknown[]) => unknown + } +> + +const nodeList: FlushList = new Map() + +let startClick: MouseEvent + +if (!isServer) { + on(document, 'mousedown', (e: MouseEvent) => (startClick = e)) + on(document, 'mouseup', (e: MouseEvent) => { + for (const { documentHandler } of nodeList.values()) { + documentHandler(e, startClick) + } + }) +} + +function createDocumentHandler(el: HTMLElement, binding: DirectiveBinding): DocumentHandler { + let excludes: HTMLElement[] = [] + if (Array.isArray(binding.arg)) { + excludes = binding.arg + } else { + // due to current implementation on binding type is wrong the type casting is necessary here + excludes.push(binding.arg as unknown as HTMLElement) + } + return function (mouseup, mousedown) { + const popperRef = ( + binding.instance as ComponentPublicInstance<{ + popperRef: Nullable + }> + ).popperRef + const mouseUpTarget = mouseup.target as Node + const mouseDownTarget = mousedown.target as Node + const isBound = !binding || !binding.instance + const isTargetExists = !mouseUpTarget || !mouseDownTarget + const isContainedByEl = el.contains(mouseUpTarget) || el.contains(mouseDownTarget) + const isSelf = el === mouseUpTarget + + const isTargetExcluded = + (excludes.length && excludes.some((item) => item?.contains(mouseUpTarget))) || + (excludes.length && excludes.includes(mouseDownTarget as HTMLElement)) + const isContainedByPopper = popperRef && (popperRef.contains(mouseUpTarget) || popperRef.contains(mouseDownTarget)) + if (isBound || isTargetExists || isContainedByEl || isSelf || isTargetExcluded || isContainedByPopper) { + return + } + binding.value() + } +} + +const ClickOutside: ObjectDirective = { + beforeMount(el, binding) { + nodeList.set(el, { + documentHandler: createDocumentHandler(el, binding), + bindingFn: binding.value + }) + }, + updated(el, binding) { + nodeList.set(el, { + documentHandler: createDocumentHandler(el, binding), + bindingFn: binding.value + }) + }, + unmounted(el) { + nodeList.delete(el) + } +} + +export default ClickOutside diff --git a/src/directives/index.ts b/src/directives/index.ts new file mode 100644 index 00000000..786b06ca --- /dev/null +++ b/src/directives/index.ts @@ -0,0 +1,11 @@ +/** + * Configure and register global directives + */ +import type { App } from 'vue' +import { setupPermissionDirective } from './permission' +import { setupLoadingDirective } from './loading' + +export function setupGlobDirectives(app: App) { + setupPermissionDirective(app) + setupLoadingDirective(app) +} diff --git a/src/directives/loading.ts b/src/directives/loading.ts new file mode 100644 index 00000000..1b06e3f6 --- /dev/null +++ b/src/directives/loading.ts @@ -0,0 +1,39 @@ +import { createLoading } from '@/components/Loading' +import type { Directive, App } from 'vue' + +const loadingDirective: Directive = { + mounted(el, binding) { + const tip = el.getAttribute('loading-tip') + const background = el.getAttribute('loading-background') + const size = el.getAttribute('loading-size') + const fullscreen = !!binding.modifiers.fullscreen + const instance = createLoading( + { + tip, + background, + size: size || 'large', + loading: !!binding.value, + absolute: !fullscreen + }, + fullscreen ? document.body : el + ) + el.instance = instance + }, + updated(el, binding) { + const instance = el.instance + if (!instance) return + instance.setTip(el.getAttribute('loading-tip')) + if (binding.oldValue !== binding.value) { + instance.setLoading?.(binding.value && !instance.loading) + } + }, + unmounted(el) { + el?.instance?.close() + } +} + +export function setupLoadingDirective(app: App) { + app.directive('loading', loadingDirective) +} + +export default loadingDirective diff --git a/src/directives/permission.ts b/src/directives/permission.ts new file mode 100644 index 00000000..f53ba3b7 --- /dev/null +++ b/src/directives/permission.ts @@ -0,0 +1,32 @@ +/** + * Global authority directive + * Used for fine-grained control of component permissions + * @Example v-auth="RoleEnum.TEST" + */ +import type { App, Directive, DirectiveBinding } from 'vue' + +import { usePermission } from '@/hooks/web/usePermission' + +function isAuth(el: Element, binding: any) { + const { hasPermission } = usePermission() + + const value = binding.value + if (!value) return + if (!hasPermission(value)) { + el.parentNode?.removeChild(el) + } +} + +const mounted = (el: Element, binding: DirectiveBinding) => { + isAuth(el, binding) +} + +const authDirective: Directive = { + mounted +} + +export function setupPermissionDirective(app: App) { + app.directive('auth', authDirective) +} + +export default authDirective diff --git a/src/directives/repeatClick.ts b/src/directives/repeatClick.ts new file mode 100644 index 00000000..f95450e0 --- /dev/null +++ b/src/directives/repeatClick.ts @@ -0,0 +1,31 @@ +/** + * Prevent repeated clicks + * @Example v-repeat-click="()=>{}" + */ +import { on, once } from '@/utils/domUtils' +import type { Directive, DirectiveBinding } from 'vue' + +const repeatDirective: Directive = { + beforeMount(el: Element, binding: DirectiveBinding) { + let interval: Nullable = null + let startTime = 0 + const handler = (): void => binding?.value() + const clear = (): void => { + if (Date.now() - startTime < 100) { + handler() + } + interval && clearInterval(interval) + interval = null + } + + on(el, 'mousedown', (e: MouseEvent): void => { + if ((e as any).button !== 0) return + startTime = Date.now() + once(document as any, 'mouseup', clear) + interval && clearInterval(interval) + interval = setInterval(handler, 100) + }) + } +} + +export default repeatDirective diff --git a/src/directives/ripple/index.less b/src/directives/ripple/index.less new file mode 100644 index 00000000..9c0718ed --- /dev/null +++ b/src/directives/ripple/index.less @@ -0,0 +1,21 @@ +.ripple-container { + position: absolute; + top: 0; + left: 0; + width: 0; + height: 0; + overflow: hidden; + pointer-events: none; +} + +.ripple-effect { + position: relative; + z-index: 9999; + width: 1px; + height: 1px; + margin-top: 0; + margin-left: 0; + pointer-events: none; + border-radius: 50%; + transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1); +} diff --git a/src/directives/ripple/index.ts b/src/directives/ripple/index.ts new file mode 100644 index 00000000..b85e6ce4 --- /dev/null +++ b/src/directives/ripple/index.ts @@ -0,0 +1,180 @@ +import type { Directive } from 'vue' +import './index.less' +export interface RippleOptions { + event: string + transition: number +} + +export interface RippleProto { + background?: string + zIndex?: string +} + +export type EventType = Event & MouseEvent & TouchEvent + +const options: RippleOptions = { + event: 'mousedown', + transition: 400 +} + +const RippleDirective: Directive & RippleProto = { + beforeMount: (el: HTMLElement, binding) => { + if (binding.value === false) return + + const bg = el.getAttribute('ripple-background') + setProps(Object.keys(binding.modifiers), options) + + const background = bg || RippleDirective.background + const zIndex = RippleDirective.zIndex + + el.addEventListener(options.event, (event: EventType) => { + rippler({ + event, + el, + background, + zIndex + }) + }) + }, + updated(el, binding) { + if (!binding.value) { + el?.clearRipple?.() + return + } + const bg = el.getAttribute('ripple-background') + el?.setBackground?.(bg) + } +} + +function rippler({ event, el, zIndex, background }: { event: EventType; el: HTMLElement } & RippleProto) { + const targetBorder = parseInt(getComputedStyle(el).borderWidth.replace('px', '')) + const clientX = event.clientX || event.touches[0].clientX + const clientY = event.clientY || event.touches[0].clientY + + const rect = el.getBoundingClientRect() + const { left, top } = rect + const { offsetWidth: width, offsetHeight: height } = el + const { transition } = options + const dx = clientX - left + const dy = clientY - top + const maxX = Math.max(dx, width - dx) + const maxY = Math.max(dy, height - dy) + const style = window.getComputedStyle(el) + const radius = Math.sqrt(maxX * maxX + maxY * maxY) + const border = targetBorder > 0 ? targetBorder : 0 + + const ripple = document.createElement('div') + const rippleContainer = document.createElement('div') + + // Styles for ripple + ripple.className = 'ripple' + + Object.assign(ripple.style ?? {}, { + marginTop: '0px', + marginLeft: '0px', + width: '1px', + height: '1px', + transition: `all ${transition}ms cubic-bezier(0.4, 0, 0.2, 1)`, + borderRadius: '50%', + pointerEvents: 'none', + position: 'relative', + zIndex: zIndex ?? '9999', + backgroundColor: background ?? 'rgba(0, 0, 0, 0.12)' + }) + + // Styles for rippleContainer + rippleContainer.className = 'ripple-container' + Object.assign(rippleContainer.style ?? {}, { + position: 'absolute', + left: `${0 - border}px`, + top: `${0 - border}px`, + height: '0', + width: '0', + pointerEvents: 'none', + overflow: 'hidden' + }) + + const storedTargetPosition = el.style.position.length > 0 ? el.style.position : getComputedStyle(el).position + + if (storedTargetPosition !== 'relative') { + el.style.position = 'relative' + } + + rippleContainer.appendChild(ripple) + el.appendChild(rippleContainer) + + Object.assign(ripple.style, { + marginTop: `${dy}px`, + marginLeft: `${dx}px` + }) + + const { borderTopLeftRadius, borderTopRightRadius, borderBottomLeftRadius, borderBottomRightRadius } = style + Object.assign(rippleContainer.style, { + width: `${width}px`, + height: `${height}px`, + direction: 'ltr', + borderTopLeftRadius, + borderTopRightRadius, + borderBottomLeftRadius, + borderBottomRightRadius + }) + + setTimeout(() => { + const wh = `${radius * 2}px` + Object.assign(ripple.style ?? {}, { + width: wh, + height: wh, + marginLeft: `${dx - radius}px`, + marginTop: `${dy - radius}px` + }) + }, 0) + + function clearRipple() { + setTimeout(() => { + ripple.style.backgroundColor = 'rgba(0, 0, 0, 0)' + }, 250) + + setTimeout(() => { + rippleContainer?.parentNode?.removeChild(rippleContainer) + }, 850) + el.removeEventListener('mouseup', clearRipple, false) + el.removeEventListener('mouseleave', clearRipple, false) + el.removeEventListener('dragstart', clearRipple, false) + setTimeout(() => { + let clearPosition = true + for (let i = 0; i < el.childNodes.length; i++) { + if ((el.childNodes[i] as Recordable).className === 'ripple-container') { + clearPosition = false + } + } + + if (clearPosition) { + el.style.position = storedTargetPosition !== 'static' ? storedTargetPosition : '' + } + }, options.transition + 260) + } + + if (event.type === 'mousedown') { + el.addEventListener('mouseup', clearRipple, false) + el.addEventListener('mouseleave', clearRipple, false) + el.addEventListener('dragstart', clearRipple, false) + } else { + clearRipple() + } + + ;(el as Recordable).setBackground = (bgColor: string) => { + if (!bgColor) { + return + } + ripple.style.backgroundColor = bgColor + } +} + +function setProps(modifiers: Recordable, props: Recordable) { + modifiers.forEach((item: Recordable) => { + if (isNaN(Number(item))) props.event = item + else props.transition = item + }) +} + +export default RippleDirective diff --git a/src/enums/appEnum.ts b/src/enums/appEnum.ts new file mode 100644 index 00000000..2b837ecd --- /dev/null +++ b/src/enums/appEnum.ts @@ -0,0 +1,52 @@ +export const SIDE_BAR_MINI_WIDTH = 48 +export const SIDE_BAR_SHOW_TIT_MINI_WIDTH = 80 + +export enum ContentEnum { + // auto width + FULL = 'full', + // fixed width + FIXED = 'fixed' +} + +// menu theme enum +export enum ThemeEnum { + DARK = 'dark', + LIGHT = 'light' +} + +export enum SettingButtonPositionEnum { + AUTO = 'auto', + HEADER = 'header', + FIXED = 'fixed' +} + +export enum SessionTimeoutProcessingEnum { + ROUTE_JUMP, + PAGE_COVERAGE +} + +/** + * 权限模式 + */ +export enum PermissionModeEnum { + // role + // 角色权限 + ROLE = 'ROLE', + // black + // 后端 + BACK = 'BACK', + // route mapping + // 路由映射 + ROUTE_MAPPING = 'ROUTE_MAPPING' +} + +// Route switching animation +// 路由切换动画 +export enum RouterTransitionEnum { + ZOOM_FADE = 'zoom-fade', + ZOOM_OUT = 'zoom-out', + FADE_SIDE = 'fade-slide', + FADE = 'fade', + FADE_BOTTOM = 'fade-bottom', + FADE_SCALE = 'fade-scale' +} diff --git a/src/enums/breakpointEnum.ts b/src/enums/breakpointEnum.ts new file mode 100644 index 00000000..128abb75 --- /dev/null +++ b/src/enums/breakpointEnum.ts @@ -0,0 +1,28 @@ +export enum sizeEnum { + XS = 'XS', + SM = 'SM', + MD = 'MD', + LG = 'LG', + XL = 'XL', + XXL = 'XXL' +} + +export enum screenEnum { + XS = 480, + SM = 576, + MD = 768, + LG = 992, + XL = 1200, + XXL = 1600 +} + +const screenMap = new Map() + +screenMap.set(sizeEnum.XS, screenEnum.XS) +screenMap.set(sizeEnum.SM, screenEnum.SM) +screenMap.set(sizeEnum.MD, screenEnum.MD) +screenMap.set(sizeEnum.LG, screenEnum.LG) +screenMap.set(sizeEnum.XL, screenEnum.XL) +screenMap.set(sizeEnum.XXL, screenEnum.XXL) + +export { screenMap } diff --git a/src/enums/cacheEnum.ts b/src/enums/cacheEnum.ts new file mode 100644 index 00000000..5c8d8045 --- /dev/null +++ b/src/enums/cacheEnum.ts @@ -0,0 +1,31 @@ +// token key +export const TOKEN_KEY = 'TOKEN__' + +export const LOCALE_KEY = 'LOCALE__' + +// user info key +export const USER_INFO_KEY = 'USER__INFO__' + +// role info key +export const ROLES_KEY = 'ROLES__KEY__' + +// project config key +export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__' + +// lock info +export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__' + +export const MULTIPLE_TABS_KEY = 'MULTIPLE_TABS__KEY__' + +export const APP_DARK_MODE_KEY_ = '__APP__DARK__MODE__' + +// base global local key +export const APP_LOCAL_CACHE_KEY = 'COMMON__LOCAL__KEY__' + +// base global session key +export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__' + +export enum CacheTypeEnum { + SESSION, + LOCAL +} diff --git a/src/enums/exceptionEnum.ts b/src/enums/exceptionEnum.ts new file mode 100644 index 00000000..83d76d4d --- /dev/null +++ b/src/enums/exceptionEnum.ts @@ -0,0 +1,27 @@ +/** + * @description: Exception related enumeration + */ +export enum ExceptionEnum { + // page not access + PAGE_NOT_ACCESS = 403, + + // page not found + PAGE_NOT_FOUND = 404, + + // error + ERROR = 500, + + // net work error + NET_WORK_ERROR = 10000, + + // No data on the page. In fact, it is not an exception page + PAGE_NOT_DATA = 10100 +} + +export enum ErrorTypeEnum { + VUE = 'vue', + SCRIPT = 'script', + RESOURCE = 'resource', + AJAX = 'ajax', + PROMISE = 'promise' +} diff --git a/src/enums/httpEnum.ts b/src/enums/httpEnum.ts new file mode 100644 index 00000000..2905f0f4 --- /dev/null +++ b/src/enums/httpEnum.ts @@ -0,0 +1,31 @@ +/** + * @description: Request result set + */ +export enum ResultEnum { + SUCCESS = 0, + ERROR = -1, + TIMEOUT = 401, + TYPE = 'success' +} + +/** + * @description: request method + */ +export enum RequestEnum { + GET = 'GET', + POST = 'POST', + PUT = 'PUT', + DELETE = 'DELETE' +} + +/** + * @description: contentType + */ +export enum ContentTypeEnum { + // json + JSON = 'application/json;charset=UTF-8', + // form-data qs + FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', + // form-data upload + FORM_DATA = 'multipart/form-data;charset=UTF-8' +} diff --git a/src/enums/menuEnum.ts b/src/enums/menuEnum.ts new file mode 100644 index 00000000..9ea74dd5 --- /dev/null +++ b/src/enums/menuEnum.ts @@ -0,0 +1,50 @@ +/** + * @description: menu type + */ +export enum MenuTypeEnum { + // left menu + SIDEBAR = 'sidebar', + + MIX_SIDEBAR = 'mix-sidebar', + // mixin menu + MIX = 'mix', + // top menu + TOP_MENU = 'top-menu' +} + +// 折叠触发器位置 +export enum TriggerEnum { + // 不显示 + NONE = 'NONE', + // 菜单底部 + FOOTER = 'FOOTER', + // 头部 + HEADER = 'HEADER' +} + +export type Mode = 'vertical' | 'vertical-right' | 'horizontal' | 'inline' + +// menu mode +export enum MenuModeEnum { + VERTICAL = 'vertical', + HORIZONTAL = 'horizontal', + VERTICAL_RIGHT = 'vertical-right', + INLINE = 'inline' +} + +export enum MenuSplitTyeEnum { + NONE, + TOP, + LEFT +} + +export enum TopMenuAlignEnum { + CENTER = 'center', + START = 'start', + END = 'end' +} + +export enum MixSidebarTriggerEnum { + HOVER = 'hover', + CLICK = 'click' +} diff --git a/src/enums/pageEnum.ts b/src/enums/pageEnum.ts new file mode 100644 index 00000000..d329ec4a --- /dev/null +++ b/src/enums/pageEnum.ts @@ -0,0 +1,10 @@ +export enum PageEnum { + // basic login path + BASE_LOGIN = '/login', + // basic home path + BASE_HOME = '/dashboard', + // error page path + ERROR_PAGE = '/exception', + // error log page path + ERROR_LOG_PAGE = '/error-log/list' +} diff --git a/src/enums/roleEnum.ts b/src/enums/roleEnum.ts new file mode 100644 index 00000000..a2c60e0c --- /dev/null +++ b/src/enums/roleEnum.ts @@ -0,0 +1,7 @@ +export enum RoleEnum { + // super admin + SUPER = 'super', + + // tester + TEST = 'test' +} diff --git a/src/enums/sizeEnum.ts b/src/enums/sizeEnum.ts new file mode 100644 index 00000000..35dd9134 --- /dev/null +++ b/src/enums/sizeEnum.ts @@ -0,0 +1,19 @@ +export enum SizeEnum { + DEFAULT = 'default', + SMALL = 'small', + LARGE = 'large' +} + +export enum SizeNumberEnum { + DEFAULT = 48, + SMALL = 16, + LARGE = 64 +} + +export const sizeMap: Map = (() => { + const map = new Map() + map.set(SizeEnum.DEFAULT, SizeNumberEnum.DEFAULT) + map.set(SizeEnum.SMALL, SizeNumberEnum.SMALL) + map.set(SizeEnum.LARGE, SizeNumberEnum.LARGE) + return map +})() diff --git a/src/hooks/component/useFormItem.ts b/src/hooks/component/useFormItem.ts new file mode 100644 index 00000000..fe4723bf --- /dev/null +++ b/src/hooks/component/useFormItem.ts @@ -0,0 +1,46 @@ +import type { UnwrapRef, Ref, WritableComputedRef, DeepReadonly } from 'vue' +import { reactive, readonly, computed, getCurrentInstance, watchEffect, unref, toRaw } from 'vue' + +import { isEqual } from 'lodash-es' + +export function useRuleFormItem>( + props: T, + key?: K, + changeEvent?, + emitData?: Ref +): [WritableComputedRef, (val: V) => void, DeepReadonly] + +export function useRuleFormItem(props: T, key: keyof T = 'value', changeEvent = 'change', emitData?: Ref) { + const instance = getCurrentInstance() + const emit = instance?.emit + + const innerState = reactive({ + value: props[key] + }) + + const defaultState = readonly(innerState) + + const setState = (val: UnwrapRef): void => { + innerState.value = val as T[keyof T] + } + + watchEffect(() => { + innerState.value = props[key] + }) + + const state: any = computed({ + get() { + return innerState.value + }, + set(value) { + if (isEqual(value, defaultState.value)) return + + innerState.value = value as T[keyof T] + setTimeout(() => { + emit?.(changeEvent, value, ...(toRaw(unref(emitData)) || [])) + }) + } + }) + + return [state, setState, defaultState] +} diff --git a/src/hooks/component/usePageContext.ts b/src/hooks/component/usePageContext.ts new file mode 100644 index 00000000..5ea4c5fb --- /dev/null +++ b/src/hooks/component/usePageContext.ts @@ -0,0 +1,18 @@ +import type { InjectionKey, ComputedRef, Ref } from 'vue' +import { createContext, useContext } from '@/hooks/core/useContext' + +export interface PageContextProps { + contentHeight: ComputedRef + pageHeight: Ref + setPageHeight: (height: number) => Promise +} + +const key: InjectionKey = Symbol() + +export function createPageContext(context: PageContextProps) { + return createContext(context, key, { native: true }) +} + +export function usePageContext() { + return useContext(key) +} diff --git a/src/hooks/core/onMountedOrActivated.ts b/src/hooks/core/onMountedOrActivated.ts new file mode 100644 index 00000000..2c92181a --- /dev/null +++ b/src/hooks/core/onMountedOrActivated.ts @@ -0,0 +1,18 @@ +import { nextTick, onMounted, onActivated } from 'vue' + +export function onMountedOrActivated(hook: Fn) { + let mounted: boolean + + onMounted(() => { + hook() + nextTick(() => { + mounted = true + }) + }) + + onActivated(() => { + if (mounted) { + hook() + } + }) +} diff --git a/src/hooks/core/useAttrs.ts b/src/hooks/core/useAttrs.ts new file mode 100644 index 00000000..1bd35095 --- /dev/null +++ b/src/hooks/core/useAttrs.ts @@ -0,0 +1,40 @@ +import { getCurrentInstance, reactive, shallowRef, watchEffect } from 'vue' +import type { Ref } from 'vue' +interface Params { + excludeListeners?: boolean + excludeKeys?: string[] + excludeDefaultKeys?: boolean +} + +const DEFAULT_EXCLUDE_KEYS = ['class', 'style'] +const LISTENER_PREFIX = /^on[A-Z]/ + +export function entries(obj: Recordable): [string, T][] { + return Object.keys(obj).map((key: string) => [key, obj[key]]) +} + +export function useAttrs(params: Params = {}): Ref | {} { + const instance = getCurrentInstance() + if (!instance) return {} + + const { excludeListeners = false, excludeKeys = [], excludeDefaultKeys = true } = params + const attrs = shallowRef({}) + const allExcludeKeys = excludeKeys.concat(excludeDefaultKeys ? DEFAULT_EXCLUDE_KEYS : []) + + // Since attrs are not reactive, make it reactive instead of doing in `onUpdated` hook for better performance + instance.attrs = reactive(instance.attrs) + + watchEffect(() => { + const res = entries(instance.attrs).reduce((acm, [key, val]) => { + if (!allExcludeKeys.includes(key) && !(excludeListeners && LISTENER_PREFIX.test(key))) { + acm[key] = val + } + + return acm + }, {} as Recordable) + + attrs.value = res + }) + + return attrs +} diff --git a/src/hooks/core/useContext.ts b/src/hooks/core/useContext.ts new file mode 100644 index 00000000..e0c69304 --- /dev/null +++ b/src/hooks/core/useContext.ts @@ -0,0 +1,29 @@ +import { InjectionKey, provide, inject, reactive, readonly as defineReadonly, UnwrapRef } from 'vue' + +export interface CreateContextOptions { + readonly?: boolean + createProvider?: boolean + native?: boolean +} + +type ShallowUnwrap = { + [P in keyof T]: UnwrapRef +} + +export function createContext(context: any, key: InjectionKey = Symbol(), options: CreateContextOptions = {}) { + const { readonly = true, createProvider = false, native = false } = options + + const state = reactive(context) + const provideData = readonly ? defineReadonly(state) : state + !createProvider && provide(key, native ? context : provideData) + + return { + state + } +} + +export function useContext(key: InjectionKey, native?: boolean): T + +export function useContext(key: InjectionKey = Symbol(), defaultValue?: any): ShallowUnwrap { + return inject(key, defaultValue || {}) +} diff --git a/src/hooks/core/useLockFn.ts b/src/hooks/core/useLockFn.ts new file mode 100644 index 00000000..2b4236fd --- /dev/null +++ b/src/hooks/core/useLockFn.ts @@ -0,0 +1,17 @@ +import { ref, unref } from 'vue' + +export function useLockFn

(fn: (...args: P) => Promise) { + const lockRef = ref(false) + return async function (...args: P) { + if (unref(lockRef)) return + lockRef.value = true + try { + const ret = await fn(...args) + lockRef.value = false + return ret + } catch (e) { + lockRef.value = false + throw e + } + } +} diff --git a/src/hooks/core/useRefs.ts b/src/hooks/core/useRefs.ts new file mode 100644 index 00000000..6ffb3513 --- /dev/null +++ b/src/hooks/core/useRefs.ts @@ -0,0 +1,16 @@ +import type { Ref } from 'vue' +import { ref, onBeforeUpdate } from 'vue' + +export function useRefs(): [Ref, (index: number) => (el: HTMLElement) => void] { + const refs = ref([]) as Ref + + onBeforeUpdate(() => { + refs.value = [] + }) + + const setRefs = (index: number) => (el: HTMLElement) => { + refs.value[index] = el + } + + return [refs, setRefs] +} diff --git a/src/hooks/core/useTimeout.ts b/src/hooks/core/useTimeout.ts new file mode 100644 index 00000000..e7b386cf --- /dev/null +++ b/src/hooks/core/useTimeout.ts @@ -0,0 +1,45 @@ +import { ref, watch } from 'vue' +import { tryOnUnmounted } from '@vueuse/core' +import { isFunction } from '@/utils/is' + +export function useTimeoutFn(handle: Fn, wait: number, native = false) { + if (!isFunction(handle)) { + throw new Error('handle is not Function!') + } + + const { readyRef, stop, start } = useTimeoutRef(wait) + if (native) { + handle() + } else { + watch( + readyRef, + (maturity) => { + maturity && handle() + }, + { immediate: false } + ) + } + return { readyRef, stop, start } +} + +export function useTimeoutRef(wait: number) { + const readyRef = ref(false) + + let timer: TimeoutHandle + function stop(): void { + readyRef.value = false + timer && window.clearTimeout(timer) + } + function start(): void { + stop() + timer = setTimeout(() => { + readyRef.value = true + }, wait) + } + + start() + + tryOnUnmounted(stop) + + return { readyRef, stop, start } +} diff --git a/src/hooks/event/useBreakpoint.ts b/src/hooks/event/useBreakpoint.ts new file mode 100644 index 00000000..20184970 --- /dev/null +++ b/src/hooks/event/useBreakpoint.ts @@ -0,0 +1,93 @@ +import { ref, computed, ComputedRef, unref } from 'vue' +import { useEventListener } from '@/hooks/event/useEventListener' +import { screenMap, sizeEnum, screenEnum } from '@/enums/breakpointEnum' + +// 可以用这个替换,优化项 +// import { Grid } from 'ant-design-vue'; +// const { useBreakpoint } = Grid; + +let globalScreenRef: ComputedRef +let globalWidthRef: ComputedRef +let globalRealWidthRef: ComputedRef + +export interface CreateCallbackParams { + screen: ComputedRef + width: ComputedRef + realWidth: ComputedRef + screenEnum: typeof screenEnum + screenMap: Map + sizeEnum: typeof sizeEnum +} + +export function useBreakpoint() { + return { + screenRef: computed(() => unref(globalScreenRef)), + widthRef: globalWidthRef, + screenEnum, + realWidthRef: globalRealWidthRef + } +} + +// Just call it once +export function createBreakpointListen(fn?: (opt: CreateCallbackParams) => void) { + const screenRef = ref(sizeEnum.XL) + const realWidthRef = ref(window.innerWidth) + + function getWindowWidth() { + const width = document.body.clientWidth + const xs = screenMap.get(sizeEnum.XS)! + const sm = screenMap.get(sizeEnum.SM)! + const md = screenMap.get(sizeEnum.MD)! + const lg = screenMap.get(sizeEnum.LG)! + const xl = screenMap.get(sizeEnum.XL)! + if (width < xs) { + screenRef.value = sizeEnum.XS + } else if (width < sm) { + screenRef.value = sizeEnum.SM + } else if (width < md) { + screenRef.value = sizeEnum.MD + } else if (width < lg) { + screenRef.value = sizeEnum.LG + } else if (width < xl) { + screenRef.value = sizeEnum.XL + } else { + screenRef.value = sizeEnum.XXL + } + realWidthRef.value = width + } + + useEventListener({ + el: window, + name: 'resize', + + listener: () => { + getWindowWidth() + resizeFn() + } + // wait: 100, + }) + + getWindowWidth() + globalScreenRef = computed(() => unref(screenRef)) + globalWidthRef = computed((): number => screenMap.get(unref(screenRef)!)!) + globalRealWidthRef = computed((): number => unref(realWidthRef)) + + function resizeFn() { + fn?.({ + screen: globalScreenRef, + width: globalWidthRef, + realWidth: globalRealWidthRef, + screenEnum, + screenMap, + sizeEnum + }) + } + + resizeFn() + return { + screenRef: globalScreenRef, + screenEnum, + widthRef: globalWidthRef, + realWidthRef: globalRealWidthRef + } +} diff --git a/src/hooks/event/useEventListener.ts b/src/hooks/event/useEventListener.ts new file mode 100644 index 00000000..44e9652b --- /dev/null +++ b/src/hooks/event/useEventListener.ts @@ -0,0 +1,58 @@ +import type { Ref } from 'vue' +import { ref, watch, unref } from 'vue' +import { useThrottleFn, useDebounceFn } from '@vueuse/core' + +export type RemoveEventFn = () => void +export interface UseEventParams { + el?: Element | Ref | Window | any + name: string + listener: EventListener + options?: boolean | AddEventListenerOptions + autoRemove?: boolean + isDebounce?: boolean + wait?: number +} +export function useEventListener({ + el = window, + name, + listener, + options, + autoRemove = true, + isDebounce = true, + wait = 80 +}: UseEventParams): { removeEvent: RemoveEventFn } { + /* eslint-disable-next-line */ + let remove: RemoveEventFn = () => {} + const isAddRef = ref(false) + + if (el) { + const element = ref(el as Element) as Ref + + const handler = isDebounce ? useDebounceFn(listener, wait) : useThrottleFn(listener, wait) + const realHandler = wait ? handler : listener + const removeEventListener = (e: Element) => { + isAddRef.value = true + e.removeEventListener(name, realHandler, options) + } + const addEventListener = (e: Element) => e.addEventListener(name, realHandler, options) + + const removeWatch = watch( + element, + (v, _ov, cleanUp) => { + if (v) { + !unref(isAddRef) && addEventListener(v) + cleanUp(() => { + autoRemove && removeEventListener(v) + }) + } + }, + { immediate: true } + ) + + remove = () => { + removeEventListener(element.value) + removeWatch() + } + } + return { removeEvent: remove } +} diff --git a/src/hooks/event/useIntersectionObserver.ts b/src/hooks/event/useIntersectionObserver.ts new file mode 100644 index 00000000..e0abb963 --- /dev/null +++ b/src/hooks/event/useIntersectionObserver.ts @@ -0,0 +1,42 @@ +import { Ref, watchEffect, ref } from 'vue' + +interface IntersectionObserverProps { + target: Ref + root?: Ref + onIntersect: IntersectionObserverCallback + rootMargin?: string + threshold?: number +} + +export function useIntersectionObserver({ target, root, onIntersect, rootMargin = '0px', threshold = 0.1 }: IntersectionObserverProps) { + let cleanup = () => {} + const observer: Ref> = ref(null) + const stopEffect = watchEffect(() => { + cleanup() + + observer.value = new IntersectionObserver(onIntersect, { + root: root ? root.value : null, + rootMargin, + threshold + }) + + const current = target.value + + current && observer.value.observe(current) + + cleanup = () => { + if (observer.value) { + observer.value.disconnect() + target.value && observer.value.unobserve(target.value) + } + } + }) + + return { + observer, + stop: () => { + cleanup() + stopEffect() + } + } +} diff --git a/src/hooks/event/useScroll.ts b/src/hooks/event/useScroll.ts new file mode 100644 index 00000000..ce95ea7c --- /dev/null +++ b/src/hooks/event/useScroll.ts @@ -0,0 +1,65 @@ +import type { Ref } from 'vue' + +import { ref, onMounted, watch, onUnmounted } from 'vue' +import { isWindow, isObject } from '@/utils/is' +import { useThrottleFn } from '@vueuse/core' + +export function useScroll( + refEl: Ref, + options?: { + wait?: number + leading?: boolean + trailing?: boolean + } +) { + const refX = ref(0) + const refY = ref(0) + let handler = () => { + if (isWindow(refEl.value)) { + refX.value = refEl.value.scrollX + refY.value = refEl.value.scrollY + } else if (refEl.value) { + refX.value = (refEl.value as Element).scrollLeft + refY.value = (refEl.value as Element).scrollTop + } + } + + if (isObject(options)) { + let wait = 0 + if (options.wait && options.wait > 0) { + wait = options.wait + Reflect.deleteProperty(options, 'wait') + } + + handler = useThrottleFn(handler, wait) + } + + let stopWatch: () => void + onMounted(() => { + stopWatch = watch( + refEl, + (el, prevEl, onCleanup) => { + if (el) { + el.addEventListener('scroll', handler) + } else if (prevEl) { + prevEl.removeEventListener('scroll', handler) + } + onCleanup(() => { + refX.value = refY.value = 0 + el && el.removeEventListener('scroll', handler) + }) + }, + { immediate: true } + ) + }) + + onUnmounted(() => { + refEl.value && refEl.value.removeEventListener('scroll', handler) + }) + + function stop() { + stopWatch && stopWatch() + } + + return { refX, refY, stop } +} diff --git a/src/hooks/event/useScrollTo.ts b/src/hooks/event/useScrollTo.ts new file mode 100644 index 00000000..df903774 --- /dev/null +++ b/src/hooks/event/useScrollTo.ts @@ -0,0 +1,59 @@ +import { isFunction, isUnDef } from '@/utils/is' +import { ref, unref } from 'vue' + +export interface ScrollToParams { + el: any + to: number + duration?: number + callback?: () => any +} + +const easeInOutQuad = (t: number, b: number, c: number, d: number) => { + t /= d / 2 + if (t < 1) { + return (c / 2) * t * t + b + } + t-- + return (-c / 2) * (t * (t - 2) - 1) + b +} +const move = (el: HTMLElement, amount: number) => { + el.scrollTop = amount +} + +const position = (el: HTMLElement) => { + return el.scrollTop +} +export function useScrollTo({ el, to, duration = 500, callback }: ScrollToParams) { + const isActiveRef = ref(false) + const start = position(el) + const change = to - start + const increment = 20 + let currentTime = 0 + duration = isUnDef(duration) ? 500 : duration + + const animateScroll = function () { + if (!unref(isActiveRef)) { + return + } + currentTime += increment + const val = easeInOutQuad(currentTime, start, change, duration) + move(el, val) + if (currentTime < duration && unref(isActiveRef)) { + requestAnimationFrame(animateScroll) + } else { + if (callback && isFunction(callback)) { + callback() + } + } + } + const run = () => { + isActiveRef.value = true + animateScroll() + } + + const stop = () => { + isActiveRef.value = false + } + + return { start: run, stop } +} diff --git a/src/hooks/event/useWindowSizeFn.ts b/src/hooks/event/useWindowSizeFn.ts new file mode 100644 index 00000000..14c437c7 --- /dev/null +++ b/src/hooks/event/useWindowSizeFn.ts @@ -0,0 +1,35 @@ +import { tryOnMounted, tryOnUnmounted, useDebounceFn } from '@vueuse/core' + +interface WindowSizeOptions { + once?: boolean + immediate?: boolean + listenerOptions?: AddEventListenerOptions | boolean +} + +export function useWindowSizeFn(fn: Fn, wait = 150, options?: WindowSizeOptions) { + let handler = () => { + fn() + } + const handleSize = useDebounceFn(handler, wait) + handler = handleSize + + const start = () => { + if (options && options.immediate) { + handler() + } + window.addEventListener('resize', handler) + } + + const stop = () => { + window.removeEventListener('resize', handler) + } + + tryOnMounted(() => { + start() + }) + + tryOnUnmounted(() => { + stop() + }) + return [start, stop] +} diff --git a/src/hooks/setting/index.ts b/src/hooks/setting/index.ts new file mode 100644 index 00000000..51fc4b86 --- /dev/null +++ b/src/hooks/setting/index.ts @@ -0,0 +1,25 @@ +import type { GlobConfig } from '@/types/config' + +import { warn } from '@/utils/log' +import { getAppEnvConfig } from '@/utils/env' + +export const useGlobSetting = (): Readonly => { + const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL, VITE_GLOB_APP_SHORT_NAME, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL } = + getAppEnvConfig() + + if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) { + warn( + `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.` + ) + } + + // Take global configuration + const glob: Readonly = { + title: VITE_GLOB_APP_TITLE, + apiUrl: VITE_GLOB_API_URL, + shortName: VITE_GLOB_APP_SHORT_NAME, + urlPrefix: VITE_GLOB_API_URL_PREFIX, + uploadUrl: VITE_GLOB_UPLOAD_URL + } + return glob as Readonly +} diff --git a/src/hooks/setting/useHeaderSetting.ts b/src/hooks/setting/useHeaderSetting.ts new file mode 100644 index 00000000..a65b503c --- /dev/null +++ b/src/hooks/setting/useHeaderSetting.ts @@ -0,0 +1,86 @@ +import type { HeaderSetting } from '@/types/config' + +import { computed, unref } from 'vue' + +import { useAppStore } from '@/store/modules/app' + +import { useMenuSetting } from '@/hooks/setting/useMenuSetting' +import { useRootSetting } from '@/hooks/setting/useRootSetting' +import { useFullContent } from '@/hooks/web/useFullContent' +import { MenuModeEnum } from '@/enums/menuEnum' + +export function useHeaderSetting() { + const { getFullContent } = useFullContent() + const appStore = useAppStore() + + const getShowFullHeaderRef = computed(() => { + return !unref(getFullContent) && unref(getShowMixHeaderRef) && unref(getShowHeader) && !unref(getIsTopMenu) && !unref(getIsMixSidebar) + }) + + const getUnFixedAndFull = computed(() => !unref(getFixed) && !unref(getShowFullHeaderRef)) + + const getShowInsetHeaderRef = computed(() => { + const need = !unref(getFullContent) && unref(getShowHeader) + return (need && !unref(getShowMixHeaderRef)) || (need && unref(getIsTopMenu)) || (need && unref(getIsMixSidebar)) + }) + + const { getMenuMode, getSplit, getShowHeaderTrigger, getIsSidebarType, getIsMixSidebar, getIsTopMenu } = useMenuSetting() + const { getShowBreadCrumb, getShowLogo } = useRootSetting() + + const getShowMixHeaderRef = computed(() => !unref(getIsSidebarType) && unref(getShowHeader)) + + const getShowDoc = computed(() => appStore.getHeaderSetting.showDoc) + + const getHeaderTheme = computed(() => appStore.getHeaderSetting.theme) + + const getShowHeader = computed(() => appStore.getHeaderSetting.show) + + const getFixed = computed(() => appStore.getHeaderSetting.fixed) + + const getHeaderBgColor = computed(() => appStore.getHeaderSetting.bgColor) + + const getShowSearch = computed(() => appStore.getHeaderSetting.showSearch) + + const getUseLockPage = computed(() => appStore.getHeaderSetting.useLockPage) + + const getShowFullScreen = computed(() => appStore.getHeaderSetting.showFullScreen) + + const getShowNotice = computed(() => appStore.getHeaderSetting.showNotice) + + const getShowBread = computed(() => { + return unref(getMenuMode) !== MenuModeEnum.HORIZONTAL && unref(getShowBreadCrumb) && !unref(getSplit) + }) + + const getShowHeaderLogo = computed(() => { + return unref(getShowLogo) && !unref(getIsSidebarType) && !unref(getIsMixSidebar) + }) + + const getShowContent = computed(() => { + return unref(getShowBread) || unref(getShowHeaderTrigger) + }) + + // Set header configuration + function setHeaderSetting(headerSetting: Partial) { + appStore.setProjectConfig({ headerSetting }) + } + return { + setHeaderSetting, + + getShowDoc, + getShowSearch, + getHeaderTheme, + getUseLockPage, + getShowFullScreen, + getShowNotice, + getShowBread, + getShowContent, + getShowHeaderLogo, + getShowHeader, + getFixed, + getShowMixHeaderRef, + getShowFullHeaderRef, + getShowInsetHeaderRef, + getUnFixedAndFull, + getHeaderBgColor + } +} diff --git a/src/hooks/setting/useMenuSetting.ts b/src/hooks/setting/useMenuSetting.ts new file mode 100644 index 00000000..bf8a2f8f --- /dev/null +++ b/src/hooks/setting/useMenuSetting.ts @@ -0,0 +1,155 @@ +import type { MenuSetting } from '@/types/config' + +import { computed, unref, ref } from 'vue' + +import { useAppStore } from '@/store/modules/app' + +import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '@/enums/appEnum' +import { MenuModeEnum, MenuTypeEnum, TriggerEnum } from '@/enums/menuEnum' +import { useFullContent } from '@/hooks/web/useFullContent' + +const mixSideHasChildren = ref(false) + +export function useMenuSetting() { + const { getFullContent: fullContent } = useFullContent() + const appStore = useAppStore() + + const getShowSidebar = computed(() => { + return unref(getSplit) || (unref(getShowMenu) && unref(getMenuMode) !== MenuModeEnum.HORIZONTAL && !unref(fullContent)) + }) + + const getCollapsed = computed(() => appStore.getMenuSetting.collapsed) + + const getMenuType = computed(() => appStore.getMenuSetting.type) + + const getMenuMode = computed(() => appStore.getMenuSetting.mode) + + const getMenuFixed = computed(() => appStore.getMenuSetting.fixed) + + const getShowMenu = computed(() => appStore.getMenuSetting.show) + + const getMenuHidden = computed(() => appStore.getMenuSetting.hidden) + + const getMenuWidth = computed(() => appStore.getMenuSetting.menuWidth) + + const getTrigger = computed(() => appStore.getMenuSetting.trigger) + + const getMenuTheme = computed(() => appStore.getMenuSetting.theme) + + const getSplit = computed(() => appStore.getMenuSetting.split) + + const getMenuBgColor = computed(() => appStore.getMenuSetting.bgColor) + + const getMixSideTrigger = computed(() => appStore.getMenuSetting.mixSideTrigger) + + const getCanDrag = computed(() => appStore.getMenuSetting.canDrag) + + const getAccordion = computed(() => appStore.getMenuSetting.accordion) + + const getMixSideFixed = computed(() => appStore.getMenuSetting.mixSideFixed) + + const getTopMenuAlign = computed(() => appStore.getMenuSetting.topMenuAlign) + + const getCloseMixSidebarOnChange = computed(() => appStore.getMenuSetting.closeMixSidebarOnChange) + + const getIsSidebarType = computed(() => unref(getMenuType) === MenuTypeEnum.SIDEBAR) + + const getIsTopMenu = computed(() => unref(getMenuType) === MenuTypeEnum.TOP_MENU) + + const getCollapsedShowTitle = computed(() => appStore.getMenuSetting.collapsedShowTitle) + + const getShowTopMenu = computed(() => { + return unref(getMenuMode) === MenuModeEnum.HORIZONTAL || unref(getSplit) + }) + + const getShowHeaderTrigger = computed(() => { + if (unref(getMenuType) === MenuTypeEnum.TOP_MENU || !unref(getShowMenu) || unref(getMenuHidden)) { + return false + } + + return unref(getTrigger) === TriggerEnum.HEADER + }) + + const getIsHorizontal = computed(() => { + return unref(getMenuMode) === MenuModeEnum.HORIZONTAL + }) + + const getIsMixSidebar = computed(() => { + return unref(getMenuType) === MenuTypeEnum.MIX_SIDEBAR + }) + + const getIsMixMode = computed(() => { + return unref(getMenuMode) === MenuModeEnum.INLINE && unref(getMenuType) === MenuTypeEnum.MIX + }) + + const getRealWidth = computed(() => { + if (unref(getIsMixSidebar)) { + return unref(getCollapsed) && !unref(getMixSideFixed) ? unref(getMiniWidthNumber) : unref(getMenuWidth) + } + return unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMenuWidth) + }) + + const getMiniWidthNumber = computed(() => { + const { collapsedShowTitle, siderHidden } = appStore.getMenuSetting + return siderHidden ? 0 : collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH + }) + + const getCalcContentWidth = computed(() => { + const width = + unref(getIsTopMenu) || !unref(getShowMenu) || (unref(getSplit) && unref(getMenuHidden)) + ? 0 + : unref(getIsMixSidebar) + ? (unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH) + + (unref(getMixSideFixed) && unref(mixSideHasChildren) ? unref(getRealWidth) : 0) + : unref(getRealWidth) + + return `calc(100% - ${unref(width)}px)` + }) + + // Set menu configuration + function setMenuSetting(menuSetting: Partial): void { + appStore.setProjectConfig({ menuSetting }) + } + + function toggleCollapsed() { + setMenuSetting({ + collapsed: !unref(getCollapsed) + }) + } + return { + setMenuSetting, + + toggleCollapsed, + + getMenuFixed, + getRealWidth, + getMenuType, + getMenuMode, + getShowMenu, + getCollapsed, + getMiniWidthNumber, + getCalcContentWidth, + getMenuWidth, + getTrigger, + getSplit, + getMenuTheme, + getCanDrag, + getCollapsedShowTitle, + getIsHorizontal, + getIsSidebarType, + getAccordion, + getShowTopMenu, + getShowHeaderTrigger, + getTopMenuAlign, + getMenuHidden, + getIsTopMenu, + getMenuBgColor, + getShowSidebar, + getIsMixMode, + getIsMixSidebar, + getCloseMixSidebarOnChange, + getMixSideTrigger, + getMixSideFixed, + mixSideHasChildren + } +} diff --git a/src/hooks/setting/useMultipleTabSetting.ts b/src/hooks/setting/useMultipleTabSetting.ts new file mode 100644 index 00000000..0a3225c3 --- /dev/null +++ b/src/hooks/setting/useMultipleTabSetting.ts @@ -0,0 +1,28 @@ +import type { MultiTabsSetting } from '@/types/config' + +import { computed } from 'vue' + +import { useAppStore } from '@/store/modules/app' + +export function useMultipleTabSetting() { + const appStore = useAppStore() + + const getShowMultipleTab = computed(() => appStore.getMultiTabsSetting.show) + + const getShowQuick = computed(() => appStore.getMultiTabsSetting.showQuick) + + const getShowRedo = computed(() => appStore.getMultiTabsSetting.showRedo) + + const getShowFold = computed(() => appStore.getMultiTabsSetting.showFold) + + function setMultipleTabSetting(multiTabsSetting: Partial) { + appStore.setProjectConfig({ multiTabsSetting }) + } + return { + setMultipleTabSetting, + getShowMultipleTab, + getShowQuick, + getShowRedo, + getShowFold + } +} diff --git a/src/hooks/setting/useRootSetting.ts b/src/hooks/setting/useRootSetting.ts new file mode 100644 index 00000000..2c4d165f --- /dev/null +++ b/src/hooks/setting/useRootSetting.ts @@ -0,0 +1,90 @@ +import type { ProjectConfig } from '@/types/config' + +import { computed } from 'vue' + +import { useAppStore } from '@/store/modules/app' +import { ContentEnum, ThemeEnum } from '@/enums/appEnum' + +type RootSetting = Omit + +export function useRootSetting() { + const appStore = useAppStore() + + const getPageLoading = computed(() => appStore.getPageLoading) + + const getOpenKeepAlive = computed(() => appStore.getProjectConfig.openKeepAlive) + + const getSettingButtonPosition = computed(() => appStore.getProjectConfig.settingButtonPosition) + + const getCanEmbedIFramePage = computed(() => appStore.getProjectConfig.canEmbedIFramePage) + + const getPermissionMode = computed(() => appStore.getProjectConfig.permissionMode) + + const getShowLogo = computed(() => appStore.getProjectConfig.showLogo) + + const getContentMode = computed(() => appStore.getProjectConfig.contentMode) + + const getUseOpenBackTop = computed(() => appStore.getProjectConfig.useOpenBackTop) + + const getShowSettingButton = computed(() => appStore.getProjectConfig.showSettingButton) + + const getUseErrorHandle = computed(() => appStore.getProjectConfig.useErrorHandle) + + const getShowFooter = computed(() => appStore.getProjectConfig.showFooter) + + const getShowBreadCrumb = computed(() => appStore.getProjectConfig.showBreadCrumb) + + const getThemeColor = computed(() => appStore.getProjectConfig.themeColor) + + const getShowBreadCrumbIcon = computed(() => appStore.getProjectConfig.showBreadCrumbIcon) + + const getFullContent = computed(() => appStore.getProjectConfig.fullContent) + + const getColorWeak = computed(() => appStore.getProjectConfig.colorWeak) + + const getGrayMode = computed(() => appStore.getProjectConfig.grayMode) + + const getLockTime = computed(() => appStore.getProjectConfig.lockTime) + + const getShowDarkModeToggle = computed(() => appStore.getProjectConfig.showDarkModeToggle) + + const getDarkMode = computed(() => appStore.getDarkMode) + + const getLayoutContentMode = computed(() => + appStore.getProjectConfig.contentMode === ContentEnum.FULL ? ContentEnum.FULL : ContentEnum.FIXED + ) + + function setRootSetting(setting: Partial) { + appStore.setProjectConfig(setting) + } + + function setDarkMode(mode: ThemeEnum) { + appStore.setDarkMode(mode) + } + return { + setRootSetting, + + getSettingButtonPosition, + getFullContent, + getColorWeak, + getGrayMode, + getLayoutContentMode, + getPageLoading, + getOpenKeepAlive, + getCanEmbedIFramePage, + getPermissionMode, + getShowLogo, + getUseErrorHandle, + getShowBreadCrumb, + getShowBreadCrumbIcon, + getUseOpenBackTop, + getShowSettingButton, + getShowFooter, + getContentMode, + getLockTime, + getThemeColor, + getDarkMode, + setDarkMode, + getShowDarkModeToggle + } +} diff --git a/src/hooks/setting/useTransitionSetting.ts b/src/hooks/setting/useTransitionSetting.ts new file mode 100644 index 00000000..a446c6af --- /dev/null +++ b/src/hooks/setting/useTransitionSetting.ts @@ -0,0 +1,31 @@ +import type { TransitionSetting } from '@/types/config' + +import { computed } from 'vue' + +import { useAppStore } from '@/store/modules/app' + +export function useTransitionSetting() { + const appStore = useAppStore() + + const getEnableTransition = computed(() => appStore.getTransitionSetting?.enable) + + const getOpenNProgress = computed(() => appStore.getTransitionSetting?.openNProgress) + + const getOpenPageLoading = computed((): boolean => { + return !!appStore.getTransitionSetting?.openPageLoading + }) + + const getBasicTransition = computed(() => appStore.getTransitionSetting?.basicTransition) + + function setTransitionSetting(transitionSetting: Partial) { + appStore.setProjectConfig({ transitionSetting }) + } + return { + setTransitionSetting, + + getEnableTransition, + getOpenNProgress, + getOpenPageLoading, + getBasicTransition + } +} diff --git a/src/hooks/web/useAppInject.ts b/src/hooks/web/useAppInject.ts new file mode 100644 index 00000000..f987451a --- /dev/null +++ b/src/hooks/web/useAppInject.ts @@ -0,0 +1,10 @@ +import { useAppProviderContext } from '@/components/Application' +import { computed, unref } from 'vue' + +export function useAppInject() { + const values = useAppProviderContext() + + return { + getIsMobile: computed(() => unref(values.isMobile)) + } +} diff --git a/src/hooks/web/useContentHeight.ts b/src/hooks/web/useContentHeight.ts new file mode 100644 index 00000000..dd89e903 --- /dev/null +++ b/src/hooks/web/useContentHeight.ts @@ -0,0 +1,183 @@ +import { ComputedRef, isRef, nextTick, Ref, ref, unref, watch } from 'vue' +import { onMountedOrActivated } from '@/hooks/core/onMountedOrActivated' +import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn' +import { useLayoutHeight } from '@/layouts/default/content/useContentViewHeight' +import { getViewportOffset } from '@/utils/domUtils' +import { isNumber, isString } from '@/utils/is' + +export interface CompensationHeight { + // 使用 layout Footer 高度作为判断补偿高度的条件 + useLayoutFooter: boolean + // refs HTMLElement + elements?: Ref[] +} + +type Upward = number | string | null | undefined + +/** + * 动态计算内容高度,根据锚点dom最下坐标到屏幕最下坐标,根据传入dom的高度、padding、margin等值进行动态计算 + * 最终获取合适的内容高度 + * + * @param flag 用于开启计算的响应式标识 + * @param anchorRef 锚点组件 Ref + * @param subtractHeightRefs 待减去高度的组件列表 Ref + * @param substractSpaceRefs 待减去空闲空间(margins/paddings)的组件列表 Ref + * @param offsetHeightRef 计算偏移的响应式高度,计算高度时将直接减去此值 + * @param upwardSpace 向上递归减去空闲空间的 层级 或 直到指定class为止 数值为2代表向上递归两次|数值为ant-layout表示向上递归直到碰见.ant-layout为止 + * @returns 响应式高度 + */ +export function useContentHeight( + flag: ComputedRef, + anchorRef: Ref, + subtractHeightRefs: Ref[], + substractSpaceRefs: Ref[], + upwardSpace: Ref | ComputedRef | Upward = 0, + offsetHeightRef: Ref = ref(0) +) { + const contentHeight: Ref> = ref(null) + const { footerHeightRef: layoutFooterHeightRef } = useLayoutHeight() + let compensationHeight: CompensationHeight = { + useLayoutFooter: true + } + + const setCompensation = (params: CompensationHeight) => { + compensationHeight = params + } + + function redoHeight() { + nextTick(() => { + calcContentHeight() + }) + } + + function calcSubtractSpace(element: Element | null | undefined, direction: 'all' | 'top' | 'bottom' = 'all'): number { + function numberPx(px: string) { + return Number(px.replace(/[^\d]/g, '')) + } + let subtractHeight = 0 + const ZERO_PX = '0px' + if (element) { + const cssStyle = getComputedStyle(element) + const marginTop = numberPx(cssStyle?.marginTop ?? ZERO_PX) + const marginBottom = numberPx(cssStyle?.marginBottom ?? ZERO_PX) + const paddingTop = numberPx(cssStyle?.paddingTop ?? ZERO_PX) + const paddingBottom = numberPx(cssStyle?.paddingBottom ?? ZERO_PX) + if (direction === 'all') { + subtractHeight += marginTop + subtractHeight += marginBottom + subtractHeight += paddingTop + subtractHeight += paddingBottom + } else if (direction === 'top') { + subtractHeight += marginTop + subtractHeight += paddingTop + } else { + subtractHeight += marginBottom + subtractHeight += paddingBottom + } + } + return subtractHeight + } + + function getEl(element: any): Nullable { + if (element == null) { + return null + } + return (element instanceof HTMLDivElement ? element : element.$el) as HTMLDivElement + } + + async function calcContentHeight() { + if (!flag.value) { + return + } + // Add a delay to get the correct height + await nextTick() + + const anchorEl = getEl(unref(anchorRef)) + if (!anchorEl) { + return + } + const { bottomIncludeBody } = getViewportOffset(anchorEl) + + // substract elements height + let substractHeight = 0 + subtractHeightRefs.forEach((item) => { + substractHeight += getEl(unref(item))?.offsetHeight ?? 0 + }) + + // subtract margins / paddings + let substractSpaceHeight = calcSubtractSpace(anchorEl) ?? 0 + substractSpaceRefs.forEach((item) => { + substractSpaceHeight += calcSubtractSpace(getEl(unref(item))) + }) + + // upwardSpace + let upwardSpaceHeight = 0 + function upward(element: Element | null, upwardLvlOrClass: number | string | null | undefined) { + if (element && upwardLvlOrClass) { + const parent = element.parentElement + if (parent) { + if (isString(upwardLvlOrClass)) { + if (!parent.classList.contains(upwardLvlOrClass)) { + upwardSpaceHeight += calcSubtractSpace(parent, 'bottom') + upward(parent, upwardLvlOrClass) + } else { + upwardSpaceHeight += calcSubtractSpace(parent, 'bottom') + } + } else if (isNumber(upwardLvlOrClass)) { + if (upwardLvlOrClass > 0) { + upwardSpaceHeight += calcSubtractSpace(parent, 'bottom') + upward(parent, --upwardLvlOrClass) + } + } + } + } + } + if (isRef(upwardSpace)) { + upward(anchorEl, unref(upwardSpace)) + } else { + upward(anchorEl, upwardSpace) + } + + let height = + bottomIncludeBody - unref(layoutFooterHeightRef) - unref(offsetHeightRef) - substractHeight - substractSpaceHeight - upwardSpaceHeight + + // compensation height + const calcCompensationHeight = () => { + compensationHeight.elements?.forEach((item) => { + height += getEl(unref(item))?.offsetHeight ?? 0 + }) + } + if (compensationHeight.useLayoutFooter && unref(layoutFooterHeightRef) > 0) { + calcCompensationHeight() + } else { + calcCompensationHeight() + } + + contentHeight.value = height + } + + onMountedOrActivated(() => { + nextTick(() => { + calcContentHeight() + }) + }) + useWindowSizeFn( + () => { + calcContentHeight() + }, + 50, + { immediate: true } + ) + watch( + () => [layoutFooterHeightRef.value], + () => { + calcContentHeight() + }, + { + flush: 'post', + immediate: true + } + ) + + return { redoHeight, setCompensation, contentHeight } +} diff --git a/src/hooks/web/useContextMenu.ts b/src/hooks/web/useContextMenu.ts new file mode 100644 index 00000000..7bf4cc05 --- /dev/null +++ b/src/hooks/web/useContextMenu.ts @@ -0,0 +1,12 @@ +import { onUnmounted, getCurrentInstance } from 'vue' +import { createContextMenu, destroyContextMenu } from '@/components/ContextMenu' +import type { ContextMenuItem } from '@/components/ContextMenu' +export type { ContextMenuItem } +export function useContextMenu(authRemove = true) { + if (getCurrentInstance() && authRemove) { + onUnmounted(() => { + destroyContextMenu() + }) + } + return [createContextMenu, destroyContextMenu] +} diff --git a/src/hooks/web/useCopyToClipboard.ts b/src/hooks/web/useCopyToClipboard.ts new file mode 100644 index 00000000..029fb320 --- /dev/null +++ b/src/hooks/web/useCopyToClipboard.ts @@ -0,0 +1,68 @@ +import { ref, watch } from 'vue' + +import { isDef } from '@/utils/is' +interface Options { + target?: HTMLElement +} +export function useCopyToClipboard(initial?: string) { + const clipboardRef = ref(initial || '') + const isSuccessRef = ref(false) + const copiedRef = ref(false) + + watch( + clipboardRef, + (str?: string) => { + if (isDef(str)) { + copiedRef.value = true + isSuccessRef.value = copyTextToClipboard(str) + } + }, + { immediate: !!initial, flush: 'sync' } + ) + + return { clipboardRef, isSuccessRef, copiedRef } +} + +export function copyTextToClipboard(input: string, { target = document.body }: Options = {}) { + const element = document.createElement('textarea') + const previouslyFocusedElement = document.activeElement + + element.value = input + + element.setAttribute('readonly', '') + ;(element.style as any).contain = 'strict' + element.style.position = 'absolute' + element.style.left = '-9999px' + element.style.fontSize = '12pt' + + const selection = document.getSelection() + let originalRange + if (selection && selection.rangeCount > 0) { + originalRange = selection.getRangeAt(0) + } + + target.append(element) + element.select() + + element.selectionStart = 0 + element.selectionEnd = input.length + + let isSuccess = false + try { + isSuccess = document.execCommand('copy') + } catch (e: any) { + throw new Error(e) + } + + element.remove() + + if (originalRange && selection) { + selection.removeAllRanges() + selection.addRange(originalRange) + } + + if (previouslyFocusedElement) { + ;(previouslyFocusedElement as HTMLElement).focus() + } + return isSuccess +} diff --git a/src/hooks/web/useDesign.ts b/src/hooks/web/useDesign.ts new file mode 100644 index 00000000..2db971b0 --- /dev/null +++ b/src/hooks/web/useDesign.ts @@ -0,0 +1,22 @@ +import { useAppProviderContext } from '@/components/Application' +// import { computed } from 'vue'; +// import { lowerFirst } from 'lodash-es'; +export function useDesign(scope: string) { + const values = useAppProviderContext() + // const $style = cssModule ? useCssModule() : {}; + + // const style: Record = {}; + // if (cssModule) { + // Object.keys($style).forEach((key) => { + // // const moduleCls = $style[key]; + // const k = key.replace(new RegExp(`^${values.prefixCls}-?`, 'ig'), ''); + // style[lowerFirst(k)] = $style[key]; + // }); + // } + return { + // prefixCls: computed(() => `${values.prefixCls}-${scope}`), + prefixCls: `${values.prefixCls}-${scope}`, + prefixVar: values.prefixCls + // style, + } +} diff --git a/src/hooks/web/useECharts.ts b/src/hooks/web/useECharts.ts new file mode 100644 index 00000000..c87959ea --- /dev/null +++ b/src/hooks/web/useECharts.ts @@ -0,0 +1,126 @@ +import type { EChartsOption } from 'echarts' +import type { Ref } from 'vue' +import { useTimeoutFn } from '@/hooks/core/useTimeout' +import { tryOnUnmounted } from '@vueuse/core' +import { unref, nextTick, watch, computed, ref } from 'vue' +import { useDebounceFn } from '@vueuse/core' +import { useEventListener } from '@/hooks/event/useEventListener' +import { useBreakpoint } from '@/hooks/event/useBreakpoint' +import echarts from '@/utils/lib/echarts' +import { useRootSetting } from '@/hooks/setting/useRootSetting' +import { useMenuSetting } from '@/hooks/setting/useMenuSetting' + +export function useECharts(elRef: Ref, theme: 'light' | 'dark' | 'default' = 'default') { + const { getDarkMode: getSysDarkMode } = useRootSetting() + const { getCollapsed } = useMenuSetting() + + const getDarkMode = computed(() => { + return theme === 'default' ? getSysDarkMode.value : theme + }) + let chartInstance: echarts.ECharts | null = null + let resizeFn: Fn = resize + const cacheOptions = ref({}) as Ref + let removeResizeFn: Fn = () => {} + + resizeFn = useDebounceFn(resize, 200) + + const getOptions = computed(() => { + if (getDarkMode.value !== 'dark') { + return cacheOptions.value as EChartsOption + } + return { + backgroundColor: 'transparent', + ...cacheOptions.value + } as EChartsOption + }) + + function initCharts(t = theme) { + const el = unref(elRef) + if (!el || !unref(el)) { + return + } + + chartInstance = echarts.init(el, t) + const { removeEvent } = useEventListener({ + el: window, + name: 'resize', + listener: resizeFn + }) + removeResizeFn = removeEvent + const { widthRef, screenEnum } = useBreakpoint() + if (unref(widthRef) <= screenEnum.MD || el.offsetHeight === 0) { + useTimeoutFn(() => { + resizeFn() + }, 30) + } + } + + function setOptions(options: EChartsOption, clear = true) { + cacheOptions.value = options + if (unref(elRef)?.offsetHeight === 0) { + useTimeoutFn(() => { + setOptions(unref(getOptions)) + }, 30) + return + } + nextTick(() => { + useTimeoutFn(() => { + if (!chartInstance) { + initCharts(getDarkMode.value as 'default') + + if (!chartInstance) return + } + clear && chartInstance?.clear() + + chartInstance?.setOption(unref(getOptions)) + }, 30) + }) + } + + function resize() { + chartInstance?.resize({ + animation: { + duration: 300, + easing: 'quadraticIn' + } + }) + } + + watch( + () => getDarkMode.value, + (theme) => { + if (chartInstance) { + chartInstance.dispose() + initCharts(theme as 'default') + setOptions(cacheOptions.value) + } + } + ) + + watch(getCollapsed, (_) => { + useTimeoutFn(() => { + resizeFn() + }, 300) + }) + + tryOnUnmounted(() => { + if (!chartInstance) return + removeResizeFn() + chartInstance.dispose() + chartInstance = null + }) + + function getInstance(): echarts.ECharts | null { + if (!chartInstance) { + initCharts(getDarkMode.value as 'default') + } + return chartInstance + } + + return { + setOptions, + resize, + echarts, + getInstance + } +} diff --git a/src/hooks/web/useFullContent.ts b/src/hooks/web/useFullContent.ts new file mode 100644 index 00000000..ca155376 --- /dev/null +++ b/src/hooks/web/useFullContent.ts @@ -0,0 +1,28 @@ +import { computed, unref } from 'vue' + +import { useAppStore } from '@/store/modules/app' + +import { useRouter } from 'vue-router' + +/** + * @description: Full screen display content + */ +export const useFullContent = () => { + const appStore = useAppStore() + const router = useRouter() + const { currentRoute } = router + + // Whether to display the content in full screen without displaying the menu + const getFullContent = computed(() => { + // Query parameters, the full screen is displayed when the address bar has a full parameter + const route = unref(currentRoute) + const query = route.query + if (query && Reflect.has(query, '__full__')) { + return true + } + // Return to the configuration in the configuration file + return appStore.getProjectConfig.fullContent + }) + + return { getFullContent } +} diff --git a/src/hooks/web/useI18n.ts b/src/hooks/web/useI18n.ts new file mode 100644 index 00000000..bb671cb0 --- /dev/null +++ b/src/hooks/web/useI18n.ts @@ -0,0 +1,55 @@ +import { i18n } from '@/locales/setupI18n' + +type I18nGlobalTranslation = { + (key: string): string + (key: string, locale: string): string + (key: string, locale: string, list: unknown[]): string + (key: string, locale: string, named: Record): string + (key: string, list: unknown[]): string + (key: string, named: Record): string +} + +type I18nTranslationRestParameters = [string, any] + +function getKey(namespace: string | undefined, key: string) { + if (!namespace) { + return key + } + if (key.startsWith(namespace)) { + return key + } + return `${namespace}.${key}` +} + +export function useI18n(namespace?: string): { + t: I18nGlobalTranslation +} { + const normalFn = { + t: (key: string) => { + return getKey(namespace, key) + } + } + + if (!i18n) { + return normalFn + } + + const { t, ...methods } = i18n.global + + const tFn: I18nGlobalTranslation = (key: string, ...arg: any[]) => { + if (!key) return '' + if (!key.includes('.') && !namespace) return key + return t(getKey(namespace, key), ...(arg as I18nTranslationRestParameters)) + } + return { + ...methods, + t: tFn + } +} + +// Why write this function? +// Mainly to configure the vscode i18nn ally plugin. This function is only used for routing and menus. Please use useI18n for other places + +// 为什么要编写此函数? +// 主要用于配合vscode i18nn ally插件。此功能仅用于路由和菜单。请在其他地方使用useI18n +export const t = (key: string) => key diff --git a/src/hooks/web/useLockPage.ts b/src/hooks/web/useLockPage.ts new file mode 100644 index 00000000..afdde64c --- /dev/null +++ b/src/hooks/web/useLockPage.ts @@ -0,0 +1,72 @@ +import { computed, onUnmounted, unref, watchEffect } from 'vue' +import { useThrottleFn } from '@vueuse/core' + +import { useAppStore } from '@/store/modules/app' +import { useLockStore } from '@/store/modules/lock' + +import { useUserStore } from '@/store/modules/user' +import { useRootSetting } from '../setting/useRootSetting' + +export function useLockPage() { + const { getLockTime } = useRootSetting() + const lockStore = useLockStore() + const userStore = useUserStore() + const appStore = useAppStore() + + let timeId: TimeoutHandle + + function clear(): void { + window.clearTimeout(timeId) + } + + function resetCalcLockTimeout(): void { + // not login + if (!userStore.getToken) { + clear() + return + } + const lockTime = appStore.getProjectConfig.lockTime + if (!lockTime || lockTime < 1) { + clear() + return + } + clear() + + timeId = setTimeout(() => { + lockPage() + }, lockTime * 60 * 1000) + } + + function lockPage(): void { + lockStore.setLockInfo({ + isLock: true, + pwd: undefined + }) + } + + watchEffect((onClean) => { + if (userStore.getToken) { + resetCalcLockTimeout() + } else { + clear() + } + onClean(() => { + clear() + }) + }) + + onUnmounted(() => { + clear() + }) + + const keyupFn = useThrottleFn(resetCalcLockTimeout, 2000) + + return computed(() => { + if (unref(getLockTime)) { + return { onKeyup: keyupFn, onMousemove: keyupFn } + } else { + clear() + return {} + } + }) +} diff --git a/src/hooks/web/useMessage.tsx b/src/hooks/web/useMessage.tsx new file mode 100644 index 00000000..2e39043a --- /dev/null +++ b/src/hooks/web/useMessage.tsx @@ -0,0 +1,123 @@ +import type { ModalFunc, ModalFuncProps } from 'ant-design-vue/lib/modal/Modal' + +import { Modal, message as Message, notification } from 'ant-design-vue' +import { InfoCircleFilled, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons-vue' + +import { NotificationArgsProps, ConfigProps } from 'ant-design-vue/lib/notification' +import { useI18n } from './useI18n' +import { isString } from '@/utils/is' + +export interface NotifyApi { + info(config: NotificationArgsProps): void + success(config: NotificationArgsProps): void + error(config: NotificationArgsProps): void + warn(config: NotificationArgsProps): void + warning(config: NotificationArgsProps): void + open(args: NotificationArgsProps): void + close(key: String): void + config(options: ConfigProps): void + destroy(): void +} + +export declare type NotificationPlacement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' +export declare type IconType = 'success' | 'info' | 'error' | 'warning' +export interface ModalOptionsEx extends Omit { + iconType: 'warning' | 'success' | 'error' | 'info' +} +export type ModalOptionsPartial = Partial & Pick + +interface ConfirmOptions { + info: ModalFunc + success: ModalFunc + error: ModalFunc + warn: ModalFunc + warning: ModalFunc +} + +function getIcon(iconType: string) { + if (iconType === 'warning') { + return + } else if (iconType === 'success') { + return + } else if (iconType === 'info') { + return + } else { + return + } +} + +function renderContent({ content }: Pick) { + if (isString(content)) { + return

${content as string}
`}> + } else { + return content + } +} + +/** + * @description: Create confirmation box + */ +function createConfirm(options: ModalOptionsEx): ConfirmOptions { + const iconType = options.iconType || 'warning' + Reflect.deleteProperty(options, 'iconType') + const opt: ModalFuncProps = { + centered: true, + icon: getIcon(iconType), + ...options, + content: renderContent(options) + } + return Modal.confirm(opt) as unknown as ConfirmOptions +} + +const getBaseOptions = () => { + const { t } = useI18n() + return { + okText: t('common.okText'), + centered: true + } +} + +function createModalOptions(options: ModalOptionsPartial, icon: string): ModalOptionsPartial { + return { + ...getBaseOptions(), + ...options, + content: renderContent(options), + icon: getIcon(icon) + } +} + +function createSuccessModal(options: ModalOptionsPartial) { + return Modal.success(createModalOptions(options, 'success')) +} + +function createErrorModal(options: ModalOptionsPartial) { + return Modal.error(createModalOptions(options, 'error')) +} + +function createInfoModal(options: ModalOptionsPartial) { + return Modal.info(createModalOptions(options, 'info')) +} + +function createWarningModal(options: ModalOptionsPartial) { + return Modal.warning(createModalOptions(options, 'warning')) +} + +notification.config({ + placement: 'topRight', + duration: 3 +}) + +/** + * @description: message + */ +export function useMessage() { + return { + createMessage: Message, + notification: notification as NotifyApi, + createConfirm: createConfirm, + createSuccessModal, + createErrorModal, + createInfoModal, + createWarningModal + } +} diff --git a/src/hooks/web/usePage.ts b/src/hooks/web/usePage.ts new file mode 100644 index 00000000..e9176fb4 --- /dev/null +++ b/src/hooks/web/usePage.ts @@ -0,0 +1,53 @@ +import type { RouteLocationRaw, Router } from 'vue-router' + +import { PageEnum } from '@/enums/pageEnum' +import { unref } from 'vue' + +import { useRouter } from 'vue-router' +import { REDIRECT_NAME } from '@/router/constant' + +export type PathAsPageEnum = T extends { path: string } ? T & { path: PageEnum } : T +export type RouteLocationRawEx = PathAsPageEnum + +function handleError(e: Error) { + console.error(e) +} + +/** + * page switch + */ +export function useGo(_router?: Router) { + const { push, replace } = _router || useRouter() + function go(opt: RouteLocationRawEx = PageEnum.BASE_HOME, isReplace = false) { + if (!opt) { + return + } + isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError) + } + return go +} + +/** + * @description: redo current page + */ +export const useRedo = (_router?: Router) => { + const { replace, currentRoute } = _router || useRouter() + const { query, params = {}, name, fullPath } = unref(currentRoute.value) + function redo(): Promise { + return new Promise((resolve) => { + if (name === REDIRECT_NAME) { + resolve(false) + return + } + if (name && Object.keys(params).length > 0) { + params['_redirect_type'] = 'name' + params['path'] = String(name) + } else { + params['_redirect_type'] = 'path' + params['path'] = fullPath + } + replace({ name: REDIRECT_NAME, params, query }).then(() => resolve(true)) + }) + } + return redo +} diff --git a/src/hooks/web/usePagination.ts b/src/hooks/web/usePagination.ts new file mode 100644 index 00000000..6b1311c5 --- /dev/null +++ b/src/hooks/web/usePagination.ts @@ -0,0 +1,31 @@ +import type { Ref } from 'vue' +import { ref, unref, computed } from 'vue' + +function pagination(list: T[], pageNo: number, pageSize: number): T[] { + const offset = (pageNo - 1) * Number(pageSize) + const ret = offset + Number(pageSize) >= list.length ? list.slice(offset, list.length) : list.slice(offset, offset + Number(pageSize)) + return ret +} + +export function usePagination(list: Ref, pageSize: number) { + const currentPage = ref(1) + const pageSizeRef = ref(pageSize) + + const getPaginationList = computed(() => { + return pagination(unref(list), unref(currentPage), unref(pageSizeRef)) + }) + + const getTotal = computed(() => { + return unref(list).length + }) + + function setCurrentPage(page: number) { + currentPage.value = page + } + + function setPageSize(pageSize: number) { + pageSizeRef.value = pageSize + } + + return { setCurrentPage, getTotal, setPageSize, getPaginationList } +} diff --git a/src/hooks/web/usePermission.ts b/src/hooks/web/usePermission.ts new file mode 100644 index 00000000..6cdcee5c --- /dev/null +++ b/src/hooks/web/usePermission.ts @@ -0,0 +1,107 @@ +import type { RouteRecordRaw } from 'vue-router' + +import { useAppStore } from '@/store/modules/app' +import { usePermissionStore } from '@/store/modules/permission' +import { useUserStore } from '@/store/modules/user' + +import { useTabs } from './useTabs' + +import { router, resetRouter } from '@/router' +// import { RootRoute } from '@/router/routes'; + +import projectSetting from '@/settings/projectSetting' +import { PermissionModeEnum } from '@/enums/appEnum' +import { RoleEnum } from '@/enums/roleEnum' + +import { intersection } from 'lodash-es' +import { isArray } from '@/utils/is' +import { useMultipleTabStore } from '@/store/modules/multipleTab' + +// User permissions related operations +export function usePermission() { + const userStore = useUserStore() + const appStore = useAppStore() + const permissionStore = usePermissionStore() + const { closeAll } = useTabs(router) + + /** + * Change permission mode + */ + async function togglePermissionMode() { + appStore.setProjectConfig({ + permissionMode: + appStore.projectConfig?.permissionMode === PermissionModeEnum.BACK ? PermissionModeEnum.ROUTE_MAPPING : PermissionModeEnum.BACK + }) + location.reload() + } + + /** + * Reset and regain authority resource information + * 重置和重新获得权限资源信息 + * @param id + */ + async function resume() { + const tabStore = useMultipleTabStore() + tabStore.clearCacheTabs() + resetRouter() + const routes = await permissionStore.buildRoutesAction() + routes.forEach((route) => { + router.addRoute(route as unknown as RouteRecordRaw) + }) + permissionStore.setLastBuildMenuTime() + closeAll() + } + + /** + * Determine whether there is permission + */ + function hasPermission(value?: RoleEnum | RoleEnum[] | string | string[], def = true): boolean { + // Visible by default + if (!value) { + return def + } + + const permMode = projectSetting.permissionMode + + if ([PermissionModeEnum.ROUTE_MAPPING, PermissionModeEnum.ROLE].includes(permMode)) { + if (!isArray(value)) { + return userStore.getRoleList?.includes(value as RoleEnum) + } + return (intersection(value, userStore.getRoleList) as RoleEnum[]).length > 0 + } + + if (PermissionModeEnum.BACK === permMode) { + const allCodeList = permissionStore.getPermCodeList as string[] + if (!isArray(value)) { + return allCodeList.includes(value) + } + return (intersection(value, allCodeList) as string[]).length > 0 + } + return true + } + + /** + * Change roles + * @param roles + */ + async function changeRole(roles: RoleEnum | RoleEnum[]): Promise { + if (projectSetting.permissionMode !== PermissionModeEnum.ROUTE_MAPPING) { + throw new Error('Please switch PermissionModeEnum to ROUTE_MAPPING mode in the configuration to operate!') + } + + if (!isArray(roles)) { + roles = [roles] + } + userStore.setRoleList(roles) + await resume() + } + + /** + * refresh menu data + */ + async function refreshMenu() { + resume() + } + + return { changeRole, hasPermission, togglePermissionMode, refreshMenu } +} diff --git a/src/hooks/web/useScript.ts b/src/hooks/web/useScript.ts new file mode 100644 index 00000000..7f7cb983 --- /dev/null +++ b/src/hooks/web/useScript.ts @@ -0,0 +1,46 @@ +import { onMounted, onUnmounted, ref } from 'vue' + +interface ScriptOptions { + src: string +} + +export function useScript(opts: ScriptOptions) { + const isLoading = ref(false) + const error = ref(false) + const success = ref(false) + let script: HTMLScriptElement + + const promise = new Promise((resolve, reject) => { + onMounted(() => { + script = document.createElement('script') + script.type = 'text/javascript' + script.onload = function () { + isLoading.value = false + success.value = true + error.value = false + resolve('') + } + + script.onerror = function (err) { + isLoading.value = false + success.value = false + error.value = true + reject(err) + } + + script.src = opts.src + document.head.appendChild(script) + }) + }) + + onUnmounted(() => { + script && script.remove() + }) + + return { + isLoading, + error, + success, + toPromise: () => promise + } +} diff --git a/src/hooks/web/useSortable.ts b/src/hooks/web/useSortable.ts new file mode 100644 index 00000000..8dc57527 --- /dev/null +++ b/src/hooks/web/useSortable.ts @@ -0,0 +1,21 @@ +import { nextTick, unref } from 'vue' +import type { Ref } from 'vue' +import type { Options } from 'sortablejs' + +export function useSortable(el: HTMLElement | Ref, options?: Options) { + function initSortable() { + nextTick(async () => { + if (!el) return + + const Sortable = (await import('sortablejs')).default + Sortable.create(unref(el), { + animation: 500, + delay: 400, + delayOnTouchOnly: true, + ...options + }) + }) + } + + return { initSortable } +} diff --git a/src/hooks/web/useTabs.ts b/src/hooks/web/useTabs.ts new file mode 100644 index 00000000..60bf7012 --- /dev/null +++ b/src/hooks/web/useTabs.ts @@ -0,0 +1,103 @@ +import type { RouteLocationNormalized, Router } from 'vue-router' + +import { useRouter } from 'vue-router' +import { unref } from 'vue' + +import { useMultipleTabStore } from '@/store/modules/multipleTab' +import { useAppStore } from '@/store/modules/app' + +enum TableActionEnum { + REFRESH, + CLOSE_ALL, + CLOSE_LEFT, + CLOSE_RIGHT, + CLOSE_OTHER, + CLOSE_CURRENT, + CLOSE +} + +export function useTabs(_router?: Router) { + const appStore = useAppStore() + + function canIUseTabs(): boolean { + const { show } = appStore.getMultiTabsSetting + if (!show) { + throw new Error('The multi-tab page is currently not open, please open it in the settings!') + } + return !!show + } + + const tabStore = useMultipleTabStore() + const router = _router || useRouter() + + const { currentRoute } = router + + function getCurrentTab() { + const route = unref(currentRoute) + return tabStore.getTabList.find((item) => item.fullPath === route.fullPath)! + } + + async function updateTabTitle(title: string, tab?: RouteLocationNormalized) { + const canIUse = canIUseTabs + if (!canIUse) { + return + } + const targetTab = tab || getCurrentTab() + await tabStore.setTabTitle(title, targetTab) + } + + async function updateTabPath(path: string, tab?: RouteLocationNormalized) { + const canIUse = canIUseTabs + if (!canIUse) { + return + } + const targetTab = tab || getCurrentTab() + await tabStore.updateTabPath(path, targetTab) + } + + async function handleTabAction(action: TableActionEnum, tab?: RouteLocationNormalized) { + const canIUse = canIUseTabs + if (!canIUse) { + return + } + const currentTab = getCurrentTab() + switch (action) { + case TableActionEnum.REFRESH: + await tabStore.refreshPage(router) + break + + case TableActionEnum.CLOSE_ALL: + await tabStore.closeAllTab(router) + break + + case TableActionEnum.CLOSE_LEFT: + await tabStore.closeLeftTabs(currentTab, router) + break + + case TableActionEnum.CLOSE_RIGHT: + await tabStore.closeRightTabs(currentTab, router) + break + + case TableActionEnum.CLOSE_OTHER: + await tabStore.closeOtherTabs(currentTab, router) + break + + case TableActionEnum.CLOSE_CURRENT: + case TableActionEnum.CLOSE: + await tabStore.closeTab(tab || currentTab, router) + break + } + } + + return { + refreshPage: () => handleTabAction(TableActionEnum.REFRESH), + closeAll: () => handleTabAction(TableActionEnum.CLOSE_ALL), + closeLeft: () => handleTabAction(TableActionEnum.CLOSE_LEFT), + closeRight: () => handleTabAction(TableActionEnum.CLOSE_RIGHT), + closeOther: () => handleTabAction(TableActionEnum.CLOSE_OTHER), + closeCurrent: () => handleTabAction(TableActionEnum.CLOSE_CURRENT), + close: (tab?: RouteLocationNormalized) => handleTabAction(TableActionEnum.CLOSE, tab), + setTitle: (title: string, tab?: RouteLocationNormalized) => updateTabTitle(title, tab), + updatePath: (fullPath: string, tab?: RouteLocationNormalized) => updateTabPath(fullPath, tab) + } +} diff --git a/src/hooks/web/useTitle.ts b/src/hooks/web/useTitle.ts new file mode 100644 index 00000000..3782fe7e --- /dev/null +++ b/src/hooks/web/useTitle.ts @@ -0,0 +1,35 @@ +import { watch, unref } from 'vue' +import { useI18n } from '@/hooks/web/useI18n' +import { useTitle as usePageTitle } from '@vueuse/core' +import { useGlobSetting } from '@/hooks/setting' +import { useRouter } from 'vue-router' +import { useLocaleStore } from '@/store/modules/locale' + +import { REDIRECT_NAME } from '@/router/constant' + +/** + * Listening to page changes and dynamically changing site titles + */ +export function useTitle() { + const { title } = useGlobSetting() + const { t } = useI18n() + const { currentRoute } = useRouter() + const localeStore = useLocaleStore() + + const pageTitle = usePageTitle() + + watch( + [() => currentRoute.value.path, () => localeStore.getLocale], + () => { + const route = unref(currentRoute) + + if (route.name === REDIRECT_NAME) { + return + } + + const tTitle = t(route?.meta?.title as string) + pageTitle.value = tTitle ? ` ${tTitle} - ${title} ` : `${title}` + }, + { immediate: true } + ) +} diff --git a/src/hooks/web/useWatermark.ts b/src/hooks/web/useWatermark.ts new file mode 100644 index 00000000..4a24da0a --- /dev/null +++ b/src/hooks/web/useWatermark.ts @@ -0,0 +1,98 @@ +import { getCurrentInstance, onBeforeUnmount, ref, Ref, shallowRef, unref } from 'vue' +import { useRafThrottle } from '@/utils/domUtils' +import { addResizeListener, removeResizeListener } from '@/utils/event' +import { isDef } from '@/utils/is' + +const domSymbol = Symbol('watermark-dom') + +export function useWatermark(appendEl: Ref = ref(document.body) as Ref) { + const func = useRafThrottle(function () { + const el = unref(appendEl) + if (!el) return + const { clientHeight: height, clientWidth: width } = el + updateWatermark({ height, width }) + }) + const id = domSymbol.toString() + const watermarkEl = shallowRef() + + const clear = () => { + const domId = unref(watermarkEl) + watermarkEl.value = undefined + const el = unref(appendEl) + if (!el) return + domId && el.removeChild(domId) + removeResizeListener(el, func) + } + + function createBase64(str: string) { + const can = document.createElement('canvas') + const width = 300 + const height = 240 + Object.assign(can, { width, height }) + + const cans = can.getContext('2d') + if (cans) { + cans.rotate((-20 * Math.PI) / 120) + cans.font = '15px Vedana' + cans.fillStyle = 'rgba(0, 0, 0, 0.15)' + cans.textAlign = 'left' + cans.textBaseline = 'middle' + cans.fillText(str, width / 20, height) + } + return can.toDataURL('image/png') + } + + function updateWatermark( + options: { + width?: number + height?: number + str?: string + } = {} + ) { + const el = unref(watermarkEl) + if (!el) return + if (isDef(options.width)) { + el.style.width = `${options.width}px` + } + if (isDef(options.height)) { + el.style.height = `${options.height}px` + } + if (isDef(options.str)) { + el.style.background = `url(${createBase64(options.str)}) left top repeat` + } + } + + const createWatermark = (str: string) => { + if (unref(watermarkEl)) { + updateWatermark({ str }) + return id + } + const div = document.createElement('div') + watermarkEl.value = div + div.id = id + div.style.pointerEvents = 'none' + div.style.top = '0px' + div.style.left = '0px' + div.style.position = 'absolute' + div.style.zIndex = '100000' + const el = unref(appendEl) + if (!el) return id + const { clientHeight: height, clientWidth: width } = el + updateWatermark({ str, width, height }) + el.appendChild(div) + return id + } + + function setWatermark(str: string) { + createWatermark(str) + addResizeListener(document.documentElement, func) + const instance = getCurrentInstance() + if (instance) { + onBeforeUnmount(() => { + clear() + }) + } + } + + return { setWatermark, clear } +} diff --git a/src/layouts/default/content/index.vue b/src/layouts/default/content/index.vue new file mode 100644 index 00000000..d463d316 --- /dev/null +++ b/src/layouts/default/content/index.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/layouts/default/content/useContentContext.ts b/src/layouts/default/content/useContentContext.ts new file mode 100644 index 00000000..0bc9deff --- /dev/null +++ b/src/layouts/default/content/useContentContext.ts @@ -0,0 +1,17 @@ +import type { InjectionKey, ComputedRef } from 'vue' +import { createContext, useContext } from '@/hooks/core/useContext' + +export interface ContentContextProps { + contentHeight: ComputedRef + setPageHeight: (height: number) => Promise +} + +const key: InjectionKey = Symbol() + +export function createContentContext(context: ContentContextProps) { + return createContext(context, key, { native: true }) +} + +export function useContentContext() { + return useContext(key) +} diff --git a/src/layouts/default/content/useContentViewHeight.ts b/src/layouts/default/content/useContentViewHeight.ts new file mode 100644 index 00000000..19aaa940 --- /dev/null +++ b/src/layouts/default/content/useContentViewHeight.ts @@ -0,0 +1,42 @@ +import { ref, computed, unref } from 'vue' +import { createPageContext } from '@/hooks/component/usePageContext' +import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn' + +const headerHeightRef = ref(0) +const footerHeightRef = ref(0) + +export function useLayoutHeight() { + function setHeaderHeight(val) { + headerHeightRef.value = val + } + function setFooterHeight(val) { + footerHeightRef.value = val + } + return { headerHeightRef, footerHeightRef, setHeaderHeight, setFooterHeight } +} + +export function useContentViewHeight() { + const contentHeight = ref(window.innerHeight) + const pageHeight = ref(window.innerHeight) + const getViewHeight = computed(() => { + return unref(contentHeight) - unref(headerHeightRef) - unref(footerHeightRef) || 0 + }) + + useWindowSizeFn( + () => { + contentHeight.value = window.innerHeight + }, + 100, + { immediate: true } + ) + + async function setPageHeight(height: number) { + pageHeight.value = height + } + + createPageContext({ + contentHeight: getViewHeight, + setPageHeight, + pageHeight + }) +} diff --git a/src/layouts/default/feature/index.vue b/src/layouts/default/feature/index.vue new file mode 100644 index 00000000..38562f10 --- /dev/null +++ b/src/layouts/default/feature/index.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/src/layouts/default/footer/index.vue b/src/layouts/default/footer/index.vue new file mode 100644 index 00000000..0cc1a6b7 --- /dev/null +++ b/src/layouts/default/footer/index.vue @@ -0,0 +1,79 @@ + + + + diff --git a/src/layouts/default/header/MultipleHeader.vue b/src/layouts/default/header/MultipleHeader.vue new file mode 100644 index 00000000..4eee45d7 --- /dev/null +++ b/src/layouts/default/header/MultipleHeader.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/layouts/default/header/components/Breadcrumb.vue b/src/layouts/default/header/components/Breadcrumb.vue new file mode 100644 index 00000000..07b5af9f --- /dev/null +++ b/src/layouts/default/header/components/Breadcrumb.vue @@ -0,0 +1,195 @@ + + + diff --git a/src/layouts/default/header/components/ErrorAction.vue b/src/layouts/default/header/components/ErrorAction.vue new file mode 100644 index 00000000..a5a1218a --- /dev/null +++ b/src/layouts/default/header/components/ErrorAction.vue @@ -0,0 +1,30 @@ + + diff --git a/src/layouts/default/header/components/FullScreen.vue b/src/layouts/default/header/components/FullScreen.vue new file mode 100644 index 00000000..91eb59c2 --- /dev/null +++ b/src/layouts/default/header/components/FullScreen.vue @@ -0,0 +1,24 @@ + + diff --git a/src/layouts/default/header/components/index.ts b/src/layouts/default/header/components/index.ts new file mode 100644 index 00000000..6c072d3b --- /dev/null +++ b/src/layouts/default/header/components/index.ts @@ -0,0 +1,14 @@ +import { createAsyncComponent } from '@/utils/factory/createAsyncComponent' +import FullScreen from './FullScreen.vue' + +export const UserDropDown = createAsyncComponent(() => import('./user-dropdown/index.vue'), { + loading: true +}) + +export const LayoutBreadcrumb = createAsyncComponent(() => import('./Breadcrumb.vue')) + +export const Notify = createAsyncComponent(() => import('./notify/index.vue')) + +export const ErrorAction = createAsyncComponent(() => import('./ErrorAction.vue')) + +export { FullScreen } diff --git a/src/layouts/default/header/components/lock/LockModal.vue b/src/layouts/default/header/components/lock/LockModal.vue new file mode 100644 index 00000000..98868bdb --- /dev/null +++ b/src/layouts/default/header/components/lock/LockModal.vue @@ -0,0 +1,103 @@ + + + diff --git a/src/layouts/default/header/components/notify/NoticeList.vue b/src/layouts/default/header/components/notify/NoticeList.vue new file mode 100644 index 00000000..d5c44d5f --- /dev/null +++ b/src/layouts/default/header/components/notify/NoticeList.vue @@ -0,0 +1,171 @@ + + + diff --git a/src/layouts/default/header/components/notify/data.ts b/src/layouts/default/header/components/notify/data.ts new file mode 100644 index 00000000..aef0b92a --- /dev/null +++ b/src/layouts/default/header/components/notify/data.ts @@ -0,0 +1,192 @@ +export interface ListItem { + id: string + avatar: string + // 通知的标题内容 + title: string + // 是否在标题上显示删除线 + titleDelete?: boolean + datetime: string + type: string + read?: boolean + description: string + clickClose?: boolean + extra?: string + color?: string +} + +export interface TabItem { + key: string + name: string + list: ListItem[] + unreadlist?: ListItem[] +} + +export const tabListData: TabItem[] = [ + { + key: '1', + name: '通知', + list: [ + { + id: '000000001', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', + title: '你收到了 14 份新周报', + description: '', + datetime: '2017-08-09', + type: '1' + }, + { + id: '000000002', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png', + title: '你推荐的 曲妮妮 已通过第三轮面试', + description: '', + datetime: '2017-08-08', + type: '1' + }, + { + id: '000000003', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png', + title: '这种模板可以区分多种通知类型', + description: '', + datetime: '2017-08-07', + // read: true, + type: '1' + }, + { + id: '000000004', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', + title: '左侧图标用于区分不同的类型', + description: '', + datetime: '2017-08-07', + type: '1' + }, + { + id: '000000005', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', + title: '标题可以设置自动显示省略号,本例中标题行数已设为1行,如果内容超过1行将自动截断并支持tooltip显示完整标题。', + description: '', + datetime: '2017-08-07', + type: '1' + }, + { + id: '000000006', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', + title: '左侧图标用于区分不同的类型', + description: '', + datetime: '2017-08-07', + type: '1' + }, + { + id: '000000007', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', + title: '左侧图标用于区分不同的类型', + description: '', + datetime: '2017-08-07', + type: '1' + }, + { + id: '000000008', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', + title: '左侧图标用于区分不同的类型', + description: '', + datetime: '2017-08-07', + type: '1' + }, + { + id: '000000009', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', + title: '左侧图标用于区分不同的类型', + description: '', + datetime: '2017-08-07', + type: '1' + }, + { + id: '000000010', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', + title: '左侧图标用于区分不同的类型', + description: '', + datetime: '2017-08-07', + type: '1' + } + ] + }, + { + key: '2', + name: '消息', + list: [ + { + id: '000000006', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', + title: '曲丽丽 评论了你', + description: '描述信息描述信息描述信息', + datetime: '2017-08-07', + type: '2', + clickClose: true + }, + { + id: '000000007', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', + title: '朱偏右 回复了你', + description: '这种模板用于提醒谁与你发生了互动', + datetime: '2017-08-07', + type: '2', + clickClose: true + }, + { + id: '000000008', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', + title: '标题', + description: + '请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容', + datetime: '2017-08-07', + type: '2', + clickClose: true + } + ] + }, + { + key: '3', + name: '待办', + list: [ + { + id: '000000009', + avatar: '', + title: '任务名称', + description: '任务需要在 2017-01-12 20:00 前启动', + datetime: '', + extra: '未开始', + color: '', + type: '3' + }, + { + id: '000000010', + avatar: '', + title: '第三方紧急代码变更', + description: '冠霖 需在 2017-01-07 前完成代码变更任务', + datetime: '', + extra: '马上到期', + color: 'red', + type: '3' + }, + { + id: '000000011', + avatar: '', + title: '信息安全考试', + description: '指派竹尔于 2017-01-09 前完成更新并发布', + datetime: '', + extra: '已耗时 8 天', + color: 'gold', + type: '3' + }, + { + id: '000000012', + avatar: '', + title: 'ABCD 版本发布', + description: '指派竹尔于 2017-01-09 前完成更新并发布', + datetime: '', + extra: '进行中', + color: 'blue', + type: '3' + } + ] + } +] diff --git a/src/layouts/default/header/components/notify/index.vue b/src/layouts/default/header/components/notify/index.vue new file mode 100644 index 00000000..646f52c3 --- /dev/null +++ b/src/layouts/default/header/components/notify/index.vue @@ -0,0 +1,80 @@ + + + diff --git a/src/layouts/default/header/components/user-dropdown/DropMenuItem.vue b/src/layouts/default/header/components/user-dropdown/DropMenuItem.vue new file mode 100644 index 00000000..b51dc300 --- /dev/null +++ b/src/layouts/default/header/components/user-dropdown/DropMenuItem.vue @@ -0,0 +1,25 @@ + + diff --git a/src/layouts/default/header/components/user-dropdown/index.vue b/src/layouts/default/header/components/user-dropdown/index.vue new file mode 100644 index 00000000..9c03d295 --- /dev/null +++ b/src/layouts/default/header/components/user-dropdown/index.vue @@ -0,0 +1,144 @@ + + + diff --git a/src/layouts/default/header/index.less b/src/layouts/default/header/index.less new file mode 100644 index 00000000..b16007e7 --- /dev/null +++ b/src/layouts/default/header/index.less @@ -0,0 +1,196 @@ +@header-trigger-prefix-cls: ~'@{namespace}-layout-header-trigger'; +@header-prefix-cls: ~'@{namespace}-layout-header'; +@breadcrumb-prefix-cls: ~'@{namespace}-layout-breadcrumb'; +@logo-prefix-cls: ~'@{namespace}-app-logo'; + +.@{header-prefix-cls} { + display: flex; + height: @header-height; + padding: 0; + margin-left: -1px; + line-height: @header-height; + color: @white; + background-color: @white; + align-items: center; + justify-content: space-between; + + &--mobile { + .@{breadcrumb-prefix-cls}, + .error-action, + .notify-item, + .fullscreen-item { + display: none; + } + + .@{logo-prefix-cls} { + min-width: unset; + padding-right: 0; + + &__title { + display: none; + } + } + .@{header-trigger-prefix-cls} { + padding: 0 4px 0 8px !important; + } + .@{header-prefix-cls}-action { + padding-right: 4px; + } + } + + &--fixed { + position: fixed; + top: 0; + left: 0; + z-index: @layout-header-fixed-z-index; + width: 100%; + } + + &-logo { + height: @header-height; + min-width: 192px; + padding: 0 10px; + font-size: 14px; + + img { + width: @logo-width; + height: @logo-width; + margin-right: 2px; + } + } + + &-left { + display: flex; + height: 100%; + align-items: center; + + .@{header-trigger-prefix-cls} { + display: flex; + height: 100%; + padding: 1px 10px 0; + cursor: pointer; + align-items: center; + + .anticon { + font-size: 16px; + } + + &.light { + &:hover { + background-color: @header-light-bg-hover-color; + } + + svg { + fill: #000; + } + } + + &.dark { + &:hover { + background-color: @header-dark-bg-hover-color; + } + } + } + } + + &-menu { + height: 100%; + min-width: 0; + flex: 1; + align-items: center; + } + + &-action { + display: flex; + min-width: 180px; + // padding-right: 12px; + align-items: center; + + &__item { + display: flex !important; + height: @header-height; + padding: 0 2px; + font-size: 1.2em; + cursor: pointer; + align-items: center; + + .ant-badge { + height: @header-height; + line-height: @header-height; + } + + .ant-badge-dot { + top: 10px; + right: 2px; + } + } + + span[role='img'] { + padding: 0 8px; + } + } + + &--light { + background-color: @white !important; + border-bottom: 1px solid @header-light-bottom-border-color; + border-left: 1px solid @header-light-bottom-border-color; + + .@{header-prefix-cls}-logo { + color: @text-color-base; + + &:hover { + background-color: @header-light-bg-hover-color; + } + } + + .@{header-prefix-cls}-action { + &__item { + color: @text-color-base; + + .app-iconify { + padding: 0 10px; + font-size: 16px !important; + } + + &:hover { + background-color: @header-light-bg-hover-color; + } + } + + &-icon, + span[role='img'] { + color: @text-color-base; + } + } + } + + &--dark { + background-color: @header-dark-bg-color !important; + // border-bottom: 1px solid @border-color-base; + border-left: 1px solid @border-color-base; + .@{header-prefix-cls}-logo { + &:hover { + background-color: @header-dark-bg-hover-color; + } + } + + .@{header-prefix-cls}-action { + &__item { + .app-iconify { + padding: 0 10px; + font-size: 16px !important; + } + + .ant-badge { + span { + color: @white; + } + } + + &:hover { + background-color: @header-dark-bg-hover-color; + } + } + } + } +} diff --git a/src/layouts/default/header/index.vue b/src/layouts/default/header/index.vue new file mode 100644 index 00000000..47cceeb7 --- /dev/null +++ b/src/layouts/default/header/index.vue @@ -0,0 +1,129 @@ + + + diff --git a/src/layouts/default/index.vue b/src/layouts/default/index.vue new file mode 100644 index 00000000..9ea11c0a --- /dev/null +++ b/src/layouts/default/index.vue @@ -0,0 +1,71 @@ + + + + diff --git a/src/layouts/default/menu/index.vue b/src/layouts/default/menu/index.vue new file mode 100644 index 00000000..5c2ef4f3 --- /dev/null +++ b/src/layouts/default/menu/index.vue @@ -0,0 +1,183 @@ + + diff --git a/src/layouts/default/menu/useLayoutMenu.ts b/src/layouts/default/menu/useLayoutMenu.ts new file mode 100644 index 00000000..492157c0 --- /dev/null +++ b/src/layouts/default/menu/useLayoutMenu.ts @@ -0,0 +1,105 @@ +import type { Menu } from '@/router/types' +import type { Ref } from 'vue' +import { watch, unref, ref, computed } from 'vue' +import { useRouter } from 'vue-router' +import { MenuSplitTyeEnum } from '@/enums/menuEnum' +import { useThrottleFn } from '@vueuse/core' +import { useMenuSetting } from '@/hooks/setting/useMenuSetting' +import { getChildrenMenus, getCurrentParentPath, getMenus, getShallowMenus } from '@/router/menus' +import { usePermissionStore } from '@/store/modules/permission' +import { useAppInject } from '@/hooks/web/useAppInject' + +export function useSplitMenu(splitType: Ref) { + // Menu array + const menusRef = ref([]) + const { currentRoute } = useRouter() + const { getIsMobile } = useAppInject() + const permissionStore = usePermissionStore() + const { setMenuSetting, getIsHorizontal, getSplit } = useMenuSetting() + + const throttleHandleSplitLeftMenu = useThrottleFn(handleSplitLeftMenu, 50) + + const splitNotLeft = computed(() => unref(splitType) !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontal)) + + const getSplitLeft = computed(() => !unref(getSplit) || unref(splitType) !== MenuSplitTyeEnum.LEFT) + + const getSpiltTop = computed(() => unref(splitType) === MenuSplitTyeEnum.TOP) + + const normalType = computed(() => { + return unref(splitType) === MenuSplitTyeEnum.NONE || !unref(getSplit) + }) + + watch( + [() => unref(currentRoute).path, () => unref(splitType)], + async ([path]: [string, MenuSplitTyeEnum]) => { + if (unref(splitNotLeft) || unref(getIsMobile)) return + + const { meta } = unref(currentRoute) + const currentActiveMenu = meta.currentActiveMenu as string + let parentPath = await getCurrentParentPath(path) + if (!parentPath) { + parentPath = await getCurrentParentPath(currentActiveMenu) + } + parentPath && throttleHandleSplitLeftMenu(parentPath) + }, + { + immediate: true + } + ) + + // Menu changes + watch( + [() => permissionStore.getLastBuildMenuTime, () => permissionStore.getBackMenuList], + () => { + genMenus() + }, + { + immediate: true + } + ) + + // split Menu changes + watch( + () => getSplit.value, + () => { + if (unref(splitNotLeft)) return + genMenus() + } + ) + + // Handle left menu split + async function handleSplitLeftMenu(parentPath: string) { + if (unref(getSplitLeft) || unref(getIsMobile)) return + + // spilt mode left + const children = await getChildrenMenus(parentPath) + + if (!children || !children.length) { + setMenuSetting({ hidden: true }) + menusRef.value = [] + return + } + + setMenuSetting({ hidden: false }) + menusRef.value = children + } + + // get menus + async function genMenus() { + // normal mode + if (unref(normalType) || unref(getIsMobile)) { + menusRef.value = await getMenus() + return + } + + // split-top + if (unref(getSpiltTop)) { + const shallowMenus = await getShallowMenus() + + menusRef.value = shallowMenus + return + } + } + + return { menusRef } +} diff --git a/src/layouts/default/setting/SettingDrawer.tsx b/src/layouts/default/setting/SettingDrawer.tsx new file mode 100644 index 00000000..728432bc --- /dev/null +++ b/src/layouts/default/setting/SettingDrawer.tsx @@ -0,0 +1,343 @@ +import { defineComponent, computed, unref } from 'vue' +import { BasicDrawer } from '@/components/Drawer' +import { Divider } from 'ant-design-vue' +import { TypePicker, ThemeColorPicker, SettingFooter, SwitchItem, SelectItem, InputNumberItem } from './components' + +import { AppDarkModeToggle } from '@/components/Application' + +import { MenuTypeEnum, TriggerEnum } from '@/enums/menuEnum' + +import { useRootSetting } from '@/hooks/setting/useRootSetting' +import { useMenuSetting } from '@/hooks/setting/useMenuSetting' +import { useHeaderSetting } from '@/hooks/setting/useHeaderSetting' +import { useMultipleTabSetting } from '@/hooks/setting/useMultipleTabSetting' +import { useTransitionSetting } from '@/hooks/setting/useTransitionSetting' +import { useI18n } from '@/hooks/web/useI18n' + +import { baseHandler } from './handler' + +import { + HandlerEnum, + contentModeOptions, + topMenuAlignOptions, + getMenuTriggerOptions, + routerTransitionOptions, + menuTypeList, + mixSidebarTriggerOptions +} from './enum' + +import { HEADER_PRESET_BG_COLOR_LIST, SIDE_BAR_BG_COLOR_LIST, APP_PRESET_COLOR_LIST } from '@/settings/designSetting' + +const { t } = useI18n() + +export default defineComponent({ + name: 'SettingDrawer', + setup(_, { attrs }) { + const { + getContentMode, + getShowFooter, + getShowBreadCrumb, + getShowBreadCrumbIcon, + getShowLogo, + getFullContent, + getColorWeak, + getGrayMode, + getLockTime, + getShowDarkModeToggle, + getThemeColor + } = useRootSetting() + + const { getOpenPageLoading, getBasicTransition, getEnableTransition, getOpenNProgress } = useTransitionSetting() + + const { + getIsHorizontal, + getShowMenu, + getMenuType, + getTrigger, + getCollapsedShowTitle, + getMenuFixed, + getCollapsed, + getCanDrag, + getTopMenuAlign, + getAccordion, + getMenuWidth, + getMenuBgColor, + getIsTopMenu, + getSplit, + getIsMixSidebar, + getCloseMixSidebarOnChange, + getMixSideTrigger, + getMixSideFixed + } = useMenuSetting() + + const { getShowHeader, getFixed: getHeaderFixed, getHeaderBgColor, getShowSearch } = useHeaderSetting() + + const { getShowMultipleTab, getShowQuick, getShowRedo, getShowFold } = useMultipleTabSetting() + + const getShowMenuRef = computed(() => { + return unref(getShowMenu) && !unref(getIsHorizontal) + }) + + function renderSidebar() { + return ( + <> + { + baseHandler(HandlerEnum.CHANGE_LAYOUT, { + mode: item.mode, + type: item.type, + split: unref(getIsHorizontal) ? false : undefined + }) + }} + def={unref(getMenuType)} + /> + + ) + } + + function renderHeaderTheme() { + return + } + + function renderSiderTheme() { + return + } + + function renderMainTheme() { + return + } + + /** + * @description: + */ + function renderFeatures() { + let triggerDef = unref(getTrigger) + + const triggerOptions = getMenuTriggerOptions(unref(getSplit)) + const some = triggerOptions.some((item) => item.value === triggerDef) + if (!some) { + triggerDef = TriggerEnum.FOOTER + } + + return ( + <> + + + + + + + + + + + + + + + + + + + { + return parseInt(value) === 0 ? `0(${t('layout.setting.notAutoScreenLock')})` : `${value}${t('layout.setting.minute')}` + }} + /> + `${parseInt(value)}px`} + /> + + ) + } + + function renderContent() { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + ) + } + + function renderTransition() { + return ( + <> + + + + + + + + ) + } + + return () => ( + + {unref(getShowDarkModeToggle) && {() => t('layout.setting.darkMode')}} + {unref(getShowDarkModeToggle) && } + {() => t('layout.setting.navMode')} + {renderSidebar()} + {() => t('layout.setting.sysTheme')} + {renderMainTheme()} + {() => t('layout.setting.headerTheme')} + {renderHeaderTheme()} + {() => t('layout.setting.sidebarTheme')} + {renderSiderTheme()} + {() => t('layout.setting.interfaceFunction')} + {renderFeatures()} + {() => t('layout.setting.interfaceDisplay')} + {renderContent()} + {() => t('layout.setting.animation')} + {renderTransition()} + + + + ) + } +}) diff --git a/src/layouts/default/setting/components/InputNumberItem.vue b/src/layouts/default/setting/components/InputNumberItem.vue new file mode 100644 index 00000000..4171f270 --- /dev/null +++ b/src/layouts/default/setting/components/InputNumberItem.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/layouts/default/setting/components/SelectItem.vue b/src/layouts/default/setting/components/SelectItem.vue new file mode 100644 index 00000000..3b6c1451 --- /dev/null +++ b/src/layouts/default/setting/components/SelectItem.vue @@ -0,0 +1,64 @@ + + + diff --git a/src/layouts/default/setting/components/SettingFooter.vue b/src/layouts/default/setting/components/SettingFooter.vue new file mode 100644 index 00000000..695a9d00 --- /dev/null +++ b/src/layouts/default/setting/components/SettingFooter.vue @@ -0,0 +1,85 @@ + + + diff --git a/src/layouts/default/setting/components/SwitchItem.vue b/src/layouts/default/setting/components/SwitchItem.vue new file mode 100644 index 00000000..473ab2b2 --- /dev/null +++ b/src/layouts/default/setting/components/SwitchItem.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/layouts/default/setting/components/ThemeColorPicker.vue b/src/layouts/default/setting/components/ThemeColorPicker.vue new file mode 100644 index 00000000..e7e13db6 --- /dev/null +++ b/src/layouts/default/setting/components/ThemeColorPicker.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/layouts/default/setting/components/TypePicker.vue b/src/layouts/default/setting/components/TypePicker.vue new file mode 100644 index 00000000..bed88cb2 --- /dev/null +++ b/src/layouts/default/setting/components/TypePicker.vue @@ -0,0 +1,166 @@ + + + diff --git a/src/layouts/default/setting/components/index.ts b/src/layouts/default/setting/components/index.ts new file mode 100644 index 00000000..4cd70d3f --- /dev/null +++ b/src/layouts/default/setting/components/index.ts @@ -0,0 +1,8 @@ +import { createAsyncComponent } from '@/utils/factory/createAsyncComponent' + +export const TypePicker = createAsyncComponent(() => import('./TypePicker.vue')) +export const ThemeColorPicker = createAsyncComponent(() => import('./ThemeColorPicker.vue')) +export const SettingFooter = createAsyncComponent(() => import('./SettingFooter.vue')) +export const SwitchItem = createAsyncComponent(() => import('./SwitchItem.vue')) +export const SelectItem = createAsyncComponent(() => import('./SelectItem.vue')) +export const InputNumberItem = createAsyncComponent(() => import('./InputNumberItem.vue')) diff --git a/src/layouts/default/setting/enum.ts b/src/layouts/default/setting/enum.ts new file mode 100644 index 00000000..77fa2ebd --- /dev/null +++ b/src/layouts/default/setting/enum.ts @@ -0,0 +1,150 @@ +import { ContentEnum, RouterTransitionEnum } from '@/enums/appEnum' +import { MenuModeEnum, MenuTypeEnum, TopMenuAlignEnum, TriggerEnum, MixSidebarTriggerEnum } from '@/enums/menuEnum' + +import { useI18n } from '@/hooks/web/useI18n' + +const { t } = useI18n() + +export enum HandlerEnum { + CHANGE_LAYOUT, + CHANGE_THEME_COLOR, + CHANGE_THEME, + // menu + MENU_HAS_DRAG, + MENU_ACCORDION, + MENU_TRIGGER, + MENU_TOP_ALIGN, + MENU_COLLAPSED, + MENU_COLLAPSED_SHOW_TITLE, + MENU_WIDTH, + MENU_SHOW_SIDEBAR, + MENU_THEME, + MENU_SPLIT, + MENU_FIXED, + MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE, + MENU_TRIGGER_MIX_SIDEBAR, + MENU_FIXED_MIX_SIDEBAR, + + // header + HEADER_SHOW, + HEADER_THEME, + HEADER_FIXED, + + HEADER_SEARCH, + + TABS_SHOW_QUICK, + TABS_SHOW_REDO, + TABS_SHOW, + TABS_SHOW_FOLD, + + LOCK_TIME, + FULL_CONTENT, + CONTENT_MODE, + SHOW_BREADCRUMB, + SHOW_BREADCRUMB_ICON, + GRAY_MODE, + COLOR_WEAK, + SHOW_LOGO, + SHOW_FOOTER, + + ROUTER_TRANSITION, + OPEN_PROGRESS, + OPEN_PAGE_LOADING, + OPEN_ROUTE_TRANSITION +} + +export const contentModeOptions = [ + { + value: ContentEnum.FULL, + label: t('layout.setting.contentModeFull') + }, + { + value: ContentEnum.FIXED, + label: t('layout.setting.contentModeFixed') + } +] + +export const topMenuAlignOptions = [ + { + value: TopMenuAlignEnum.CENTER, + label: t('layout.setting.topMenuAlignRight') + }, + { + value: TopMenuAlignEnum.START, + label: t('layout.setting.topMenuAlignLeft') + }, + { + value: TopMenuAlignEnum.END, + label: t('layout.setting.topMenuAlignCenter') + } +] + +export const getMenuTriggerOptions = (hideTop: boolean) => { + return [ + { + value: TriggerEnum.NONE, + label: t('layout.setting.menuTriggerNone') + }, + { + value: TriggerEnum.FOOTER, + label: t('layout.setting.menuTriggerBottom') + }, + ...(hideTop + ? [] + : [ + { + value: TriggerEnum.HEADER, + label: t('layout.setting.menuTriggerTop') + } + ]) + ] +} + +export const routerTransitionOptions = [ + RouterTransitionEnum.ZOOM_FADE, + RouterTransitionEnum.FADE, + RouterTransitionEnum.ZOOM_OUT, + RouterTransitionEnum.FADE_SIDE, + RouterTransitionEnum.FADE_BOTTOM, + RouterTransitionEnum.FADE_SCALE +].map((item) => { + return { + label: item, + value: item + } +}) + +export const menuTypeList = [ + { + title: t('layout.setting.menuTypeSidebar'), + mode: MenuModeEnum.INLINE, + type: MenuTypeEnum.SIDEBAR + }, + { + title: t('layout.setting.menuTypeMix'), + mode: MenuModeEnum.INLINE, + type: MenuTypeEnum.MIX + }, + + { + title: t('layout.setting.menuTypeTopMenu'), + mode: MenuModeEnum.HORIZONTAL, + type: MenuTypeEnum.TOP_MENU + }, + { + title: t('layout.setting.menuTypeMixSidebar'), + mode: MenuModeEnum.INLINE, + type: MenuTypeEnum.MIX_SIDEBAR + } +] + +export const mixSidebarTriggerOptions = [ + { + value: MixSidebarTriggerEnum.HOVER, + label: t('layout.setting.triggerHover') + }, + { + value: MixSidebarTriggerEnum.CLICK, + label: t('layout.setting.triggerClick') + } +] diff --git a/src/layouts/default/setting/handler.ts b/src/layouts/default/setting/handler.ts new file mode 100644 index 00000000..b3b1ab58 --- /dev/null +++ b/src/layouts/default/setting/handler.ts @@ -0,0 +1,174 @@ +import { HandlerEnum } from './enum' +import { updateHeaderBgColor, updateSidebarBgColor } from '@/logics/theme/updateBackground' +import { updateColorWeak } from '@/logics/theme/updateColorWeak' +import { updateGrayMode } from '@/logics/theme/updateGrayMode' + +import { useAppStore } from '@/store/modules/app' +import { ProjectConfig } from '@/types/config' +import { changeTheme } from '@/logics/theme' +import { updateDarkTheme } from '@/logics/theme/dark' +import { useRootSetting } from '@/hooks/setting/useRootSetting' + +export function baseHandler(event: HandlerEnum, value: any) { + const appStore = useAppStore() + const config = handler(event, value) + appStore.setProjectConfig(config) + if (event === HandlerEnum.CHANGE_THEME) { + updateHeaderBgColor() + updateSidebarBgColor() + } +} + +export function handler(event: HandlerEnum, value: any): DeepPartial { + const appStore = useAppStore() + + const { getThemeColor, getDarkMode } = useRootSetting() + switch (event) { + case HandlerEnum.CHANGE_LAYOUT: + const { mode, type, split } = value + const splitOpt = split === undefined ? { split } : {} + + return { + menuSetting: { + mode, + type, + collapsed: false, + show: true, + hidden: false, + ...splitOpt + } + } + + case HandlerEnum.CHANGE_THEME_COLOR: + if (getThemeColor.value === value) { + return {} + } + changeTheme(value) + + return { themeColor: value } + + case HandlerEnum.CHANGE_THEME: + if (getDarkMode.value === value) { + return {} + } + updateDarkTheme(value) + + return {} + + case HandlerEnum.MENU_HAS_DRAG: + return { menuSetting: { canDrag: value } } + + case HandlerEnum.MENU_ACCORDION: + return { menuSetting: { accordion: value } } + + case HandlerEnum.MENU_TRIGGER: + return { menuSetting: { trigger: value } } + + case HandlerEnum.MENU_TOP_ALIGN: + return { menuSetting: { topMenuAlign: value } } + + case HandlerEnum.MENU_COLLAPSED: + return { menuSetting: { collapsed: value } } + + case HandlerEnum.MENU_WIDTH: + return { menuSetting: { menuWidth: value } } + + case HandlerEnum.MENU_SHOW_SIDEBAR: + return { menuSetting: { show: value } } + + case HandlerEnum.MENU_COLLAPSED_SHOW_TITLE: + return { menuSetting: { collapsedShowTitle: value } } + + case HandlerEnum.MENU_THEME: + updateSidebarBgColor(value) + return { menuSetting: { bgColor: value } } + + case HandlerEnum.MENU_SPLIT: + return { menuSetting: { split: value } } + + case HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE: + return { menuSetting: { closeMixSidebarOnChange: value } } + + case HandlerEnum.MENU_FIXED: + return { menuSetting: { fixed: value } } + + case HandlerEnum.MENU_TRIGGER_MIX_SIDEBAR: + return { menuSetting: { mixSideTrigger: value } } + + case HandlerEnum.MENU_FIXED_MIX_SIDEBAR: + return { menuSetting: { mixSideFixed: value } } + + // ============transition================== + case HandlerEnum.OPEN_PAGE_LOADING: + appStore.setPageLoading(false) + return { transitionSetting: { openPageLoading: value } } + + case HandlerEnum.ROUTER_TRANSITION: + return { transitionSetting: { basicTransition: value } } + + case HandlerEnum.OPEN_ROUTE_TRANSITION: + return { transitionSetting: { enable: value } } + + case HandlerEnum.OPEN_PROGRESS: + return { transitionSetting: { openNProgress: value } } + // ============root================== + + case HandlerEnum.LOCK_TIME: + return { lockTime: value } + + case HandlerEnum.FULL_CONTENT: + return { fullContent: value } + + case HandlerEnum.CONTENT_MODE: + return { contentMode: value } + + case HandlerEnum.SHOW_BREADCRUMB: + return { showBreadCrumb: value } + + case HandlerEnum.SHOW_BREADCRUMB_ICON: + return { showBreadCrumbIcon: value } + + case HandlerEnum.GRAY_MODE: + updateGrayMode(value) + return { grayMode: value } + + case HandlerEnum.SHOW_FOOTER: + return { showFooter: value } + + case HandlerEnum.COLOR_WEAK: + updateColorWeak(value) + return { colorWeak: value } + + case HandlerEnum.SHOW_LOGO: + return { showLogo: value } + + // ============tabs================== + case HandlerEnum.TABS_SHOW_QUICK: + return { multiTabsSetting: { showQuick: value } } + + case HandlerEnum.TABS_SHOW: + return { multiTabsSetting: { show: value } } + + case HandlerEnum.TABS_SHOW_REDO: + return { multiTabsSetting: { showRedo: value } } + + case HandlerEnum.TABS_SHOW_FOLD: + return { multiTabsSetting: { showFold: value } } + + // ============header================== + case HandlerEnum.HEADER_THEME: + updateHeaderBgColor(value) + return { headerSetting: { bgColor: value } } + + case HandlerEnum.HEADER_SEARCH: + return { headerSetting: { showSearch: value } } + + case HandlerEnum.HEADER_FIXED: + return { headerSetting: { fixed: value } } + + case HandlerEnum.HEADER_SHOW: + return { headerSetting: { show: value } } + default: + return {} + } +} diff --git a/src/layouts/default/setting/index.vue b/src/layouts/default/setting/index.vue new file mode 100644 index 00000000..343fe72f --- /dev/null +++ b/src/layouts/default/setting/index.vue @@ -0,0 +1,14 @@ + + diff --git a/src/layouts/default/sider/DragBar.vue b/src/layouts/default/sider/DragBar.vue new file mode 100644 index 00000000..88186d76 --- /dev/null +++ b/src/layouts/default/sider/DragBar.vue @@ -0,0 +1,55 @@ + + + diff --git a/src/layouts/default/sider/LayoutSider.vue b/src/layouts/default/sider/LayoutSider.vue new file mode 100644 index 00000000..b2afdef4 --- /dev/null +++ b/src/layouts/default/sider/LayoutSider.vue @@ -0,0 +1,144 @@ + + + diff --git a/src/layouts/default/sider/MixSider.vue b/src/layouts/default/sider/MixSider.vue new file mode 100644 index 00000000..edb13bd1 --- /dev/null +++ b/src/layouts/default/sider/MixSider.vue @@ -0,0 +1,573 @@ + + + diff --git a/src/layouts/default/sider/index.vue b/src/layouts/default/sider/index.vue new file mode 100644 index 00000000..257f0487 --- /dev/null +++ b/src/layouts/default/sider/index.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/layouts/default/sider/useLayoutSider.ts b/src/layouts/default/sider/useLayoutSider.ts new file mode 100644 index 00000000..649e2c0b --- /dev/null +++ b/src/layouts/default/sider/useLayoutSider.ts @@ -0,0 +1,137 @@ +import type { Ref } from 'vue' + +import { computed, unref, onMounted, nextTick } from 'vue' + +import { TriggerEnum } from '@/enums/menuEnum' + +import { useMenuSetting } from '@/hooks/setting/useMenuSetting' +import { useDebounceFn } from '@vueuse/core' +import { useAppStore } from '@/store/modules/app' + +/** + * Handle related operations of menu events + */ +export function useSiderEvent() { + const appStore = useAppStore() + const { getMiniWidthNumber } = useMenuSetting() + + const getCollapsedWidth = computed(() => { + return unref(getMiniWidthNumber) + }) + + function onBreakpointChange(broken: boolean) { + appStore.setProjectConfig({ + menuSetting: { + siderHidden: broken + } + }) + } + + return { getCollapsedWidth, onBreakpointChange } +} + +/** + * Handle related operations of menu folding + */ +export function useTrigger(getIsMobile: Ref) { + const { getTrigger, getSplit } = useMenuSetting() + + const getShowTrigger = computed(() => { + const trigger = unref(getTrigger) + + return trigger !== TriggerEnum.NONE && !unref(getIsMobile) && (trigger === TriggerEnum.FOOTER || unref(getSplit)) + }) + + const getTriggerAttr = computed(() => { + if (unref(getShowTrigger)) { + return {} + } + return { + trigger: null + } + }) + + return { getTriggerAttr, getShowTrigger } +} + +/** + * Handle menu drag and drop related operations + * @param siderRef + * @param dragBarRef + */ +export function useDragLine(siderRef: Ref, dragBarRef: Ref, mix = false) { + const { getMiniWidthNumber, getCollapsed, setMenuSetting } = useMenuSetting() + + onMounted(() => { + nextTick(() => { + const exec = useDebounceFn(changeWrapWidth, 80) + exec() + }) + }) + + function getEl(elRef: Ref): any { + const el = unref(elRef) + if (!el) return null + if (Reflect.has(el, '$el')) { + return (unref(elRef) as ComponentRef)?.$el + } + return unref(elRef) + } + + function handleMouseMove(ele: HTMLElement, wrap: HTMLElement, clientX: number) { + document.onmousemove = function (innerE) { + let iT = (ele as any).left + (innerE.clientX - clientX) + innerE = innerE || window.event + const maxT = 800 + const minT = unref(getMiniWidthNumber) + iT < 0 && (iT = 0) + iT > maxT && (iT = maxT) + iT < minT && (iT = minT) + ele.style.left = wrap.style.width = iT + 'px' + return false + } + } + + // Drag and drop in the menu area-release the mouse + function removeMouseup(ele: any) { + const wrap = getEl(siderRef) + document.onmouseup = function () { + document.onmousemove = null + document.onmouseup = null + wrap.style.transition = 'width 0.2s' + const width = parseInt(wrap.style.width) + + if (!mix) { + const miniWidth = unref(getMiniWidthNumber) + if (!unref(getCollapsed)) { + width > miniWidth + 20 ? setMenuSetting({ menuWidth: width }) : setMenuSetting({ collapsed: true }) + } else { + width > miniWidth && setMenuSetting({ collapsed: false, menuWidth: width }) + } + } else { + setMenuSetting({ menuWidth: width }) + } + + ele.releaseCapture?.() + } + } + + function changeWrapWidth() { + const ele = getEl(dragBarRef) + if (!ele) return + const wrap = getEl(siderRef) + if (!wrap) return + + ele.onmousedown = (e: any) => { + wrap.style.transition = 'unset' + const clientX = e?.clientX + ele.left = ele.offsetLeft + handleMouseMove(ele, wrap, clientX) + removeMouseup(ele) + ele.setCapture?.() + return false + } + } + + return {} +} diff --git a/src/layouts/default/tabs/components/FoldButton.vue b/src/layouts/default/tabs/components/FoldButton.vue new file mode 100644 index 00000000..5786f36d --- /dev/null +++ b/src/layouts/default/tabs/components/FoldButton.vue @@ -0,0 +1,32 @@ + + diff --git a/src/layouts/default/tabs/components/TabContent.vue b/src/layouts/default/tabs/components/TabContent.vue new file mode 100644 index 00000000..cfb7cd0e --- /dev/null +++ b/src/layouts/default/tabs/components/TabContent.vue @@ -0,0 +1,54 @@ + + diff --git a/src/layouts/default/tabs/components/TabRedo.vue b/src/layouts/default/tabs/components/TabRedo.vue new file mode 100644 index 00000000..390a1ca7 --- /dev/null +++ b/src/layouts/default/tabs/components/TabRedo.vue @@ -0,0 +1,25 @@ + + diff --git a/src/layouts/default/tabs/index.less b/src/layouts/default/tabs/index.less new file mode 100644 index 00000000..e6cdeb3d --- /dev/null +++ b/src/layouts/default/tabs/index.less @@ -0,0 +1,207 @@ +@prefix-cls: ~'@{namespace}-multiple-tabs'; + +html[data-theme='dark'] { + .@{prefix-cls} { + .ant-tabs-tab { + border-bottom: 1px solid @border-color-base; + } + } +} + +html[data-theme='light'] { + .@{prefix-cls} { + .ant-tabs-tab:not(.ant-tabs-tab-active) { + border: 1px solid #d9d9d9 !important; + } + } +} + +.@{prefix-cls} { + z-index: 10; + height: @multiple-height + 2; + line-height: @multiple-height + 2; + background-color: @component-background; + border-bottom: 1px solid @border-color-base; + + .ant-tabs-small { + height: @multiple-height; + } + + .ant-tabs.ant-tabs-card { + .ant-tabs-nav { + padding-top: 2px; + height: @multiple-height; + margin: 0; + background-color: @component-background; + border: 0; + box-shadow: none; + + .ant-tabs-nav-container { + height: @multiple-height; + padding-top: 2px; + } + + .ant-tabs-tab { + height: calc(@multiple-height - 2px); + padding-right: 12px; + line-height: calc(@multiple-height - 2px); + color: @text-color-base; + background-color: @component-background; + transition: none; + + &:hover { + .ant-tabs-tab-remove { + opacity: 1; + } + } + + .ant-tabs-tab-remove { + width: 8px; + height: 28px; + font-size: 12px; + color: inherit; + opacity: 0; + transition: none; + margin-left: 2px; + margin-right: -4px; + + &:hover { + svg { + width: 0.8em; + } + } + } + + // > div { + // display: flex; + // justify-content: center; + // align-items: center; + // } + + svg { + fill: @text-color-base; + } + } + + .ant-tabs-tab:not(.ant-tabs-tab-active) { + &:hover { + color: @primary-color; + } + } + + .ant-tabs-tab-active { + position: relative; + padding-left: 18px; + background: @primary-color; + border: 0; + transition: none; + + span { + color: @white !important; + } + + .ant-tabs-tab-remove { + opacity: 1; + } + + svg { + width: 0.7em; + fill: @white; + } + } + } + + .ant-tabs-nav > div:nth-child(1) { + padding: 0 6px; + + .ant-tabs-tab { + margin-right: 3px !important; + } + } + } + + .ant-tabs-tab:not(.ant-tabs-tab-active) { + .anticon-close { + font-size: 12px; + + svg { + width: 0.6em; + } + } + } + + .ant-dropdown-trigger { + display: inline-flex; + } + + &--hide-close { + .ant-tabs-tab-remove { + opacity: 0 !important; + } + } + + &-content { + &__extra-quick, + &__extra-redo, + &__extra-fold { + display: inline-block; + width: 36px; + height: @multiple-height; + line-height: @multiple-height; + color: @text-color-secondary; + text-align: center; + cursor: pointer; + border-left: 1px solid @border-color-base; + + &:hover { + color: @text-color-base; + } + + span[role='img'] { + transform: rotate(90deg); + } + } + + &__extra-redo { + span[role='img'] { + transform: rotate(0deg); + } + } + + &__info { + display: inline-block; + width: 100%; + height: @multiple-height - 2; + padding-left: 0; + margin-left: -10px; + font-size: 12px; + cursor: pointer; + user-select: none; + } + } +} + +.ant-tabs-dropdown-menu { + &-title-content { + display: flex; + align-items: center; + + .@{prefix-cls} { + &-content__info { + width: auto; + margin-left: 0; + line-height: 28px; + } + } + } + + &-item-remove { + margin-left: auto; + } +} + +.multiple-tabs__dropdown { + .ant-dropdown-content { + width: 172px; + } +} diff --git a/src/layouts/default/tabs/index.vue b/src/layouts/default/tabs/index.vue new file mode 100644 index 00000000..74b72495 --- /dev/null +++ b/src/layouts/default/tabs/index.vue @@ -0,0 +1,120 @@ + + + diff --git a/src/layouts/default/tabs/types.ts b/src/layouts/default/tabs/types.ts new file mode 100644 index 00000000..450fda4c --- /dev/null +++ b/src/layouts/default/tabs/types.ts @@ -0,0 +1,25 @@ +import type { DropMenu } from '@/components/Dropdown' +import type { RouteLocationNormalized } from 'vue-router' + +export enum TabContentEnum { + TAB_TYPE, + EXTRA_TYPE +} + +export type { DropMenu } + +export interface TabContentProps { + tabItem: RouteLocationNormalized + type?: TabContentEnum + trigger?: ('click' | 'hover' | 'contextmenu')[] +} + +export enum MenuEventEnum { + REFRESH_PAGE, + CLOSE_CURRENT, + CLOSE_LEFT, + CLOSE_RIGHT, + CLOSE_OTHER, + CLOSE_ALL, + SCALE +} diff --git a/src/layouts/default/tabs/useMultipleTabs.ts b/src/layouts/default/tabs/useMultipleTabs.ts new file mode 100644 index 00000000..78247c78 --- /dev/null +++ b/src/layouts/default/tabs/useMultipleTabs.ts @@ -0,0 +1,78 @@ +import { toRaw, ref, nextTick } from 'vue' +import type { RouteLocationNormalized } from 'vue-router' +import { useDesign } from '@/hooks/web/useDesign' +import { useSortable } from '@/hooks/web/useSortable' +import { useMultipleTabStore } from '@/store/modules/multipleTab' +import { isNullAndUnDef } from '@/utils/is' +import projectSetting from '@/settings/projectSetting' +import { useRouter } from 'vue-router' + +export function initAffixTabs(): string[] { + const affixList = ref([]) + + const tabStore = useMultipleTabStore() + const router = useRouter() + /** + * @description: Filter all fixed routes + */ + function filterAffixTabs(routes: RouteLocationNormalized[]) { + const tabs: RouteLocationNormalized[] = [] + routes && + routes.forEach((route) => { + if (route.meta && route.meta.affix) { + tabs.push(toRaw(route)) + } + }) + return tabs + } + + /** + * @description: Set fixed tabs + */ + function addAffixTabs(): void { + const affixTabs = filterAffixTabs(router.getRoutes() as unknown as RouteLocationNormalized[]) + affixList.value = affixTabs + for (const tab of affixTabs) { + tabStore.addTab({ + meta: tab.meta, + name: tab.name, + path: tab.path + } as unknown as RouteLocationNormalized) + } + } + + let isAddAffix = false + + if (!isAddAffix) { + addAffixTabs() + isAddAffix = true + } + return affixList.value.map((item) => item.meta?.title).filter(Boolean) as string[] +} + +export function useTabsDrag(affixTextList: string[]) { + const tabStore = useMultipleTabStore() + const { multiTabsSetting } = projectSetting + const { prefixCls } = useDesign('multiple-tabs') + nextTick(() => { + if (!multiTabsSetting.canDrag) return + const el = document.querySelectorAll(`.${prefixCls} .ant-tabs-nav-wrap > div`)?.[0] as HTMLElement + const { initSortable } = useSortable(el, { + filter: (e: ChangeEvent) => { + const text = e?.target?.innerText + if (!text) return false + return affixTextList.includes(text) + }, + onEnd: (evt) => { + const { oldIndex, newIndex } = evt + + if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) { + return + } + + tabStore.sortTabs(oldIndex, newIndex) + } + }) + initSortable() + }) +} diff --git a/src/layouts/default/tabs/useTabDropdown.ts b/src/layouts/default/tabs/useTabDropdown.ts new file mode 100644 index 00000000..7a9d90de --- /dev/null +++ b/src/layouts/default/tabs/useTabDropdown.ts @@ -0,0 +1,139 @@ +import type { TabContentProps } from './types' +import type { DropMenu } from '@/components/Dropdown' +import type { ComputedRef } from 'vue' + +import { computed, unref, reactive } from 'vue' +import { MenuEventEnum } from './types' +import { useMultipleTabStore } from '@/store/modules/multipleTab' +import { RouteLocationNormalized, useRouter } from 'vue-router' +import { useTabs } from '@/hooks/web/useTabs' +import { useI18n } from '@/hooks/web/useI18n' + +export function useTabDropdown(tabContentProps: TabContentProps, getIsTabs: ComputedRef) { + const state = reactive({ + current: null as Nullable, + currentIndex: 0 + }) + + const { t } = useI18n() + const tabStore = useMultipleTabStore() + const { currentRoute } = useRouter() + const { refreshPage, closeAll, close, closeLeft, closeOther, closeRight } = useTabs() + + const getTargetTab = computed((): RouteLocationNormalized => { + return unref(getIsTabs) ? tabContentProps.tabItem : unref(currentRoute) + }) + + /** + * @description: drop-down list + */ + const getDropMenuList = computed(() => { + if (!unref(getTargetTab)) { + return + } + const { meta } = unref(getTargetTab) + const { path } = unref(currentRoute) + + const curItem = state.current + + const isCurItem = curItem ? curItem.path === path : false + + // Refresh button + const index = state.currentIndex + const refreshDisabled = !isCurItem + // Close left + const closeLeftDisabled = index === 0 || !isCurItem + + const disabled = tabStore.getTabList.length === 1 + + // Close right + const closeRightDisabled = !isCurItem || (index === tabStore.getTabList.length - 1 && tabStore.getLastDragEndIndex >= 0) + const dropMenuList: DropMenu[] = [ + { + icon: 'ion:reload-sharp', + event: MenuEventEnum.REFRESH_PAGE, + text: t('layout.multipleTab.reload'), + disabled: refreshDisabled + }, + { + icon: 'clarity:close-line', + event: MenuEventEnum.CLOSE_CURRENT, + text: t('layout.multipleTab.close'), + disabled: !!meta?.affix || disabled, + divider: true + }, + { + icon: 'line-md:arrow-close-left', + event: MenuEventEnum.CLOSE_LEFT, + text: t('layout.multipleTab.closeLeft'), + disabled: closeLeftDisabled, + divider: false + }, + { + icon: 'line-md:arrow-close-right', + event: MenuEventEnum.CLOSE_RIGHT, + text: t('layout.multipleTab.closeRight'), + disabled: closeRightDisabled, + divider: true + }, + { + icon: 'dashicons:align-center', + event: MenuEventEnum.CLOSE_OTHER, + text: t('layout.multipleTab.closeOther'), + disabled: disabled || !isCurItem + }, + { + icon: 'clarity:minus-line', + event: MenuEventEnum.CLOSE_ALL, + text: t('layout.multipleTab.closeAll'), + disabled: disabled + } + ] + + return dropMenuList + }) + + function handleContextMenu(tabItem: RouteLocationNormalized) { + return (e: Event) => { + if (!tabItem) { + return + } + e?.preventDefault() + const index = tabStore.getTabList.findIndex((tab) => tab.path === tabItem.path) + state.current = tabItem + state.currentIndex = index + } + } + + // Handle right click event + function handleMenuEvent(menu: DropMenu): void { + const { event } = menu + switch (event) { + case MenuEventEnum.REFRESH_PAGE: + // refresh page + refreshPage() + break + // Close current + case MenuEventEnum.CLOSE_CURRENT: + close(tabContentProps.tabItem) + break + // Close left + case MenuEventEnum.CLOSE_LEFT: + closeLeft() + break + // Close right + case MenuEventEnum.CLOSE_RIGHT: + closeRight() + break + // Close other + case MenuEventEnum.CLOSE_OTHER: + closeOther() + break + // Close all + case MenuEventEnum.CLOSE_ALL: + closeAll() + break + } + } + return { getDropMenuList, handleMenuEvent, handleContextMenu } +} diff --git a/src/layouts/default/trigger/HeaderTrigger.vue b/src/layouts/default/trigger/HeaderTrigger.vue new file mode 100644 index 00000000..0aab52db --- /dev/null +++ b/src/layouts/default/trigger/HeaderTrigger.vue @@ -0,0 +1,15 @@ + + diff --git a/src/layouts/default/trigger/SiderTrigger.vue b/src/layouts/default/trigger/SiderTrigger.vue new file mode 100644 index 00000000..9644c8c6 --- /dev/null +++ b/src/layouts/default/trigger/SiderTrigger.vue @@ -0,0 +1,12 @@ + + diff --git a/src/layouts/default/trigger/index.vue b/src/layouts/default/trigger/index.vue new file mode 100644 index 00000000..805cd1bf --- /dev/null +++ b/src/layouts/default/trigger/index.vue @@ -0,0 +1,15 @@ + + diff --git a/src/layouts/iframe/index.vue b/src/layouts/iframe/index.vue new file mode 100644 index 00000000..ca681a9e --- /dev/null +++ b/src/layouts/iframe/index.vue @@ -0,0 +1,17 @@ + + diff --git a/src/layouts/iframe/useFrameKeepAlive.ts b/src/layouts/iframe/useFrameKeepAlive.ts new file mode 100644 index 00000000..69ebb8c9 --- /dev/null +++ b/src/layouts/iframe/useFrameKeepAlive.ts @@ -0,0 +1,59 @@ +import type { AppRouteRecordRaw } from '@/router/types' + +import { computed, toRaw, unref } from 'vue' + +import { useMultipleTabStore } from '@/store/modules/multipleTab' + +import { uniqBy } from 'lodash-es' + +import { useMultipleTabSetting } from '@/hooks/setting/useMultipleTabSetting' + +import { useRouter } from 'vue-router' + +export function useFrameKeepAlive() { + const router = useRouter() + const { currentRoute } = router + const { getShowMultipleTab } = useMultipleTabSetting() + const tabStore = useMultipleTabStore() + const getFramePages = computed(() => { + const ret = getAllFramePages(toRaw(router.getRoutes()) as unknown as AppRouteRecordRaw[]) || [] + return ret + }) + + const getOpenTabList = computed((): string[] => { + return tabStore.getTabList.reduce((prev: string[], next) => { + if (next.meta && Reflect.has(next.meta, 'frameSrc')) { + prev.push(next.name as string) + } + return prev + }, []) + }) + + function getAllFramePages(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] { + let res: AppRouteRecordRaw[] = [] + for (const route of routes) { + const { meta: { frameSrc } = {}, children } = route + if (frameSrc) { + res.push(route) + } + if (children && children.length) { + res.push(...getAllFramePages(children)) + } + } + res = uniqBy(res, 'name') + return res + } + + function showIframe(item: AppRouteRecordRaw) { + return item.name === unref(currentRoute).name + } + + function hasRenderFrame(name: string) { + if (!unref(getShowMultipleTab)) { + return router.currentRoute.value.name === name + } + return unref(getOpenTabList).includes(name) + } + + return { hasRenderFrame, getFramePages, showIframe, getAllFramePages } +} diff --git a/src/layouts/page/index.vue b/src/layouts/page/index.vue new file mode 100644 index 00000000..2d98e7f1 --- /dev/null +++ b/src/layouts/page/index.vue @@ -0,0 +1,55 @@ + + + diff --git a/src/layouts/page/transition.ts b/src/layouts/page/transition.ts new file mode 100644 index 00000000..0a6b8d14 --- /dev/null +++ b/src/layouts/page/transition.ts @@ -0,0 +1,33 @@ +import type { FunctionalComponent } from 'vue' +import type { RouteLocation } from 'vue-router' + +export interface DefaultContext { + Component: FunctionalComponent & { type: Recordable } + route: RouteLocation +} + +export function getTransitionName({ + route, + openCache, + cacheTabs, + enableTransition, + def +}: Pick & { + enableTransition: boolean + openCache: boolean + def: string + cacheTabs: string[] +}): string | undefined { + if (!enableTransition) { + return undefined + } + + const isInCache = cacheTabs.includes(route.name as string) + const transitionName = 'fade-slide' + let name: string | undefined = transitionName + + if (openCache) { + name = isInCache && route.meta.loaded ? transitionName : undefined + } + return name || (route.meta.transitionName as string) || def +} diff --git a/src/locales/helper.ts b/src/locales/helper.ts new file mode 100644 index 00000000..ffc74d84 --- /dev/null +++ b/src/locales/helper.ts @@ -0,0 +1,37 @@ +import type { LocaleType } from '@/types/config' + +import { set } from 'lodash-es' + +export const loadLocalePool: LocaleType[] = [] + +export function setHtmlPageLang(locale: LocaleType) { + document.querySelector('html')?.setAttribute('lang', locale) +} + +export function setLoadLocalePool(cb: (loadLocalePool: LocaleType[]) => void) { + cb(loadLocalePool) +} + +export function genMessage(langs: Record>, prefix = 'lang') { + const obj: Recordable = {} + + Object.keys(langs).forEach((key) => { + const langFileModule = langs[key].default + let fileName = key.replace(`./${prefix}/`, '').replace(/^\.\//, '') + const lastIndex = fileName.lastIndexOf('.') + fileName = fileName.substring(0, lastIndex) + const keyList = fileName.split('/') + const moduleName = keyList.shift() + const objKey = keyList.join('.') + + if (moduleName) { + if (objKey) { + set(obj, moduleName, obj[moduleName] || {}) + set(obj[moduleName], objKey, langFileModule) + } else { + set(obj, moduleName, langFileModule || {}) + } + } + }) + return obj +} diff --git a/src/locales/lang/en.ts b/src/locales/lang/en.ts new file mode 100644 index 00000000..2dd493cb --- /dev/null +++ b/src/locales/lang/en.ts @@ -0,0 +1,12 @@ +import { genMessage } from '../helper' +import antdLocale from 'ant-design-vue/es/locale/en_US' + +const modules = import.meta.globEager('./en/**/*.ts') +export default { + message: { + ...genMessage(modules, 'en'), + antdLocale + }, + dateLocale: null, + dateLocaleName: 'en' +} diff --git a/src/locales/lang/en/common.ts b/src/locales/lang/en/common.ts new file mode 100644 index 00000000..1e6bf895 --- /dev/null +++ b/src/locales/lang/en/common.ts @@ -0,0 +1,20 @@ +export default { + okText: 'OK', + closeText: 'Close', + cancelText: 'Cancel', + loadingText: 'Loading...', + saveText: 'Save', + delText: 'Delete', + resetText: 'Reset', + searchText: 'Search', + queryText: 'Search', + + inputText: 'Please enter', + chooseText: 'Please choose', + + redo: 'Refresh', + back: 'Back', + + light: 'Light', + dark: 'Dark' +} diff --git a/src/locales/lang/en/component.ts b/src/locales/lang/en/component.ts new file mode 100644 index 00000000..d66bd077 --- /dev/null +++ b/src/locales/lang/en/component.ts @@ -0,0 +1,129 @@ +export default { + app: { + searchNotData: 'No search results yet', + toSearch: 'to search', + toNavigate: 'to navigate' + }, + countdown: { + normalText: 'Get SMS code', + sendText: 'Reacquire in {0}s' + }, + cropper: { + selectImage: 'Select Image', + uploadSuccess: 'Uploaded success!', + modalTitle: 'Avatar upload', + okText: 'Confirm and upload', + btn_reset: 'Reset', + btn_rotate_left: 'Counterclockwise rotation', + btn_rotate_right: 'Clockwise rotation', + btn_scale_x: 'Flip horizontal', + btn_scale_y: 'Flip vertical', + btn_zoom_in: 'Zoom in', + btn_zoom_out: 'Zoom out', + preview: 'Preivew' + }, + drawer: { + loadingText: 'Loading...', + cancelText: 'Close', + okText: 'Confirm' + }, + excel: { + exportModalTitle: 'Export data', + fileType: 'File type', + fileName: 'File name' + }, + form: { + putAway: 'Put away', + unfold: 'Unfold', + maxTip: 'The number of characters should be less than {0}', + apiSelectNotFound: 'Wait for data loading to complete...' + }, + icon: { + placeholder: 'Click the select icon', + search: 'Search icon', + copy: 'Copy icon successfully!' + }, + menu: { + search: 'Menu search' + }, + modal: { + cancelText: 'Close', + okText: 'Confirm', + close: 'Close', + maximize: 'Maximize', + restore: 'Restore' + }, + table: { + settingDens: 'Density', + settingDensDefault: 'Default', + settingDensMiddle: 'Middle', + settingDensSmall: 'Compact', + settingColumn: 'Column settings', + settingColumnShow: 'Column display', + settingIndexColumnShow: 'Index Column', + settingSelectColumnShow: 'Selection Column', + settingFixedLeft: 'Fixed Left', + settingFixedRight: 'Fixed Right', + settingFullScreen: 'Full Screen', + index: 'Index', + total: 'total of {total}' + }, + time: { + before: ' ago', + after: ' after', + just: 'just now', + seconds: ' seconds', + minutes: ' minutes', + hours: ' hours', + days: ' days' + }, + tree: { + selectAll: 'Select All', + unSelectAll: 'Cancel Select', + expandAll: 'Expand All', + unExpandAll: 'Collapse all', + + checkStrictly: 'Hierarchical association', + checkUnStrictly: 'Hierarchical independence' + }, + upload: { + save: 'Save', + upload: 'Upload', + imgUpload: 'ImageUpload', + uploaded: 'Uploaded', + + operating: 'Operating', + del: 'Delete', + download: 'download', + saveWarn: 'Please wait for the file to upload and save!', + saveError: 'There is no file successfully uploaded and cannot be saved!', + + preview: 'Preview', + choose: 'Select the file', + + accept: 'Support {0} format', + acceptUpload: 'Only upload files in {0} format', + maxSize: 'A single file does not exceed {0}MB ', + maxSizeMultiple: 'Only upload files up to {0}MB!', + maxNumber: 'Only upload up to {0} files', + + legend: 'Legend', + fileName: 'File name', + fileSize: 'File size', + fileStatue: 'File status', + + startUpload: 'Start upload', + uploadSuccess: 'Upload successfully', + uploadError: 'Upload failed', + uploading: 'Uploading', + uploadWait: 'Please wait for the file upload to finish', + reUploadFailed: 'Re-upload failed files' + }, + verify: { + error: 'verification failed!', + time: 'The verification is successful and it takes {time} seconds!', + redoTip: 'Click the picture to refresh', + dragText: 'Hold down the slider and drag', + successText: 'Verified' + } +} diff --git a/src/locales/lang/en/layout.ts b/src/locales/lang/en/layout.ts new file mode 100644 index 00000000..7e44f323 --- /dev/null +++ b/src/locales/lang/en/layout.ts @@ -0,0 +1,114 @@ +export default { + footer: { onlinePreview: 'Preview', onlineDocument: 'Document' }, + header: { + // user dropdown + dropdownItemDoc: 'Document', + dropdownItemLoginOut: 'Log Out', + + tooltipErrorLog: 'Error log', + tooltipLock: 'Lock screen', + tooltipNotify: 'Notification', + + tooltipEntryFull: 'Full Screen', + tooltipExitFull: 'Exit Full Screen', + + // lock + lockScreenPassword: 'Lock screen password', + lockScreen: 'Lock screen', + lockScreenBtn: 'Locking', + + home: 'Home' + }, + multipleTab: { + reload: 'Refresh current', + close: 'Close current', + closeLeft: 'Close Left', + closeRight: 'Close Right', + closeOther: 'Close Other', + closeAll: 'Close All' + }, + setting: { + // content mode + contentModeFull: 'Full', + contentModeFixed: 'Fixed width', + // topMenu align + topMenuAlignLeft: 'Left', + topMenuAlignRight: 'Center', + topMenuAlignCenter: 'Right', + // menu trigger + menuTriggerNone: 'Not Show', + menuTriggerBottom: 'Bottom', + menuTriggerTop: 'Top', + // menu type + menuTypeSidebar: 'Left menu mode', + menuTypeMixSidebar: 'Left menu mixed mode', + menuTypeMix: 'Top Menu Mix mode', + menuTypeTopMenu: 'Top menu mode', + + on: 'On', + off: 'Off', + minute: 'Minute', + + operatingTitle: 'Successful!', + operatingContent: 'The copy is successful, please go to src/settings/projectSetting.ts to modify the configuration!', + resetSuccess: 'Successfully reset!', + + copyBtn: 'Copy', + clearBtn: 'Clear cache and to the login page', + + drawerTitle: 'Configuration', + + darkMode: 'Dark mode', + navMode: 'Navigation mode', + interfaceFunction: 'Interface function', + interfaceDisplay: 'Interface display', + animation: 'Animation', + splitMenu: 'Split menu', + closeMixSidebarOnChange: 'Switch page to close menu', + + sysTheme: 'System theme', + headerTheme: 'Header theme', + sidebarTheme: 'Menu theme', + + menuDrag: 'Drag Sidebar', + menuSearch: 'Menu search', + menuAccordion: 'Sidebar accordion', + menuCollapse: 'Collapse menu', + collapseMenuDisplayName: 'Collapse menu display name', + topMenuLayout: 'Top menu layout', + menuCollapseButton: 'Menu collapse button', + contentMode: 'Content area width', + expandedMenuWidth: 'Expanded menu width', + + breadcrumb: 'Breadcrumbs', + breadcrumbIcon: 'Breadcrumbs Icon', + tabs: 'Tabs', + tabDetail: 'Tab Detail', + tabsQuickBtn: 'Tabs quick button', + tabsRedoBtn: 'Tabs redo button', + tabsFoldBtn: 'Tabs flod button', + sidebar: 'Sidebar', + header: 'Header', + footer: 'Footer', + fullContent: 'Full content', + grayMode: 'Gray mode', + colorWeak: 'Color Weak Mode', + + progress: 'Progress', + switchLoading: 'Switch Loading', + switchAnimation: 'Switch animation', + animationType: 'Animation type', + + autoScreenLock: 'Auto screen lock', + notAutoScreenLock: 'Not auto lock', + + fixedHeader: 'Fixed header', + fixedSideBar: 'Fixed Sidebar', + + mixSidebarTrigger: 'Mixed menu Trigger', + triggerHover: 'Hover', + triggerClick: 'Click', + + mixSidebarFixed: 'Fixed expanded menu' + } +} diff --git a/src/locales/lang/en/routes/basic.ts b/src/locales/lang/en/routes/basic.ts new file mode 100644 index 00000000..4ddee59d --- /dev/null +++ b/src/locales/lang/en/routes/basic.ts @@ -0,0 +1,4 @@ +export default { + login: 'Login', + errorLogList: 'Error Log' +} diff --git a/src/locales/lang/en/routes/dashboard.ts b/src/locales/lang/en/routes/dashboard.ts new file mode 100644 index 00000000..5382ae31 --- /dev/null +++ b/src/locales/lang/en/routes/dashboard.ts @@ -0,0 +1,6 @@ +export default { + dashboard: 'Dashboard', + about: 'About', + workbench: 'Workbench', + analysis: 'Analysis' +} diff --git a/src/locales/lang/en/routes/demo.ts b/src/locales/lang/en/routes/demo.ts new file mode 100644 index 00000000..1bfa97cb --- /dev/null +++ b/src/locales/lang/en/routes/demo.ts @@ -0,0 +1,200 @@ +export default { + charts: { + baiduMap: 'Baidu map', + aMap: 'A map', + googleMap: 'Google map', + charts: 'Chart', + map: 'Map', + line: 'Line', + pie: 'Pie' + }, + comp: { + comp: 'Component', + basic: 'Basic', + transition: 'Animation', + countTo: 'Count To', + + scroll: 'Scroll', + scrollBasic: 'Basic', + scrollAction: 'Scroll Function', + virtualScroll: 'Virtual Scroll', + + tree: 'Tree', + + treeBasic: 'Basic', + editTree: 'Searchable/toolbar', + actionTree: 'Function operation', + + modal: 'Modal', + drawer: 'Drawer', + desc: 'Desc', + + lazy: 'Lazy', + lazyBasic: 'Basic', + lazyTransition: 'Animation', + + verify: 'Verify', + verifyDrag: 'Drag ', + verifyRotate: 'Picture Restore', + + qrcode: 'QR code', + strength: 'Password strength', + upload: 'Upload', + + loading: 'Loading', + + cropperImage: 'Cropper Image', + cardList: 'Card List' + }, + editor: { + editor: 'Editor', + jsonEditor: 'Json editor', + markdown: 'Markdown editor', + + tinymce: 'Rich text', + tinymceBasic: 'Basic', + tinymceForm: 'embedded form' + }, + excel: { + excel: 'Excel', + customExport: 'Select export format', + jsonExport: 'JSON data export', + arrayExport: 'Array data export', + importExcel: 'Import' + }, + feat: { + feat: 'Page Function', + icon: 'Icon', + tabs: 'Tabs', + tabDetail: 'Tab Detail', + sessionTimeout: 'Session Timeout', + print: 'Print', + contextMenu: 'Context Menu', + download: 'Download', + imgPreview: 'Picture Preview', + copy: 'Clipboard', + msg: 'Message prompt', + watermark: 'Watermark', + ripple: 'Ripple', + fullScreen: 'Full Screen', + errorLog: 'Error Log', + tab: 'Tab with parameters', + tab1: 'Tab with parameter 1', + tab2: 'Tab with parameter 2', + menu: 'Menu with parameters', + menu1: 'Menu with parameters 1', + menu2: 'Menu with parameters 2', + + ws: 'Websocket test', + + breadcrumb: 'Breadcrumbs', + breadcrumbFlat: 'Flat Mode', + breadcrumbFlatDetail: 'Flat mode details', + requestDemo: 'Retry request demo', + + breadcrumbChildren: 'Level mode', + breadcrumbChildrenDetail: 'Level mode detail' + }, + flow: { + name: 'Graphics editor', + flowChart: 'FlowChart' + }, + form: { + form: 'Form', + basic: 'Basic', + useForm: 'useForm', + refForm: 'RefForm', + advancedForm: 'Shrinkable', + ruleForm: 'Form validation', + dynamicForm: 'Dynamic', + customerForm: 'Custom', + appendForm: 'Append', + tabsForm: 'TabsForm' + }, + iframe: { + frame: 'External', + antv: 'antVue doc (embedded)', + doc: 'Project doc (embedded)', + docExternal: 'Project doc (external)' + }, + level: { level: 'MultiMenu' }, + page: { + page: 'Page', + + form: 'Form', + formBasic: 'Basic Form', + formStep: 'Step Form', + formHigh: 'Advanced Form', + + desc: 'Details', + descBasic: 'Basic Details', + descHigh: 'Advanced Details', + + result: 'Result', + resultSuccess: 'Success', + resultFail: 'Failed', + + account: 'Personal', + accountCenter: 'Personal Center', + accountSetting: 'Personal Settings', + + exception: 'Exception', + netWorkError: 'Network Error', + notData: 'No data', + + list: 'List page', + listCard: 'Card list', + basic: 'Basic list', + listBasic: 'Basic list', + listSearch: 'Search list' + }, + permission: { + permission: 'Permission', + + front: 'front-end', + frontPage: 'Page', + frontBtn: 'Button', + frontTestA: 'Test page A', + frontTestB: 'Test page B', + + back: 'background', + backPage: 'Page', + backBtn: 'Button' + }, + setup: { + page: 'Intro page' + }, + system: { + moduleName: 'System management', + + account: 'Account management', + account_detail: 'Account detail', + password: 'Change password', + + dept: 'Department management', + + menu: 'Menu management', + role: 'Role management' + }, + table: { + table: 'Table', + + basic: 'Basic', + treeTable: 'Tree', + fetchTable: 'Remote loading', + fixedColumn: 'Fixed column', + customerCell: 'Custom column', + formTable: 'Open search', + useTable: 'UseTable', + refTable: 'RefTable', + multipleHeader: 'MultiLevel header', + mergeHeader: 'Merge cells', + expandTable: 'Expandable table', + fixedHeight: 'Fixed height', + footerTable: 'Footer', + editCellTable: 'Editable cell', + editRowTable: 'Editable row', + authColumn: 'Auth column', + resizeParentHeightTable: 'resizeParentHeightTable' + } +} diff --git a/src/locales/lang/en/sys.ts b/src/locales/lang/en/sys.ts new file mode 100644 index 00000000..c6835049 --- /dev/null +++ b/src/locales/lang/en/sys.ts @@ -0,0 +1,104 @@ +export default { + api: { + operationSuccess: 'Operation Success', + operationFailed: 'Operation failed', + errorTip: 'Error Tip', + successTip: 'Success Tip', + errorMessage: 'The operation failed, the system is abnormal!', + timeoutMessage: 'Login timed out, please log in again!', + apiTimeoutMessage: 'The interface request timed out, please refresh the page and try again!', + apiRequestFailed: 'The interface request failed, please try again later!', + networkException: 'network anomaly', + networkExceptionMsg: 'Please check if your network connection is normal! The network is abnormal', + + errMsg401: 'The user does not have permission (token, user name, password error)!', + errMsg403: 'The user is authorized, but access is forbidden!', + errMsg404: 'Network request error, the resource was not found!', + errMsg405: 'Network request error, request method not allowed!', + errMsg408: 'Network request timed out!', + errMsg500: 'Server error, please contact the administrator!', + errMsg501: 'The network is not implemented!', + errMsg502: 'Network Error!', + errMsg503: 'The service is unavailable, the server is temporarily overloaded or maintained!', + errMsg504: 'Network timeout!', + errMsg505: 'The http version does not support the request!' + }, + app: { + logoutTip: 'Reminder', + logoutMessage: 'Confirm to exit the system?', + menuLoading: 'Menu loading...' + }, + errorLog: { + tableTitle: 'Error log list', + tableColumnType: 'Type', + tableColumnDate: 'Time', + tableColumnFile: 'File', + tableColumnMsg: 'Error message', + tableColumnStackMsg: 'Stack info', + + tableActionDesc: 'Details', + + modalTitle: 'Error details', + + fireVueError: 'Fire vue error', + fireResourceError: 'Fire resource error', + fireAjaxError: 'Fire ajax error', + + enableMessage: 'Only effective when useErrorHandle=true in `/src/settings/projectSetting.ts`.' + }, + exception: { + backLogin: 'Back Login', + backHome: 'Back Home', + subTitle403: "Sorry, you don't have access to this page.", + subTitle404: 'Sorry, the page you visited does not exist.', + subTitle500: 'Sorry, the server is reporting an error.', + noDataTitle: 'No data on the current page.', + networkErrorTitle: 'Network Error', + networkErrorSubTitle: 'Sorry,Your network connection has been disconnected, please check your network!' + }, + lock: { + unlock: 'Click to unlock', + alert: 'Lock screen password error', + backToLogin: 'Back to login', + entry: 'Enter the system', + placeholder: 'Please enter the lock screen password or user password' + }, + login: { + backSignIn: 'Back sign in', + mobileSignInFormTitle: 'Mobile sign in', + qrSignInFormTitle: 'Qr code sign in', + signInFormTitle: 'Sign in', + signUpFormTitle: 'Sign up', + forgetFormTitle: 'Reset password', + + signInTitle: 'Backstage management system', + signInDesc: 'Enter your personal details and get started!', + policy: 'I agree to the xxx Privacy Policy', + scanSign: `scanning the code to complete the login`, + + loginButton: 'Sign in', + registerButton: 'Sign up', + rememberMe: 'Remember me', + forgetPassword: 'Forget Password?', + otherSignIn: 'Sign in with', + + // notify + loginSuccessTitle: 'Login successful', + loginSuccessDesc: 'Welcome back', + + // placeholder + accountPlaceholder: 'Please input username', + passwordPlaceholder: 'Please input password', + smsPlaceholder: 'Please input sms code', + mobilePlaceholder: 'Please input mobile', + policyPlaceholder: 'Register after checking', + diffPwd: 'The two passwords are inconsistent', + + userName: 'Username', + password: 'Password', + confirmPassword: 'Confirm Password', + email: 'Email', + smsCode: 'SMS code', + mobile: 'Mobile' + } +} diff --git a/src/locales/lang/zh-CN/antdLocale/DatePicker.ts b/src/locales/lang/zh-CN/antdLocale/DatePicker.ts new file mode 100644 index 00000000..8820ee51 --- /dev/null +++ b/src/locales/lang/zh-CN/antdLocale/DatePicker.ts @@ -0,0 +1,6 @@ +export default { + lang: { + shortWeekDays: ['一', '二', '三', '四', '五', '六', '日'], + shortMonths: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'] + } +} diff --git a/src/locales/lang/zh-CN/common.ts b/src/locales/lang/zh-CN/common.ts new file mode 100644 index 00000000..17e36b2f --- /dev/null +++ b/src/locales/lang/zh-CN/common.ts @@ -0,0 +1,20 @@ +export default { + okText: '确认', + closeText: '关闭', + cancelText: '取消', + loadingText: '加载中...', + saveText: '保存', + delText: '删除', + resetText: '重置', + searchText: '搜索', + queryText: '查询', + + inputText: '请输入', + chooseText: '请选择', + + redo: '刷新', + back: '返回', + + light: '亮色主题', + dark: '黑暗主题' +} diff --git a/src/locales/lang/zh-CN/component.ts b/src/locales/lang/zh-CN/component.ts new file mode 100644 index 00000000..c58b4f60 --- /dev/null +++ b/src/locales/lang/zh-CN/component.ts @@ -0,0 +1,134 @@ +export default { + app: { + searchNotData: '暂无搜索结果', + toSearch: '确认', + toNavigate: '切换' + }, + countdown: { + normalText: '获取验证码', + sendText: '{0}秒后重新获取' + }, + cropper: { + selectImage: '选择图片', + uploadSuccess: '上传成功', + modalTitle: '头像上传', + okText: '确认并上传', + btn_reset: '重置', + btn_rotate_left: '逆时针旋转', + btn_rotate_right: '顺时针旋转', + btn_scale_x: '水平翻转', + btn_scale_y: '垂直翻转', + btn_zoom_in: '放大', + btn_zoom_out: '缩小', + preview: '预览' + }, + drawer: { + loadingText: '加载中...', + cancelText: '关闭', + okText: '确认' + }, + excel: { + exportModalTitle: '导出数据', + fileType: '文件类型', + fileName: '文件名' + }, + form: { + putAway: '收起', + unfold: '展开', + + maxTip: '字符数应小于{0}位', + + apiSelectNotFound: '请等待数据加载完成...' + }, + icon: { + placeholder: '点击选择图标', + search: '搜索图标', + copy: '复制图标成功!' + }, + menu: { + search: '菜单搜索' + }, + modal: { + cancelText: '关闭', + okText: '确认', + close: '关闭', + maximize: '最大化', + restore: '还原' + }, + table: { + settingDens: '密度', + settingDensDefault: '默认', + settingDensMiddle: '中等', + settingDensSmall: '紧凑', + settingColumn: '列设置', + settingColumnShow: '列展示', + settingIndexColumnShow: '序号列', + settingSelectColumnShow: '勾选列', + settingFixedLeft: '固定到左侧', + settingFixedRight: '固定到右侧', + settingFullScreen: '全屏', + + index: '序号', + + total: '共 {total} 条数据' + }, + time: { + before: '前', + after: '后', + just: '刚刚', + seconds: '秒', + minutes: '分钟', + hours: '小时', + days: '天' + }, + tree: { + selectAll: '选择全部', + unSelectAll: '取消选择', + expandAll: '展开全部', + unExpandAll: '折叠全部', + checkStrictly: '层级关联', + checkUnStrictly: '层级独立' + }, + upload: { + save: '保存', + upload: '上传', + imgUpload: '图片上传', + uploaded: '已上传', + + operating: '操作', + del: '删除', + download: '下载', + saveWarn: '请等待文件上传后,保存!', + saveError: '没有上传成功的文件,无法保存!', + + preview: '预览', + choose: '选择文件', + + accept: '支持{0}格式', + acceptUpload: '只能上传{0}格式文件', + maxSize: '单个文件不超过{0}MB', + maxSizeMultiple: '只能上传不超过{0}MB的文件!', + maxNumber: '最多只能上传{0}个文件', + + legend: '略缩图', + fileName: '文件名', + fileSize: '文件大小', + fileStatue: '状态', + + startUpload: '开始上传', + uploadSuccess: '上传成功', + uploadError: '上传失败', + uploading: '上传中', + uploadWait: '请等待文件上传结束后操作', + reUploadFailed: '重新上传失败文件' + }, + verify: { + error: '验证失败!', + time: '验证校验成功,耗时{time}秒!', + + redoTip: '点击图片可刷新', + + dragText: '请按住滑块拖动', + successText: '验证通过' + } +} diff --git a/src/locales/lang/zh-CN/layout.ts b/src/locales/lang/zh-CN/layout.ts new file mode 100644 index 00000000..fa7770ad --- /dev/null +++ b/src/locales/lang/zh-CN/layout.ts @@ -0,0 +1,115 @@ +export default { + footer: { onlinePreview: '在线预览', onlineDocument: '在线文档' }, + header: { + // user dropdown + dropdownItemDoc: '文档', + dropdownItemLoginOut: '退出系统', + + // tooltip + tooltipErrorLog: '错误日志', + tooltipLock: '锁定屏幕', + tooltipNotify: '消息通知', + + tooltipEntryFull: '全屏', + tooltipExitFull: '退出全屏', + + // lock + lockScreenPassword: '锁屏密码', + lockScreen: '锁定屏幕', + lockScreenBtn: '锁定', + + home: '首页' + }, + multipleTab: { + reload: '重新加载', + close: '关闭标签页', + closeLeft: '关闭左侧标签页', + closeRight: '关闭右侧标签页', + closeOther: '关闭其它标签页', + closeAll: '关闭全部标签页' + }, + setting: { + // content mode + contentModeFull: '流式', + contentModeFixed: '定宽', + // topMenu align + topMenuAlignLeft: '居左', + topMenuAlignRight: '居中', + topMenuAlignCenter: '居右', + // menu trigger + menuTriggerNone: '不显示', + menuTriggerBottom: '底部', + menuTriggerTop: '顶部', + // menu type + menuTypeSidebar: '左侧菜单模式', + menuTypeMixSidebar: '左侧菜单混合模式', + menuTypeMix: '顶部菜单混合模式', + menuTypeTopMenu: '顶部菜单模式', + + on: '开', + off: '关', + minute: '分钟', + + operatingTitle: '操作成功', + operatingContent: '复制成功,请到 src/settings/projectSetting.ts 中修改配置!', + resetSuccess: '重置成功!', + + copyBtn: '拷贝', + clearBtn: '清空缓存并返回登录页', + + drawerTitle: '项目配置', + + darkMode: '主题', + navMode: '导航栏模式', + interfaceFunction: '界面功能', + interfaceDisplay: '界面显示', + animation: '动画', + splitMenu: '分割菜单', + closeMixSidebarOnChange: '切换页面关闭菜单', + + sysTheme: '系统主题', + headerTheme: '顶栏主题', + sidebarTheme: '菜单主题', + + menuDrag: '侧边菜单拖拽', + menuSearch: '菜单搜索', + menuAccordion: '侧边菜单手风琴模式', + menuCollapse: '折叠菜单', + collapseMenuDisplayName: '折叠菜单显示名称', + topMenuLayout: '顶部菜单布局', + menuCollapseButton: '菜单折叠按钮', + contentMode: '内容区域宽度', + expandedMenuWidth: '菜单展开宽度', + + breadcrumb: '面包屑', + breadcrumbIcon: '面包屑图标', + tabs: '标签页', + tabDetail: '标签详情页', + tabsQuickBtn: '标签页快捷按钮', + tabsRedoBtn: '标签页刷新按钮', + tabsFoldBtn: '标签页折叠按钮', + sidebar: '左侧菜单', + header: '顶栏', + footer: '页脚', + fullContent: '全屏内容', + grayMode: '灰色模式', + colorWeak: '色弱模式', + + progress: '顶部进度条', + switchLoading: '切换loading', + switchAnimation: '切换动画', + animationType: '动画类型', + + autoScreenLock: '自动锁屏', + notAutoScreenLock: '不自动锁屏', + + fixedHeader: '固定header', + fixedSideBar: '固定Sidebar', + + mixSidebarTrigger: '混合菜单触发方式', + triggerHover: '悬停', + triggerClick: '点击', + + mixSidebarFixed: '固定展开菜单' + } +} diff --git a/src/locales/lang/zh-CN/routes/basic.ts b/src/locales/lang/zh-CN/routes/basic.ts new file mode 100644 index 00000000..fe7a18dc --- /dev/null +++ b/src/locales/lang/zh-CN/routes/basic.ts @@ -0,0 +1,4 @@ +export default { + login: '登录', + errorLogList: '错误日志列表' +} diff --git a/src/locales/lang/zh-CN/routes/dashboard.ts b/src/locales/lang/zh-CN/routes/dashboard.ts new file mode 100644 index 00000000..3a53ad61 --- /dev/null +++ b/src/locales/lang/zh-CN/routes/dashboard.ts @@ -0,0 +1,6 @@ +export default { + dashboard: 'Dashboard', + about: '关于', + workbench: '工作台', + analysis: '分析页' +} diff --git a/src/locales/lang/zh-CN/routes/demo.ts b/src/locales/lang/zh-CN/routes/demo.ts new file mode 100644 index 00000000..45fe2161 --- /dev/null +++ b/src/locales/lang/zh-CN/routes/demo.ts @@ -0,0 +1,191 @@ +export default { + charts: { + baiduMap: '百度地图', + aMap: '高德地图', + googleMap: '谷歌地图', + charts: '图表', + map: '地图', + line: '折线图', + pie: '饼图' + }, + comp: { + comp: '组件', + basic: '基础组件', + transition: '动画组件', + countTo: '数字动画', + + scroll: '滚动组件', + scrollBasic: '基础滚动', + scrollAction: '滚动函数', + virtualScroll: '虚拟滚动', + + tree: 'Tree', + treeBasic: '基础树', + editTree: '可搜索/工具栏', + actionTree: '函数操作示例', + + modal: '弹窗扩展', + drawer: '抽屉扩展', + desc: '详情组件', + + lazy: '懒加载组件', + lazyBasic: '基础示例', + lazyTransition: '动画效果', + + verify: '验证组件', + verifyDrag: '拖拽校验', + verifyRotate: '图片还原', + + qrcode: '二维码组件', + strength: '密码强度组件', + upload: '上传组件', + + loading: 'Loading', + + cropperImage: '图片裁剪', + cardList: '卡片列表' + }, + editor: { + editor: '编辑器', + jsonEditor: 'Json编辑器', + markdown: 'markdown编辑器', + + tinymce: '富文本', + tinymceBasic: '基础使用', + tinymceForm: '嵌入form' + }, + excel: { + excel: 'Excel', + customExport: '选择导出格式', + jsonExport: 'JSON数据导出', + arrayExport: 'Array数据导出', + importExcel: '导入' + }, + feat: { + feat: '功能', + icon: '图标', + sessionTimeout: '登录过期', + tabs: '标签页操作', + tabDetail: '标签详情页', + print: '打印', + contextMenu: '右键菜单', + download: '文件下载', + imgPreview: '图片预览', + copy: '剪切板', + msg: '消息提示', + watermark: '水印', + ripple: '水波纹', + fullScreen: '全屏', + errorLog: '错误日志', + tab: 'Tab带参', + tab1: 'Tab带参1', + tab2: 'Tab带参2', + menu: 'Menu带参', + menu1: 'Menu带参1', + menu2: 'Menu带参2', + ws: 'websocket测试', + breadcrumb: '面包屑导航', + breadcrumbFlat: '平级模式', + requestDemo: '测试请求重试', + breadcrumbFlatDetail: '平级详情', + breadcrumbChildren: '层级模式', + breadcrumbChildrenDetail: '层级详情' + }, + flow: { + name: '图形编辑器', + flowChart: '流程图' + }, + form: { + form: 'Form', + basic: '基础表单', + useForm: 'useForm', + refForm: 'RefForm', + advancedForm: '可收缩表单', + ruleForm: '表单验证', + dynamicForm: '动态表单', + customerForm: '自定义组件', + appendForm: '表单增删示例', + tabsForm: '标签页+多级field' + }, + iframe: { + frame: '外部页面', + antv: 'antVue文档(内嵌)', + doc: '项目文档(内嵌)', + docExternal: '项目文档(外链)' + }, + level: { level: '多级菜单' }, + page: { + page: '页面', + + form: '表单页', + formBasic: '基础表单', + formStep: '分步表单', + formHigh: '高级表单', + + desc: '详情页', + descBasic: '基础详情页', + descHigh: '高级详情页', + + result: '结果页', + resultSuccess: '成功页', + resultFail: '失败页', + + account: '个人页', + accountCenter: '个人中心', + accountSetting: '个人设置', + + exception: '异常页', + netWorkError: '网络错误', + notData: '无数据', + + list: '列表页', + listCard: '卡片列表', + listBasic: '标准列表', + listSearch: '搜索列表' + }, + permission: { + permission: '权限管理', + + front: '基于前端权限', + frontPage: '页面权限', + frontBtn: '按钮权限', + frontTestA: '权限测试页A', + frontTestB: '权限测试页B', + + back: '基于后台权限', + backPage: '页面权限', + backBtn: '按钮权限' + }, + setup: { + page: '引导页' + }, + system: { + moduleName: '系统管理', + account: '账号管理', + account_detail: '账号详情', + password: '修改密码', + dept: '部门管理', + menu: '菜单管理', + role: '角色管理' + }, + table: { + table: 'Table', + basic: '基础表格', + treeTable: '树形表格', + fetchTable: '远程加载示例', + fixedColumn: '固定列', + customerCell: '自定义列', + formTable: '开启搜索区域', + useTable: 'UseTable', + refTable: 'RefTable', + multipleHeader: '多级表头', + mergeHeader: '合并单元格', + expandTable: '可展开表格', + fixedHeight: '定高/头部自定义', + footerTable: '表尾行合计', + editCellTable: '可编辑单元格', + editRowTable: '可编辑行', + authColumn: '权限列', + resizeParentHeightTable: '继承父元素高度' + } +} diff --git a/src/locales/lang/zh-CN/sys.ts b/src/locales/lang/zh-CN/sys.ts new file mode 100644 index 00000000..a544de6d --- /dev/null +++ b/src/locales/lang/zh-CN/sys.ts @@ -0,0 +1,100 @@ +export default { + api: { + operationSuccess: '操作成功', + operationFailed: '操作失败', + errorTip: '错误提示', + successTip: '成功提示', + errorMessage: '操作失败,系统异常!', + timeoutMessage: '登录超时,请重新登录!', + apiTimeoutMessage: '接口请求超时,请刷新页面重试!', + apiRequestFailed: '请求出错,请稍候重试', + networkException: '网络异常', + networkExceptionMsg: '网络异常,请检查您的网络连接是否正常!', + + errMsg401: '用户没有权限(令牌、用户名、密码错误)!', + errMsg403: '用户得到授权,但是访问是被禁止的。!', + errMsg404: '网络请求错误,未找到该资源!', + errMsg405: '网络请求错误,请求方法未允许!', + errMsg408: '网络请求超时!', + errMsg500: '服务器错误,请联系管理员!', + errMsg501: '网络未实现!', + errMsg502: '网络错误!', + errMsg503: '服务不可用,服务器暂时过载或维护!', + errMsg504: '网络超时!', + errMsg505: 'http版本不支持该请求!' + }, + app: { logoutTip: '温馨提醒', logoutMessage: '是否确认退出系统?', menuLoading: '菜单加载中...' }, + errorLog: { + tableTitle: '错误日志列表', + tableColumnType: '类型', + tableColumnDate: '时间', + tableColumnFile: '文件', + tableColumnMsg: '错误信息', + tableColumnStackMsg: 'stack信息', + + tableActionDesc: '详情', + + modalTitle: '错误详情', + + fireVueError: '点击触发vue错误', + fireResourceError: '点击触发资源加载错误', + fireAjaxError: '点击触发ajax错误', + + enableMessage: '只在`/src/settings/projectSetting.ts` 内的useErrorHandle=true时生效.' + }, + exception: { + backLogin: '返回登录', + backHome: '返回首页', + subTitle403: '抱歉,您无权访问此页面。', + subTitle404: '抱歉,您访问的页面不存在。', + subTitle500: '抱歉,服务器报告错误。', + noDataTitle: '当前页无数据', + networkErrorTitle: '网络错误', + networkErrorSubTitle: '抱歉,您的网络连接已断开,请检查您的网络!' + }, + lock: { + unlock: '点击解锁', + alert: '锁屏密码错误', + backToLogin: '返回登录', + entry: '进入系统', + placeholder: '请输入锁屏密码或者用户密码' + }, + login: { + backSignIn: '返回', + signInFormTitle: '登录', + mobileSignInFormTitle: '手机登录', + qrSignInFormTitle: '二维码登录', + signUpFormTitle: '注册', + forgetFormTitle: '重置密码', + + signInTitle: '开箱即用的中后台管理系统', + signInDesc: '输入您的个人详细信息开始使用!', + policy: '我同意xxx隐私政策', + scanSign: `扫码后点击"确认",即可完成登录`, + + loginButton: '登录', + registerButton: '注册', + rememberMe: '记住我', + forgetPassword: '忘记密码?', + otherSignIn: '其他登录方式', + + // notify + loginSuccessTitle: '登录成功', + loginSuccessDesc: '欢迎回来', + + // placeholder + accountPlaceholder: '请输入账号', + passwordPlaceholder: '请输入密码', + smsPlaceholder: '请输入验证码', + mobilePlaceholder: '请输入手机号码', + policyPlaceholder: '勾选后才能注册', + diffPwd: '两次输入密码不一致', + + userName: '账号', + password: '密码', + confirmPassword: '确认密码', + email: '邮箱', + smsCode: '短信验证码', + mobile: '手机号码' + } +} diff --git a/src/locales/lang/zh_CN.ts b/src/locales/lang/zh_CN.ts new file mode 100644 index 00000000..04643ae0 --- /dev/null +++ b/src/locales/lang/zh_CN.ts @@ -0,0 +1,10 @@ +import { genMessage } from '../helper' +import antdLocale from 'ant-design-vue/es/locale/zh_CN' + +const modules = import.meta.globEager('./zh-CN/**/*.ts') +export default { + message: { + ...genMessage(modules, 'zh-CN'), + antdLocale + } +} diff --git a/src/locales/setupI18n.ts b/src/locales/setupI18n.ts new file mode 100644 index 00000000..fd261d30 --- /dev/null +++ b/src/locales/setupI18n.ts @@ -0,0 +1,44 @@ +import type { App } from 'vue' +import type { I18n, I18nOptions } from 'vue-i18n' + +import { createI18n } from 'vue-i18n' +import { setHtmlPageLang, setLoadLocalePool } from './helper' +import { localeSetting } from '@/settings/localeSetting' +import { useLocaleStoreWithOut } from '@/store/modules/locale' + +const { fallback, availableLocales } = localeSetting + +export let i18n: ReturnType + +async function createI18nOptions(): Promise { + const localeStore = useLocaleStoreWithOut() + const locale = localeStore.getLocale + const defaultLocal = await import(`./lang/${locale}.ts`) + const message = defaultLocal.default?.message ?? {} + + setHtmlPageLang(locale) + setLoadLocalePool((loadLocalePool) => { + loadLocalePool.push(locale) + }) + + return { + legacy: false, + locale, + fallbackLocale: fallback, + messages: { + [locale]: message + }, + availableLocales: availableLocales, + sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false. + silentTranslationWarn: true, // true - warning off + missingWarn: false, + silentFallbackWarn: true + } +} + +// setup i18n instance with glob +export async function setupI18n(app: App) { + const options = await createI18nOptions() + i18n = createI18n(options) as I18n + app.use(i18n) +} diff --git a/src/locales/useLocale.ts b/src/locales/useLocale.ts new file mode 100644 index 00000000..f131145b --- /dev/null +++ b/src/locales/useLocale.ts @@ -0,0 +1,69 @@ +/** + * Multi-language related operations + */ +import type { LocaleType } from '@/types/config' + +import { i18n } from './setupI18n' +import { useLocaleStoreWithOut } from '@/store/modules/locale' +import { unref, computed } from 'vue' +import { loadLocalePool, setHtmlPageLang } from './helper' + +interface LangModule { + message: Recordable + dateLocale: Recordable + dateLocaleName: string +} + +function setI18nLanguage(locale: LocaleType) { + const localeStore = useLocaleStoreWithOut() + + if (i18n.mode === 'legacy') { + i18n.global.locale = locale + } else { + ;(i18n.global.locale as any).value = locale + } + localeStore.setLocaleInfo({ locale }) + setHtmlPageLang(locale) +} + +export function useLocale() { + const localeStore = useLocaleStoreWithOut() + const getLocale = computed(() => localeStore.getLocale) + const getShowLocalePicker = computed(() => localeStore.getShowPicker) + + const getAntdLocale = computed((): any => { + return i18n.global.getLocaleMessage(unref(getLocale))?.antdLocale ?? {} + }) + + // Switching the language will change the locale of useI18n + // And submit to configuration modification + async function changeLocale(locale: LocaleType) { + const globalI18n = i18n.global + const currentLocale = unref(globalI18n.locale) + if (currentLocale === locale) { + return locale + } + + if (loadLocalePool.includes(locale)) { + setI18nLanguage(locale) + return locale + } + const langModule = ((await import(`./lang/${locale}.ts`)) as any).default as LangModule + if (!langModule) return + + const { message } = langModule + + globalI18n.setLocaleMessage(locale, message) + loadLocalePool.push(locale) + + setI18nLanguage(locale) + return locale + } + + return { + getLocale, + getShowLocalePicker, + changeLocale, + getAntdLocale + } +} diff --git a/src/logics/error-handle/index.ts b/src/logics/error-handle/index.ts new file mode 100644 index 00000000..2f06099a --- /dev/null +++ b/src/logics/error-handle/index.ts @@ -0,0 +1,178 @@ +/** + * Used to configure the global error handling function, which can monitor vue errors, script errors, static resource errors and Promise errors + */ + +import type { ErrorLogInfo } from '@/types/store' + +import { useErrorLogStoreWithOut } from '@/store/modules/errorLog' + +import { ErrorTypeEnum } from '@/enums/exceptionEnum' +import { App } from 'vue' +import projectSetting from '@/settings/projectSetting' + +/** + * Handling error stack information + * @param error + */ +function processStackMsg(error: Error) { + if (!error.stack) { + return '' + } + let stack = error.stack + .replace(/\n/gi, '') // Remove line breaks to save the size of the transmitted content + .replace(/\bat\b/gi, '@') // At in chrome, @ in ff + .split('@') // Split information with @ + .slice(0, 9) // The maximum stack length (Error.stackTraceLimit = 10), so only take the first 10 + .map((v) => v.replace(/^\s*|\s*$/g, '')) // Remove extra spaces + .join('~') // Manually add separators for later display + .replace(/\?[^:]+/gi, '') // Remove redundant parameters of js file links (?x=1 and the like) + const msg = error.toString() + if (stack.indexOf(msg) < 0) { + stack = msg + '@' + stack + } + return stack +} + +/** + * get comp name + * @param vm + */ +function formatComponentName(vm: any) { + if (vm.$root === vm) { + return { + name: 'root', + path: 'root' + } + } + + const options = vm.$options as any + if (!options) { + return { + name: 'anonymous', + path: 'anonymous' + } + } + const name = options.name || options._componentTag + return { + name: name, + path: options.__file + } +} + +/** + * Configure Vue error handling function + */ + +function vueErrorHandler(err: Error, vm: any, info: string) { + const errorLogStore = useErrorLogStoreWithOut() + const { name, path } = formatComponentName(vm) + errorLogStore.addErrorLogInfo({ + type: ErrorTypeEnum.VUE, + name, + file: path, + message: err.message, + stack: processStackMsg(err), + detail: info, + url: window.location.href + }) +} + +/** + * Configure script error handling function + */ +export function scriptErrorHandler(event: Event | string, source?: string, lineno?: number, colno?: number, error?: Error) { + if (event === 'Script error.' && !source) { + return false + } + const errorInfo: Partial = {} + colno = colno || (window.event && (window.event as any).errorCharacter) || 0 + errorInfo.message = event as string + if (error?.stack) { + errorInfo.stack = error.stack + } else { + errorInfo.stack = '' + } + const name = source ? source.substr(source.lastIndexOf('/') + 1) : 'script' + const errorLogStore = useErrorLogStoreWithOut() + errorLogStore.addErrorLogInfo({ + type: ErrorTypeEnum.SCRIPT, + name: name, + file: source as string, + detail: 'lineno' + lineno, + url: window.location.href, + ...(errorInfo as Pick) + }) + return true +} + +/** + * Configure Promise error handling function + */ +function registerPromiseErrorHandler() { + window.addEventListener( + 'unhandledrejection', + function (event) { + const errorLogStore = useErrorLogStoreWithOut() + errorLogStore.addErrorLogInfo({ + type: ErrorTypeEnum.PROMISE, + name: 'Promise Error!', + file: 'none', + detail: 'promise error!', + url: window.location.href, + stack: 'promise error!', + message: event.reason + }) + }, + true + ) +} + +/** + * Configure monitoring resource loading error handling function + */ +function registerResourceErrorHandler() { + // Monitoring resource loading error(img,script,css,and jsonp) + window.addEventListener( + 'error', + function (e: Event) { + const target = e.target ? e.target : (e.srcElement as any) + const errorLogStore = useErrorLogStoreWithOut() + errorLogStore.addErrorLogInfo({ + type: ErrorTypeEnum.RESOURCE, + name: 'Resource Error!', + file: (e.target || ({} as any)).currentSrc, + detail: JSON.stringify({ + tagName: target.localName, + html: target.outerHTML, + type: e.type + }), + url: window.location.href, + stack: 'resource is not found', + message: (e.target || ({} as any)).localName + ' is load error' + }) + }, + true + ) +} + +/** + * Configure global error handling + * @param app + */ +export function setupErrorHandle(app: App) { + const { useErrorHandle } = projectSetting + if (!useErrorHandle) { + return + } + // Vue exception monitoring; + app.config.errorHandler = vueErrorHandler + + // script error + window.onerror = scriptErrorHandler + + // promise exception + registerPromiseErrorHandler() + + // Static resource exception + registerResourceErrorHandler() +} diff --git a/src/logics/initAppConfig.ts b/src/logics/initAppConfig.ts new file mode 100644 index 00000000..e2b37cef --- /dev/null +++ b/src/logics/initAppConfig.ts @@ -0,0 +1,84 @@ +/** + * Application configuration + */ +import type { ProjectConfig } from '@/types/config' + +import { PROJ_CFG_KEY } from '@/enums/cacheEnum' +import projectSetting from '@/settings/projectSetting' + +import { updateHeaderBgColor, updateSidebarBgColor } from '@/logics/theme/updateBackground' +import { updateColorWeak } from '@/logics/theme/updateColorWeak' +import { updateGrayMode } from '@/logics/theme/updateGrayMode' +import { updateDarkTheme } from '@/logics/theme/dark' +import { changeTheme } from '@/logics/theme' + +import { useAppStore } from '@/store/modules/app' +import { useLocaleStore } from '@/store/modules/locale' + +import { getCommonStoragePrefix, getStorageShortName } from '@/utils/env' + +import { primaryColor } from '../../build/config/themeConfig' +import { Persistent } from '@/utils/cache/persistent' +import { deepMerge } from '@/utils' +import { ThemeEnum } from '@/enums/appEnum' + +// Initial project configuration +export function initAppConfigStore() { + const localeStore = useLocaleStore() + const appStore = useAppStore() + let projCfg: ProjectConfig = Persistent.getLocal(PROJ_CFG_KEY) as ProjectConfig + projCfg = deepMerge(projectSetting, projCfg || {}) + const darkMode = appStore.getDarkMode + const { + colorWeak, + grayMode, + themeColor, + + headerSetting: { bgColor: headerBgColor } = {}, + menuSetting: { bgColor } = {} + } = projCfg + try { + if (themeColor && themeColor !== primaryColor) { + changeTheme(themeColor) + } + + grayMode && updateGrayMode(grayMode) + colorWeak && updateColorWeak(colorWeak) + } catch (error) { + console.log(error) + } + appStore.setProjectConfig(projCfg) + + // init dark mode + updateDarkTheme(darkMode) + if (darkMode === ThemeEnum.DARK) { + updateHeaderBgColor() + updateSidebarBgColor() + } else { + headerBgColor && updateHeaderBgColor(headerBgColor) + bgColor && updateSidebarBgColor(bgColor) + } + // init store + localeStore.initLocale() + + setTimeout(() => { + clearObsoleteStorage() + }, 16) +} + +/** + * As the version continues to iterate, there will be more and more cache keys stored in localStorage. + * This method is used to delete useless keys + */ +export function clearObsoleteStorage() { + const commonPrefix = getCommonStoragePrefix() + const shortPrefix = getStorageShortName() + + ;[localStorage, sessionStorage].forEach((item: Storage) => { + Object.keys(item).forEach((key) => { + if (key && key.startsWith(commonPrefix) && !key.startsWith(shortPrefix)) { + item.removeItem(key) + } + }) + }) +} diff --git a/src/logics/mitt/routeChange.ts b/src/logics/mitt/routeChange.ts new file mode 100644 index 00000000..c16cf502 --- /dev/null +++ b/src/logics/mitt/routeChange.ts @@ -0,0 +1,28 @@ +/** + * Used to monitor routing changes to change the status of menus and tabs. There is no need to monitor the route, because the route status change is affected by the page rendering time, which will be slow + */ + +import mitt from '@/utils/mitt' +import type { RouteLocationNormalized } from 'vue-router' +import { getRawRoute } from '@/utils' + +const emitter = mitt() + +const key = Symbol() + +let lastChangeTab: RouteLocationNormalized + +export function setRouteChange(lastChangeRoute: RouteLocationNormalized) { + const r = getRawRoute(lastChangeRoute) + emitter.emit(key, r) + lastChangeTab = r +} + +export function listenerRouteChange(callback: (route: RouteLocationNormalized) => void, immediate = true) { + emitter.on(key, callback) + immediate && lastChangeTab && callback(lastChangeTab) +} + +export function removeTabChangeListener() { + emitter.clear() +} diff --git a/src/logics/theme/dark.ts b/src/logics/theme/dark.ts new file mode 100644 index 00000000..0de5e252 --- /dev/null +++ b/src/logics/theme/dark.ts @@ -0,0 +1,24 @@ +import { darkCssIsReady, loadDarkThemeCss } from '@kirklin/vite-plugin-vben-theme/es/client' +import { addClass, hasClass, removeClass } from '@/utils/domUtils' + +export async function updateDarkTheme(mode: string | null = 'light') { + const htmlRoot = document.getElementById('htmlRoot') + if (!htmlRoot) { + return + } + const hasDarkClass = hasClass(htmlRoot, 'dark') + if (mode === 'dark') { + if (import.meta.env.PROD && !darkCssIsReady) { + await loadDarkThemeCss() + } + htmlRoot.setAttribute('data-theme', 'dark') + if (!hasDarkClass) { + addClass(htmlRoot, 'dark') + } + } else { + htmlRoot.setAttribute('data-theme', 'light') + if (hasDarkClass) { + removeClass(htmlRoot, 'dark') + } + } +} diff --git a/src/logics/theme/index.ts b/src/logics/theme/index.ts new file mode 100644 index 00000000..e451c681 --- /dev/null +++ b/src/logics/theme/index.ts @@ -0,0 +1,17 @@ +import { getThemeColors, generateColors } from '../../../build/config/themeConfig' + +import { replaceStyleVariables } from '@kirklin/vite-plugin-vben-theme/es/client' +import { mixLighten, mixDarken, tinycolor } from '@kirklin/vite-plugin-vben-theme/es/colorUtils' + +export async function changeTheme(color: string) { + const colors = generateColors({ + mixDarken, + mixLighten, + tinycolor, + color + }) + + return await replaceStyleVariables({ + colorVariables: [...getThemeColors(color), ...colors] + }) +} diff --git a/src/logics/theme/updateBackground.ts b/src/logics/theme/updateBackground.ts new file mode 100644 index 00000000..85324980 --- /dev/null +++ b/src/logics/theme/updateBackground.ts @@ -0,0 +1,75 @@ +import { colorIsDark, lighten, darken } from '@/utils/color' +import { useAppStore } from '@/store/modules/app' +import { ThemeEnum } from '@/enums/appEnum' +import { setCssVar } from './util' + +const HEADER_BG_COLOR_VAR = '--header-bg-color' +const HEADER_BG_HOVER_COLOR_VAR = '--header-bg-hover-color' +const HEADER_MENU_ACTIVE_BG_COLOR_VAR = '--header-active-menu-bg-color' + +const SIDER_DARK_BG_COLOR = '--sider-dark-bg-color' +const SIDER_DARK_DARKEN_BG_COLOR = '--sider-dark-darken-bg-color' +const SIDER_LIGHTEN_BG_COLOR = '--sider-dark-lighten-bg-color' + +/** + * Change the background color of the top header + * @param color + */ +export function updateHeaderBgColor(color?: string) { + const appStore = useAppStore() + const darkMode = appStore.getDarkMode === ThemeEnum.DARK + if (!color) { + if (darkMode) { + color = '#151515' + } else { + color = appStore.getHeaderSetting.bgColor + } + } + // bg color + setCssVar(HEADER_BG_COLOR_VAR, color) + + // hover color + const hoverColor = lighten(color!, 6) + setCssVar(HEADER_BG_HOVER_COLOR_VAR, hoverColor) + setCssVar(HEADER_MENU_ACTIVE_BG_COLOR_VAR, hoverColor) + + // Determine the depth of the color value and automatically switch the theme + const isDark = colorIsDark(color!) + + appStore.setProjectConfig({ + headerSetting: { + theme: isDark || darkMode ? ThemeEnum.DARK : ThemeEnum.LIGHT + } + }) +} + +/** + * Change the background color of the left menu + * @param color bg color + */ +export function updateSidebarBgColor(color?: string) { + const appStore = useAppStore() + + // if (!isHexColor(color)) return; + const darkMode = appStore.getDarkMode === ThemeEnum.DARK + if (!color) { + if (darkMode) { + color = '#212121' + } else { + color = appStore.getMenuSetting.bgColor + } + } + setCssVar(SIDER_DARK_BG_COLOR, color) + setCssVar(SIDER_DARK_DARKEN_BG_COLOR, darken(color!, 6)) + setCssVar(SIDER_LIGHTEN_BG_COLOR, lighten(color!, 5)) + + // only #ffffff is light + // Only when the background color is #fff, the theme of the menu will be changed to light + const isLight = ['#fff', '#ffffff'].includes(color!.toLowerCase()) + + appStore.setProjectConfig({ + menuSetting: { + theme: isLight && !darkMode ? ThemeEnum.LIGHT : ThemeEnum.DARK + } + }) +} diff --git a/src/logics/theme/updateColorWeak.ts b/src/logics/theme/updateColorWeak.ts new file mode 100644 index 00000000..031c9e36 --- /dev/null +++ b/src/logics/theme/updateColorWeak.ts @@ -0,0 +1,9 @@ +import { toggleClass } from './util' + +/** + * Change the status of the project's color weakness mode + * @param colorWeak + */ +export function updateColorWeak(colorWeak: boolean) { + toggleClass(colorWeak, 'color-weak', document.documentElement) +} diff --git a/src/logics/theme/updateGrayMode.ts b/src/logics/theme/updateGrayMode.ts new file mode 100644 index 00000000..c548958e --- /dev/null +++ b/src/logics/theme/updateGrayMode.ts @@ -0,0 +1,9 @@ +import { toggleClass } from './util' + +/** + * Change project gray mode status + * @param gray + */ +export function updateGrayMode(gray: boolean) { + toggleClass(gray, 'gray-mode', document.documentElement) +} diff --git a/src/logics/theme/util.ts b/src/logics/theme/util.ts new file mode 100644 index 00000000..43e30563 --- /dev/null +++ b/src/logics/theme/util.ts @@ -0,0 +1,11 @@ +const docEle = document.documentElement +export function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) { + const targetEl = target || document.body + let { className } = targetEl + className = className.replace(clsName, '') + targetEl.className = flag ? `${className} ${clsName} ` : className +} + +export function setCssVar(prop: string, val: any, dom = docEle) { + dom.style.setProperty(prop, val) +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 00000000..6d438b4a --- /dev/null +++ b/src/main.ts @@ -0,0 +1,67 @@ +import 'virtual:windi-base.css' +import 'virtual:windi-components.css' +import '@/design/index.less' +import 'virtual:windi-utilities.css' +// Register icon sprite +import 'virtual:svg-icons-register' +import App from './App.vue' +import { createApp } from 'vue' +import { initAppConfigStore } from '@/logics/initAppConfig' +import { setupErrorHandle } from '@/logics/error-handle' +import { router, setupRouter } from '@/router' +import { setupRouterGuard } from '@/router/guard' +import { setupStore } from '@/store' +import { setupGlobDirectives } from '@/directives' +import { setupI18n } from '@/locales/setupI18n' +import { registerGlobComp } from '@/components/registerGlobComp' + +import { isDevMode } from './utils/env' + +if (isDevMode()) { + import('ant-design-vue/dist/antd.css') +} + +async function bootstrap() { + const app = createApp(App) + + // Configure store + // 配置 store + setupStore(app) + + // Initialize internal system configuration + // 初始化内部系统配置 + initAppConfigStore() + + // Register global components + // 注册全局组件 + registerGlobComp(app) + + // Multilingual configuration + // 多语言配置 + // Asynchronous case: language files may be obtained from the server side + // 异步案例:语言文件可能从服务器端获取 + await setupI18n(app) + + // Configure routing + // 配置路由 + setupRouter(app) + + // router-guard + // 路由守卫 + setupRouterGuard(router) + + // Register global directive + // 注册全局指令 + setupGlobDirectives(app) + + // Configure global error handling + // 配置全局错误处理 + setupErrorHandle(app) + + // https://next.router.vuejs.org/api/#isready + // await router.isReady(); + + app.mount('#app') +} + +bootstrap() diff --git a/src/router/constant.ts b/src/router/constant.ts new file mode 100644 index 00000000..75d1eda5 --- /dev/null +++ b/src/router/constant.ts @@ -0,0 +1,24 @@ +export const REDIRECT_NAME = 'Redirect' + +export const PARENT_LAYOUT_NAME = 'ParentLayout' + +export const PAGE_NOT_FOUND_NAME = 'PageNotFound' + +export const EXCEPTION_COMPONENT = () => import('@/views/sys/exception/Exception.vue') + +/** + * @description: default layout + */ +export const LAYOUT = () => import('@/layouts/default/index.vue') + +/** + * @description: parent-layout + */ +export const getParentLayout = (_name?: string) => { + return () => + new Promise((resolve) => { + resolve({ + name: _name || PARENT_LAYOUT_NAME + }) + }) +} diff --git a/src/router/guard/index.ts b/src/router/guard/index.ts new file mode 100644 index 00000000..f7e659ef --- /dev/null +++ b/src/router/guard/index.ts @@ -0,0 +1,147 @@ +import type { Router, RouteLocationNormalized } from 'vue-router' +import { useAppStoreWithOut } from '@/store/modules/app' +import { useUserStoreWithOut } from '@/store/modules/user' +import { useTransitionSetting } from '@/hooks/setting/useTransitionSetting' +import { AxiosCanceler } from '@/utils/http/axios/axiosCancel' +import { Modal, notification } from 'ant-design-vue' +import { warn } from '@/utils/log' +import { unref } from 'vue' +import { setRouteChange } from '@/logics/mitt/routeChange' +import { createPermissionGuard } from './permissionGuard' +import { createStateGuard } from './stateGuard' +import nProgress from 'nprogress' +import projectSetting from '@/settings/projectSetting' +import { createParamMenuGuard } from './paramMenuGuard' + +// Don't change the order of creation +export function setupRouterGuard(router: Router) { + createPageGuard(router) + createPageLoadingGuard(router) + createHttpGuard(router) + createScrollGuard(router) + createMessageGuard(router) + createProgressGuard(router) + createPermissionGuard(router) + createParamMenuGuard(router) // must after createPermissionGuard (menu has been built.) + createStateGuard(router) +} + +/** + * Hooks for handling page state + */ +function createPageGuard(router: Router) { + const loadedPageMap = new Map() + + router.beforeEach(async (to) => { + // The page has already been loaded, it will be faster to open it again, you don’t need to do loading and other processing + to.meta.loaded = !!loadedPageMap.get(to.path) + // Notify routing changes + setRouteChange(to) + + return true + }) + + router.afterEach((to) => { + loadedPageMap.set(to.path, true) + }) +} + +// Used to handle page loading status +function createPageLoadingGuard(router: Router) { + const userStore = useUserStoreWithOut() + const appStore = useAppStoreWithOut() + const { getOpenPageLoading } = useTransitionSetting() + router.beforeEach(async (to) => { + if (!userStore.getToken) { + return true + } + if (to.meta.loaded) { + return true + } + + if (unref(getOpenPageLoading)) { + appStore.setPageLoadingAction(true) + return true + } + + return true + }) + router.afterEach(async () => { + if (unref(getOpenPageLoading)) { + // TODO Looking for a better way + // The timer simulates the loading time to prevent flashing too fast, + setTimeout(() => { + appStore.setPageLoading(false) + }, 220) + } + return true + }) +} + +/** + * The interface used to close the current page to complete the request when the route is switched + * @param router + */ +function createHttpGuard(router: Router) { + const { removeAllHttpPending } = projectSetting + let axiosCanceler: Nullable + if (removeAllHttpPending) { + axiosCanceler = new AxiosCanceler() + } + router.beforeEach(async () => { + // Switching the route will delete the previous request + axiosCanceler?.removeAllPending() + return true + }) +} + +// Routing switch back to the top +function createScrollGuard(router: Router) { + const isHash = (href: string) => { + return /^#/.test(href) + } + + const body = document.body + + router.afterEach(async (to) => { + // scroll top + isHash((to as RouteLocationNormalized & { href: string })?.href) && body.scrollTo(0, 0) + return true + }) +} + +/** + * Used to close the message instance when the route is switched + * @param router + */ +export function createMessageGuard(router: Router) { + const { closeMessageOnSwitch } = projectSetting + + router.beforeEach(async () => { + try { + if (closeMessageOnSwitch) { + Modal.destroyAll() + notification.destroy() + } + } catch (error) { + warn('message guard error:' + error) + } + return true + }) +} + +export function createProgressGuard(router: Router) { + const { getOpenNProgress } = useTransitionSetting() + router.beforeEach(async (to) => { + if (to.meta.loaded) { + return true + } + unref(getOpenNProgress) && nProgress.start() + return true + }) + + router.afterEach(async () => { + unref(getOpenNProgress) && nProgress.done() + return true + }) +} diff --git a/src/router/guard/paramMenuGuard.ts b/src/router/guard/paramMenuGuard.ts new file mode 100644 index 00000000..4bdfec19 --- /dev/null +++ b/src/router/guard/paramMenuGuard.ts @@ -0,0 +1,47 @@ +import type { Router } from 'vue-router' +import { configureDynamicParamsMenu } from '../helper/menuHelper' +import { Menu } from '../types' +import { PermissionModeEnum } from '@/enums/appEnum' +import { useAppStoreWithOut } from '@/store/modules/app' + +import { usePermissionStoreWithOut } from '@/store/modules/permission' + +export function createParamMenuGuard(router: Router) { + const permissionStore = usePermissionStoreWithOut() + router.beforeEach(async (to, _, next) => { + // filter no name route + if (!to.name) { + next() + return + } + + // menu has been built. + if (!permissionStore.getIsDynamicAddedRoute) { + next() + return + } + + let menus: Menu[] = [] + if (isBackMode()) { + menus = permissionStore.getBackMenuList + } else if (isRouteMappingMode()) { + menus = permissionStore.getFrontMenuList + } + menus.forEach((item) => configureDynamicParamsMenu(item, to.params)) + + next() + }) +} + +const getPermissionMode = () => { + const appStore = useAppStoreWithOut() + return appStore.getProjectConfig.permissionMode +} + +const isBackMode = () => { + return getPermissionMode() === PermissionModeEnum.BACK +} + +const isRouteMappingMode = () => { + return getPermissionMode() === PermissionModeEnum.ROUTE_MAPPING +} diff --git a/src/router/guard/permissionGuard.ts b/src/router/guard/permissionGuard.ts new file mode 100644 index 00000000..cfd121ae --- /dev/null +++ b/src/router/guard/permissionGuard.ts @@ -0,0 +1,117 @@ +import type { Router, RouteRecordRaw } from 'vue-router' + +import { usePermissionStoreWithOut } from '@/store/modules/permission' + +import { PageEnum } from '@/enums/pageEnum' +import { useUserStoreWithOut } from '@/store/modules/user' + +import { PAGE_NOT_FOUND_ROUTE } from '@/router/routes/basic' + +import { RootRoute } from '@/router/routes' + +const LOGIN_PATH = PageEnum.BASE_LOGIN + +const ROOT_PATH = RootRoute.path + +const whitePathList: PageEnum[] = [LOGIN_PATH] + +export function createPermissionGuard(router: Router) { + const userStore = useUserStoreWithOut() + const permissionStore = usePermissionStoreWithOut() + router.beforeEach(async (to, from, next) => { + if ( + from.path === ROOT_PATH && + to.path === PageEnum.BASE_HOME && + userStore.getUserInfo.homePath && + userStore.getUserInfo.homePath !== PageEnum.BASE_HOME + ) { + next(userStore.getUserInfo.homePath) + return + } + + const token = userStore.getToken + + // Whitelist can be directly entered + if (whitePathList.includes(to.path as PageEnum)) { + if (to.path === LOGIN_PATH && token) { + const isSessionTimeout = userStore.getSessionTimeout + try { + await userStore.afterLoginAction() + if (!isSessionTimeout) { + next((to.query?.redirect as string) || '/') + return + } + } catch {} + } + next() + return + } + // token or user does not exist + if (!token) { + // You can access without permission. You need to set the routing meta.ignoreAuth to true + if (to.meta.ignoreAuth) { + next() + return + } + + // redirect login page + const redirectData: { path: string; replace: boolean; query?: Recordable } = { + path: LOGIN_PATH, + replace: true + } + if (to.path) { + redirectData.query = { + ...redirectData.query, + redirect: to.path + } + } + next(redirectData) + return + } + + // Jump to the 404 page after processing the login + if ( + from.path === LOGIN_PATH && + to.name === PAGE_NOT_FOUND_ROUTE.name && + to.fullPath !== (userStore.getUserInfo.homePath || PageEnum.BASE_HOME) + ) { + next(userStore.getUserInfo.homePath || PageEnum.BASE_HOME) + return + } + + // get userinfo while last fetch time is empty + if (userStore.getLastUpdateTime === 0) { + try { + await userStore.getUserInfoAction() + } catch (err) { + next() + return + } + } + + if (permissionStore.getIsDynamicAddedRoute) { + next() + return + } + + const routes = await permissionStore.buildRoutesAction() + + routes.forEach((route) => { + router.addRoute(route as unknown as RouteRecordRaw) + }) + + router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw) + + permissionStore.setDynamicAddedRoute(true) + + if (to.name === PAGE_NOT_FOUND_ROUTE.name) { + // 动态添加路由后,此处应当重定向到fullPath,否则会加载404页面内容 + next({ path: to.fullPath, replace: true, query: to.query }) + } else { + const redirectPath = (from.query.redirect || to.path) as string + const redirect = decodeURIComponent(redirectPath) + const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect } + next(nextData) + } + }) +} diff --git a/src/router/guard/stateGuard.ts b/src/router/guard/stateGuard.ts new file mode 100644 index 00000000..01c30c2a --- /dev/null +++ b/src/router/guard/stateGuard.ts @@ -0,0 +1,24 @@ +import type { Router } from 'vue-router' +import { useAppStore } from '@/store/modules/app' +import { useMultipleTabStore } from '@/store/modules/multipleTab' +import { useUserStore } from '@/store/modules/user' +import { usePermissionStore } from '@/store/modules/permission' +import { PageEnum } from '@/enums/pageEnum' +import { removeTabChangeListener } from '@/logics/mitt/routeChange' + +export function createStateGuard(router: Router) { + router.afterEach((to) => { + // Just enter the login page and clear the authentication information + if (to.path === PageEnum.BASE_LOGIN) { + const tabStore = useMultipleTabStore() + const userStore = useUserStore() + const appStore = useAppStore() + const permissionStore = usePermissionStore() + appStore.resetAllState() + permissionStore.resetState() + tabStore.resetState() + userStore.resetState() + removeTabChangeListener() + } + }) +} diff --git a/src/router/helper/menuHelper.ts b/src/router/helper/menuHelper.ts new file mode 100644 index 00000000..0ab6ab45 --- /dev/null +++ b/src/router/helper/menuHelper.ts @@ -0,0 +1,106 @@ +import { AppRouteModule } from '@/router/types' +import type { MenuModule, Menu, AppRouteRecordRaw } from '@/router/types' +import { findPath, treeMap } from '@/utils/helper/treeHelper' +import { cloneDeep } from 'lodash-es' +import { isUrl } from '@/utils/is' +import { RouteParams } from 'vue-router' +import { toRaw } from 'vue' + +export function getAllParentPath(treeData: T[], path: string) { + const menuList = findPath(treeData, (n) => n.path === path) as Menu[] + return (menuList || []).map((item) => item.path) +} + +// 路径处理 +function joinParentPath(menus: Menu[], parentPath = '') { + for (let index = 0; index < menus.length; index++) { + const menu = menus[index] + // https://next.router.vuejs.org/guide/essentials/nested-routes.html + // Note that nested paths that start with / will be treated as a root path. + // 请注意,以 / 开头的嵌套路径将被视为根路径。 + // This allows you to leverage the component nesting without having to use a nested URL. + // 这允许你利用组件嵌套,而无需使用嵌套 URL。 + if (!(menu.path.startsWith('/') || isUrl(menu.path))) { + // path doesn't start with /, nor is it a url, join parent path + // 路径不以 / 开头,也不是 url,加入父路径 + menu.path = `${parentPath}/${menu.path}` + } + if (menu?.children?.length) { + joinParentPath(menu.children, menu.meta?.hidePathForChildren ? parentPath : menu.path) + } + } +} + +// Parsing the menu module +export function transformMenuModule(menuModule: MenuModule): Menu { + const { menu } = menuModule + + const menuList = [menu] + + joinParentPath(menuList) + return menuList[0] +} + +// 将路由转换成菜单 +export function transformRouteToMenu(routeModList: AppRouteModule[], routerMapping = false) { + // 借助 lodash 深拷贝 + const cloneRouteModList = cloneDeep(routeModList) + const routeList: AppRouteRecordRaw[] = [] + + // 对路由项进行修改 + cloneRouteModList.forEach((item) => { + if (routerMapping && item.meta.hideChildrenInMenu && typeof item.redirect === 'string') { + item.path = item.redirect + } + + if (item.meta?.single) { + const realItem = item?.children?.[0] + realItem && routeList.push(realItem) + } else { + routeList.push(item) + } + }) + // 提取树指定结构 + const list = treeMap(routeList, { + conversion: (node: AppRouteRecordRaw) => { + const { meta: { title, hideMenu = false } = {} } = node + + return { + ...(node.meta || {}), + meta: node.meta, + name: title, + hideMenu, + path: node.path, + ...(node.redirect ? { redirect: node.redirect } : {}) + } + } + }) + // 路径处理 + joinParentPath(list) + return cloneDeep(list) +} + +/** + * config menu with given params + */ +const menuParamRegex = /(?::)([\s\S]+?)((?=\/)|$)/g + +export function configureDynamicParamsMenu(menu: Menu, params: RouteParams) { + const { path, paramPath } = toRaw(menu) + let realPath = paramPath ? paramPath : path + const matchArr = realPath.match(menuParamRegex) + + matchArr?.forEach((it) => { + const realIt = it.substr(1) + if (params[realIt]) { + realPath = realPath.replace(`:${realIt}`, params[realIt] as string) + } + }) + // save original param path. + if (!paramPath && matchArr && matchArr.length > 0) { + menu.paramPath = path + } + menu.path = realPath + // children + menu.children?.forEach((item) => configureDynamicParamsMenu(item, params)) +} diff --git a/src/router/helper/routeHelper.ts b/src/router/helper/routeHelper.ts new file mode 100644 index 00000000..b42b75a0 --- /dev/null +++ b/src/router/helper/routeHelper.ts @@ -0,0 +1,172 @@ +import type { AppRouteModule, AppRouteRecordRaw } from '@/router/types' +import type { Router, RouteRecordNormalized } from 'vue-router' + +import { getParentLayout, LAYOUT, EXCEPTION_COMPONENT } from '@/router/constant' +import { cloneDeep, omit } from 'lodash-es' +import { warn } from '@/utils/log' +import { createRouter, createWebHashHistory } from 'vue-router' + +export type LayoutMapKey = 'LAYOUT' +const IFRAME = () => import('@/views/sys/iframe/FrameBlank.vue') + +const LayoutMap = new Map Promise>() + +LayoutMap.set('LAYOUT', LAYOUT) +LayoutMap.set('IFRAME', IFRAME) + +let dynamicViewsModules: Record Promise> + +// Dynamic introduction +function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) { + dynamicViewsModules = dynamicViewsModules || import.meta.glob('../../views/**/*.{vue,tsx}') + if (!routes) return + routes.forEach((item) => { + if (!item.component && item.meta?.frameSrc) { + item.component = 'IFRAME' + } + const { component, name } = item + const { children } = item + if (component) { + const layoutFound = LayoutMap.get(component.toUpperCase()) + if (layoutFound) { + item.component = layoutFound + } else { + item.component = dynamicImport(dynamicViewsModules, component as string) + } + } else if (name) { + item.component = getParentLayout() + } + children && asyncImportRoute(children) + }) +} + +function dynamicImport(dynamicViewsModules: Record Promise>, component: string) { + const keys = Object.keys(dynamicViewsModules) + const matchKeys = keys.filter((key) => { + const k = key.replace('../../views', '') + const startFlag = component.startsWith('/') + const endFlag = component.endsWith('.vue') || component.endsWith('.tsx') + const startIndex = startFlag ? 0 : 1 + const lastIndex = endFlag ? k.length : k.lastIndexOf('.') + return k.substring(startIndex, lastIndex) === component + }) + if (matchKeys?.length === 1) { + const matchKey = matchKeys[0] + return dynamicViewsModules[matchKey] + } else if (matchKeys?.length > 1) { + warn( + // eslint-disable-next-line max-len + 'Please do not create `.vue` and `.TSX` files with the same file name in the same hierarchical directory under the views folder. This will cause dynamic introduction failure' + ) + return + } else { + warn('在src/views/下找不到`' + component + '.vue` 或 `' + component + '.tsx`, 请自行创建!') + return EXCEPTION_COMPONENT + } +} + +// Turn background objects into routing objects +// 将背景对象变成路由对象 +export function transformObjToRoute(routeList: AppRouteModule[]): T[] { + routeList.forEach((route) => { + const component = route.component as string + if (component) { + if (component.toUpperCase() === 'LAYOUT') { + route.component = LayoutMap.get(component.toUpperCase()) + } else { + route.children = [cloneDeep(route)] + route.component = LAYOUT + route.name = `${route.name}Parent` + route.path = '' + const meta = route.meta || {} + meta.single = true + meta.affix = false + route.meta = meta + } + } else { + warn('请正确配置路由:' + route?.name + '的component属性') + } + route.children && asyncImportRoute(route.children) + }) + return routeList as unknown as T[] +} + +/** + * Convert multi-level routing to level 2 routing + * 将多级路由转换为 2 级路由 + */ +export function flatMultiLevelRoutes(routeModules: AppRouteModule[]) { + const modules: AppRouteModule[] = cloneDeep(routeModules) + + for (let index = 0; index < modules.length; index++) { + const routeModule = modules[index] + // 判断级别是否 多级 路由 + if (!isMultipleRoute(routeModule)) { + // 声明终止当前循环, 即跳过此次循环,进行下一轮 + continue + } + // 路由等级提升 + promoteRouteLevel(routeModule) + } + return modules +} + +// Routing level upgrade +// 路由等级提升 +function promoteRouteLevel(routeModule: AppRouteModule) { + // Use vue-router to splice menus + // 使用vue-router拼接菜单 + // createRouter 创建一个可以被 Vue 应用程序使用的路由实例 + let router: Router | null = createRouter({ + routes: [routeModule as unknown as RouteRecordNormalized], + history: createWebHashHistory() + }) + // getRoutes: 获取所有 路由记录的完整列表。 + const routes = router.getRoutes() + // 将所有子路由添加到二级路由 + addToChildren(routes, routeModule.children || [], routeModule) + router = null + + // omit lodash的函数 对传入的item对象的children进行删除 + routeModule.children = routeModule.children?.map((item) => omit(item, 'children')) +} + +// Add all sub-routes to the secondary route +// 将所有子路由添加到二级路由 +function addToChildren(routes: RouteRecordNormalized[], children: AppRouteRecordRaw[], routeModule: AppRouteModule) { + for (let index = 0; index < children.length; index++) { + const child = children[index] + const route = routes.find((item) => item.name === child.name) + if (!route) { + continue + } + routeModule.children = routeModule.children || [] + if (!routeModule.children.find((item) => item.name === route.name)) { + routeModule.children?.push(route as unknown as AppRouteModule) + } + if (child.children?.length) { + addToChildren(routes, child.children, routeModule) + } + } +} + +// Determine whether the level exceeds 2 levels +// 判断级别是否超过2级 +function isMultipleRoute(routeModule: AppRouteModule) { + // Reflect.has 与 in 操作符 相同, 用于检查一个对象(包括它原型链上)是否拥有某个属性 + if (!routeModule || !Reflect.has(routeModule, 'children') || !routeModule.children?.length) { + return false + } + + const children = routeModule.children + + let flag = false + for (let index = 0; index < children.length; index++) { + const child = children[index] + if (child.children?.length) { + flag = true + break + } + } + return flag +} diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 00000000..ab4214e8 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,42 @@ +import type { RouteRecordRaw } from 'vue-router' +import type { App } from 'vue' + +import { createRouter, createWebHashHistory } from 'vue-router' +import { basicRoutes } from './routes' + +// 白名单应该包含基本静态路由 +const WHITE_NAME_LIST: string[] = [] +const getRouteNames = (array: any[]) => + array.forEach((item) => { + WHITE_NAME_LIST.push(item.name) + getRouteNames(item.children || []) + }) +getRouteNames(basicRoutes) + +// app router +// 创建一个可以被 Vue 应用程序使用的路由实例 +export const router = createRouter({ + // 创建一个 hash 历史记录。 + history: createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH), + // 应该添加到路由的初始路由列表。 + routes: basicRoutes as unknown as RouteRecordRaw[], + // 是否应该禁止尾部斜杠。默认为假 + strict: true, + scrollBehavior: () => ({ left: 0, top: 0 }) +}) + +// reset router +export function resetRouter() { + router.getRoutes().forEach((route) => { + const { name } = route + if (name && !WHITE_NAME_LIST.includes(name as string)) { + router.hasRoute(name) && router.removeRoute(name) + } + }) +} + +// config router +// 配置路由器 +export function setupRouter(app: App) { + app.use(router) +} diff --git a/src/router/menus/index.ts b/src/router/menus/index.ts new file mode 100644 index 00000000..11e73787 --- /dev/null +++ b/src/router/menus/index.ts @@ -0,0 +1,136 @@ +import type { Menu, MenuModule } from '@/router/types' +import type { RouteRecordNormalized } from 'vue-router' + +import { useAppStoreWithOut } from '@/store/modules/app' +import { usePermissionStore } from '@/store/modules/permission' +import { transformMenuModule, getAllParentPath } from '@/router/helper/menuHelper' +import { filter } from '@/utils/helper/treeHelper' +import { isUrl } from '@/utils/is' +import { router } from '@/router' +import { PermissionModeEnum } from '@/enums/appEnum' +import { pathToRegexp } from 'path-to-regexp' + +const modules = import.meta.globEager('./modules/**/*.ts') + +const menuModules: MenuModule[] = [] + +Object.keys(modules).forEach((key) => { + const mod = (modules as Recordable)[key].default || {} + const modList = Array.isArray(mod) ? [...mod] : [mod] + menuModules.push(...modList) +}) + +// =========================== +// ==========Helper=========== +// =========================== + +const getPermissionMode = () => { + const appStore = useAppStoreWithOut() + return appStore.getProjectConfig.permissionMode +} +const isBackMode = () => { + return getPermissionMode() === PermissionModeEnum.BACK +} + +const isRouteMappingMode = () => { + return getPermissionMode() === PermissionModeEnum.ROUTE_MAPPING +} + +const isRoleMode = () => { + return getPermissionMode() === PermissionModeEnum.ROLE +} + +const staticMenus: Menu[] = [] +;(() => { + menuModules.sort((a, b) => { + return (a.orderNo || 0) - (b.orderNo || 0) + }) + + for (const menu of menuModules) { + staticMenus.push(transformMenuModule(menu)) + } +})() + +async function getAsyncMenus() { + const permissionStore = usePermissionStore() + //递归过滤所有隐藏的菜单 + const menuFilter = (items) => { + return items.filter((item) => { + const show = !item.meta?.hideMenu && !item.hideMenu + if (show && item.children) { + item.children = menuFilter(item.children) + } + return show + }) + } + if (isBackMode()) { + return menuFilter(permissionStore.getBackMenuList) + } + if (isRouteMappingMode()) { + return menuFilter(permissionStore.getFrontMenuList) + } + return staticMenus +} + +export const getMenus = async (): Promise => { + const menus = await getAsyncMenus() + if (isRoleMode()) { + const routes = router.getRoutes() + return filter(menus, basicFilter(routes)) + } + return menus +} + +export async function getCurrentParentPath(currentPath: string) { + const menus = await getAsyncMenus() + const allParentPath = await getAllParentPath(menus, currentPath) + return allParentPath?.[0] +} + +// Get the level 1 menu, delete children +export async function getShallowMenus(): Promise { + const menus = await getAsyncMenus() + const shallowMenuList = menus.map((item) => ({ ...item, children: undefined })) + if (isRoleMode()) { + const routes = router.getRoutes() + return shallowMenuList.filter(basicFilter(routes)) + } + return shallowMenuList +} + +// Get the children of the menu +export async function getChildrenMenus(parentPath: string) { + const menus = await getMenus() + const parent = menus.find((item) => item.path === parentPath) + if (!parent || !parent.children || !!parent?.meta?.hideChildrenInMenu) { + return [] as Menu[] + } + if (isRoleMode()) { + const routes = router.getRoutes() + return filter(parent.children, basicFilter(routes)) + } + return parent.children +} + +function basicFilter(routes: RouteRecordNormalized[]) { + return (menu: Menu) => { + const matchRoute = routes.find((route) => { + if (isUrl(menu.path)) return true + + if (route.meta?.carryParam) { + return pathToRegexp(route.path).test(menu.path) + } + const isSame = route.path === menu.path + if (!isSame) return false + + if (route.meta?.ignoreAuth) return true + + return isSame || pathToRegexp(route.path).test(menu.path) + }) + + if (!matchRoute) return false + menu.icon = (menu.icon || matchRoute.meta.icon) as string + menu.meta = matchRoute.meta + return true + } +} diff --git a/src/router/routes/basic.ts b/src/router/routes/basic.ts new file mode 100644 index 00000000..9c351586 --- /dev/null +++ b/src/router/routes/basic.ts @@ -0,0 +1,73 @@ +import type { AppRouteRecordRaw } from '@/router/types' +import { t } from '@/hooks/web/useI18n' +import { REDIRECT_NAME, LAYOUT, EXCEPTION_COMPONENT, PAGE_NOT_FOUND_NAME } from '@/router/constant' + +// 404 on a page +export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = { + path: '/:path(.*)*', + name: PAGE_NOT_FOUND_NAME, + component: LAYOUT, + meta: { + title: 'ErrorPage', + hideBreadcrumb: true, + hideMenu: true + }, + children: [ + { + path: '/:path(.*)*', + name: PAGE_NOT_FOUND_NAME, + component: EXCEPTION_COMPONENT, + meta: { + title: 'ErrorPage', + hideBreadcrumb: true, + hideMenu: true + } + } + ] +} + +export const REDIRECT_ROUTE: AppRouteRecordRaw = { + path: '/redirect', + component: LAYOUT, + name: 'RedirectTo', + meta: { + title: REDIRECT_NAME, + hideBreadcrumb: true, + hideMenu: true + }, + children: [ + { + path: '/redirect/:path(.*)', + name: REDIRECT_NAME, + component: () => import('@/views/sys/redirect/index.vue'), + meta: { + title: REDIRECT_NAME, + hideBreadcrumb: true + } + } + ] +} + +export const ERROR_LOG_ROUTE: AppRouteRecordRaw = { + path: '/error-log', + name: 'ErrorLog', + component: LAYOUT, + redirect: '/error-log/list', + meta: { + title: 'ErrorLog', + hideBreadcrumb: true, + hideChildrenInMenu: true + }, + children: [ + { + path: 'list', + name: 'ErrorLogList', + component: () => import('@/views/sys/error-log/index.vue'), + meta: { + title: t('routes.basic.errorLogList'), + hideBreadcrumb: true, + currentActiveMenu: '/error-log' + } + } + ] +} diff --git a/src/router/routes/index.ts b/src/router/routes/index.ts new file mode 100644 index 00000000..894cbbed --- /dev/null +++ b/src/router/routes/index.ts @@ -0,0 +1,43 @@ +import type { AppRouteRecordRaw, AppRouteModule } from '@/router/types' + +import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '@/router/routes/basic' + +import { mainOutRoutes } from './mainOut' +import { PageEnum } from '@/enums/pageEnum' +import { t } from '@/hooks/web/useI18n' + +// import.meta.globEager() 直接引入所有的模块 Vite 独有的功能 +const modules = import.meta.globEager('./modules/**/*.ts') +const routeModuleList: AppRouteModule[] = [] + +// 加入到路由集合中 +Object.keys(modules).forEach((key) => { + const mod = (modules as Recordable)[key].default || {} + const modList = Array.isArray(mod) ? [...mod] : [mod] + routeModuleList.push(...modList) +}) + +export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList] + +// 根路由 +export const RootRoute: AppRouteRecordRaw = { + path: '/', + name: 'Root', + redirect: PageEnum.BASE_HOME, + meta: { + title: 'Root' + } +} + +export const LoginRoute: AppRouteRecordRaw = { + path: '/login', + name: 'Login', + component: () => import('@/views/sys/login/Login.vue'), + meta: { + title: t('routes.basic.login') + } +} + +// Basic routing without permission +// 未经许可的基本路由 +export const basicRoutes = [LoginRoute, RootRoute, ...mainOutRoutes, REDIRECT_ROUTE, PAGE_NOT_FOUND_ROUTE] diff --git a/src/router/routes/mainOut.ts b/src/router/routes/mainOut.ts new file mode 100644 index 00000000..4991b1b2 --- /dev/null +++ b/src/router/routes/mainOut.ts @@ -0,0 +1,22 @@ +/** +The routing of this file will not show the layout. +It is an independent new page. +the contents of the file still need to log in to access + */ +import type { AppRouteModule } from '@/router/types' + +// test +// http:ip:port/main-out +export const mainOutRoutes: AppRouteModule[] = [ + { + path: '/main-out', + name: 'MainOut', + component: () => import('@/views/demo/main-out/index.vue'), + meta: { + title: 'MainOut', + ignoreAuth: true + } + } +] + +export const mainOutRouteNames = mainOutRoutes.map((item) => item.name) diff --git a/src/router/routes/modules/about.ts b/src/router/routes/modules/about.ts new file mode 100644 index 00000000..c9a1015a --- /dev/null +++ b/src/router/routes/modules/about.ts @@ -0,0 +1,31 @@ +import type { AppRouteModule } from '@/router/types' + +import { LAYOUT } from '@/router/constant' +import { t } from '@/hooks/web/useI18n' + +const about: AppRouteModule = { + path: '/about', + name: 'About', + component: LAYOUT, + redirect: '/about/index', + meta: { + hideChildrenInMenu: true, + icon: 'simple-icons:about-dot-me', + title: t('routes.dashboard.about'), + orderNo: 100000 + }, + children: [ + { + path: 'index', + name: 'AboutPage', + component: () => import('@/views/sys/about/index.vue'), + meta: { + title: t('routes.dashboard.about'), + icon: 'simple-icons:about-dot-me', + hideMenu: true + } + } + ] +} + +export default about diff --git a/src/router/routes/modules/dashboard.ts b/src/router/routes/modules/dashboard.ts new file mode 100644 index 00000000..aeaccf9a --- /dev/null +++ b/src/router/routes/modules/dashboard.ts @@ -0,0 +1,37 @@ +import type { AppRouteModule } from '@/router/types' + +import { LAYOUT } from '@/router/constant' +import { t } from '@/hooks/web/useI18n' + +const dashboard: AppRouteModule = { + path: '/dashboard', + name: 'Dashboard', + component: LAYOUT, + redirect: '/dashboard/analysis', + meta: { + orderNo: 10, + icon: 'ion:grid-outline', + title: t('routes.dashboard.dashboard') + }, + children: [ + { + path: 'analysis', + name: 'Analysis', + component: () => import('@/views/dashboard/analysis/index.vue'), + meta: { + // affix: true, + title: t('routes.dashboard.analysis') + } + }, + { + path: 'workbench', + name: 'Workbench', + component: () => import('@/views/dashboard/workbench/index.vue'), + meta: { + title: t('routes.dashboard.workbench') + } + } + ] +} + +export default dashboard diff --git a/src/router/routes/modules/demo/charts.ts b/src/router/routes/modules/demo/charts.ts new file mode 100644 index 00000000..576571e0 --- /dev/null +++ b/src/router/routes/modules/demo/charts.ts @@ -0,0 +1,80 @@ +import type { AppRouteModule } from '@/router/types' + +import { getParentLayout, LAYOUT } from '@/router/constant' +import { t } from '@/hooks/web/useI18n' + +const charts: AppRouteModule = { + path: '/charts', + name: 'Charts', + component: LAYOUT, + redirect: '/charts/echarts/map', + meta: { + orderNo: 500, + icon: 'ion:bar-chart-outline', + title: t('routes.demo.charts.charts') + }, + children: [ + { + path: 'baiduMap', + name: 'BaiduMap', + meta: { + title: t('routes.demo.charts.baiduMap') + }, + component: () => import('@/views/demo/charts/map/Baidu.vue') + }, + { + path: 'aMap', + name: 'AMap', + meta: { + title: t('routes.demo.charts.aMap') + }, + component: () => import('@/views/demo/charts/map/Gaode.vue') + }, + { + path: 'googleMap', + name: 'GoogleMap', + meta: { + title: t('routes.demo.charts.googleMap') + }, + component: () => import('@/views/demo/charts/map/Google.vue') + }, + + { + path: 'echarts', + name: 'Echarts', + component: getParentLayout('Echarts'), + meta: { + title: 'Echarts' + }, + redirect: '/charts/echarts/map', + children: [ + { + path: 'map', + name: 'Map', + component: () => import('@/views/demo/charts/Map.vue'), + meta: { + title: t('routes.demo.charts.map') + } + }, + { + path: 'line', + name: 'Line', + component: () => import('@/views/demo/charts/Line.vue'), + meta: { + title: t('routes.demo.charts.line') + } + }, + { + path: 'pie', + name: 'Pie', + component: () => import('@/views/demo/charts/Pie.vue'), + meta: { + title: t('routes.demo.charts.pie') + } + } + ] + } + ] +} + +export default charts diff --git a/src/router/routes/modules/demo/comp.ts b/src/router/routes/modules/demo/comp.ts new file mode 100644 index 00000000..160478f1 --- /dev/null +++ b/src/router/routes/modules/demo/comp.ts @@ -0,0 +1,555 @@ +import type { AppRouteModule } from '@/router/types' + +import { getParentLayout, LAYOUT } from '@/router/constant' +import { t } from '@/hooks/web/useI18n' + +const comp: AppRouteModule = { + path: '/comp', + name: 'Comp', + component: LAYOUT, + redirect: '/comp/basic', + meta: { + orderNo: 30, + icon: 'ion:layers-outline', + title: t('routes.demo.comp.comp') + }, + + children: [ + { + path: 'basic', + name: 'BasicDemo', + component: () => import('@/views/demo/comp/button/index.vue'), + meta: { + title: t('routes.demo.comp.basic') + } + }, + + { + path: 'form', + name: 'FormDemo', + redirect: '/comp/form/basic', + component: getParentLayout('FormDemo'), + meta: { + // icon: 'mdi:form-select', + title: t('routes.demo.form.form') + }, + children: [ + { + path: 'basic', + name: 'FormBasicDemo', + component: () => import('@/views/demo/form/index.vue'), + meta: { + title: t('routes.demo.form.basic') + } + }, + { + path: 'useForm', + name: 'UseFormDemo', + component: () => import('@/views/demo/form/UseForm.vue'), + meta: { + title: t('routes.demo.form.useForm') + } + }, + { + path: 'refForm', + name: 'RefFormDemo', + component: () => import('@/views/demo/form/RefForm.vue'), + meta: { + title: t('routes.demo.form.refForm') + } + }, + { + path: 'advancedForm', + name: 'AdvancedFormDemo', + component: () => import('@/views/demo/form/AdvancedForm.vue'), + meta: { + title: t('routes.demo.form.advancedForm') + } + }, + { + path: 'ruleForm', + name: 'RuleFormDemo', + component: () => import('@/views/demo/form/RuleForm.vue'), + meta: { + title: t('routes.demo.form.ruleForm') + } + }, + { + path: 'dynamicForm', + name: 'DynamicFormDemo', + component: () => import('@/views/demo/form/DynamicForm.vue'), + meta: { + title: t('routes.demo.form.dynamicForm') + } + }, + { + path: 'customerForm', + name: 'CustomerFormDemo', + component: () => import('@/views/demo/form/CustomerForm.vue'), + meta: { + title: t('routes.demo.form.customerForm') + } + }, + { + path: 'appendForm', + name: 'appendFormDemo', + component: () => import('@/views/demo/form/AppendForm.vue'), + meta: { + title: t('routes.demo.form.appendForm') + } + }, + { + path: 'tabsForm', + name: 'tabsFormDemo', + component: () => import('@/views/demo/form/TabsForm.vue'), + meta: { + title: t('routes.demo.form.tabsForm') + } + } + ] + }, + { + path: 'table', + name: 'TableDemo', + redirect: '/comp/table/basic', + component: getParentLayout('TableDemo'), + meta: { + // icon: 'carbon:table-split', + title: t('routes.demo.table.table') + }, + + children: [ + { + path: 'basic', + name: 'TableBasicDemo', + component: () => import('@/views/demo/table/Basic.vue'), + meta: { + title: t('routes.demo.table.basic') + } + }, + { + path: 'treeTable', + name: 'TreeTableDemo', + component: () => import('@/views/demo/table/TreeTable.vue'), + meta: { + title: t('routes.demo.table.treeTable') + } + }, + { + path: 'fetchTable', + name: 'FetchTableDemo', + component: () => import('@/views/demo/table/FetchTable.vue'), + meta: { + title: t('routes.demo.table.fetchTable') + } + }, + { + path: 'fixedColumn', + name: 'FixedColumnDemo', + component: () => import('@/views/demo/table/FixedColumn.vue'), + meta: { + title: t('routes.demo.table.fixedColumn') + } + }, + { + path: 'customerCell', + name: 'CustomerCellDemo', + component: () => import('@/views/demo/table/CustomerCell.vue'), + meta: { + title: t('routes.demo.table.customerCell') + } + }, + { + path: 'formTable', + name: 'FormTableDemo', + component: () => import('@/views/demo/table/FormTable.vue'), + meta: { + title: t('routes.demo.table.formTable') + } + }, + { + path: 'useTable', + name: 'UseTableDemo', + component: () => import('@/views/demo/table/UseTable.vue'), + meta: { + title: t('routes.demo.table.useTable') + } + }, + { + path: 'refTable', + name: 'RefTableDemo', + component: () => import('@/views/demo/table/RefTable.vue'), + meta: { + title: t('routes.demo.table.refTable') + } + }, + { + path: 'multipleHeader', + name: 'MultipleHeaderDemo', + component: () => import('@/views/demo/table/MultipleHeader.vue'), + meta: { + title: t('routes.demo.table.multipleHeader') + } + }, + { + path: 'mergeHeader', + name: 'MergeHeaderDemo', + component: () => import('@/views/demo/table/MergeHeader.vue'), + meta: { + title: t('routes.demo.table.mergeHeader') + } + }, + { + path: 'expandTable', + name: 'ExpandTableDemo', + component: () => import('@/views/demo/table/ExpandTable.vue'), + meta: { + title: t('routes.demo.table.expandTable') + } + }, + { + path: 'fixedHeight', + name: 'FixedHeightDemo', + component: () => import('@/views/demo/table/FixedHeight.vue'), + meta: { + title: t('routes.demo.table.fixedHeight') + } + }, + { + path: 'footerTable', + name: 'FooterTableDemo', + component: () => import('@/views/demo/table/FooterTable.vue'), + meta: { + title: t('routes.demo.table.footerTable') + } + }, + { + path: 'editCellTable', + name: 'EditCellTableDemo', + component: () => import('@/views/demo/table/EditCellTable.vue'), + meta: { + title: t('routes.demo.table.editCellTable') + } + }, + { + path: 'editRowTable', + name: 'EditRowTableDemo', + component: () => import('@/views/demo/table/EditRowTable.vue'), + meta: { + title: t('routes.demo.table.editRowTable') + } + }, + { + path: 'authColumn', + name: 'AuthColumnDemo', + component: () => import('@/views/demo/table/AuthColumn.vue'), + meta: { + title: t('routes.demo.table.authColumn') + } + }, + { + path: 'resizeParentHeightTable', + name: 'ResizeParentHeightTable', + component: () => import('@/views/demo/table/ResizeParentHeightTable.vue'), + meta: { + title: t('routes.demo.table.resizeParentHeightTable') + } + } + ] + }, + { + path: 'transition', + name: 'transitionDemo', + component: () => import('@/views/demo/comp/transition/index.vue'), + meta: { + title: t('routes.demo.comp.transition') + } + }, + { + path: 'cropper', + name: 'CropperDemo', + component: () => import('@/views/demo/comp/cropper/index.vue'), + meta: { + title: t('routes.demo.comp.cropperImage') + } + }, + { + path: 'countTo', + name: 'CountTo', + component: () => import('@/views/demo/comp/count-to/index.vue'), + meta: { + title: t('routes.demo.comp.countTo') + } + }, + { + path: 'tree', + name: 'TreeDemo', + redirect: '/comp/tree/basic', + component: getParentLayout('TreeDemo'), + meta: { + // icon: 'clarity:tree-view-line', + title: t('routes.demo.comp.tree') + }, + children: [ + { + path: 'basic', + name: 'BasicTreeDemo', + component: () => import('@/views/demo/tree/index.vue'), + meta: { + title: t('routes.demo.comp.treeBasic') + } + }, + { + path: 'editTree', + name: 'EditTreeDemo', + component: () => import('@/views/demo/tree/EditTree.vue'), + meta: { + title: t('routes.demo.comp.editTree') + } + }, + { + path: 'actionTree', + name: 'ActionTreeDemo', + component: () => import('@/views/demo/tree/ActionTree.vue'), + meta: { + title: t('routes.demo.comp.actionTree') + } + } + ] + }, + { + path: 'editor', + name: 'EditorDemo', + redirect: '/comp/editor/markdown', + component: getParentLayout('EditorDemo'), + meta: { + // icon: 'carbon:table-split', + title: t('routes.demo.editor.editor') + }, + children: [ + { + path: 'json', + component: () => import('@/views/demo/editor/json/index.vue'), + name: 'JsonEditorDemo', + meta: { + title: t('routes.demo.editor.jsonEditor') + } + }, + { + path: 'markdown', + component: getParentLayout('MarkdownDemo'), + name: 'MarkdownDemo', + meta: { + title: t('routes.demo.editor.markdown') + }, + redirect: '/comp/editor/markdown/index', + children: [ + { + path: 'index', + name: 'MarkDownBasicDemo', + component: () => import('@/views/demo/editor/markdown/index.vue'), + meta: { + title: t('routes.demo.editor.tinymceBasic') + } + }, + { + path: 'editor', + name: 'MarkDownFormDemo', + component: () => import('@/views/demo/editor/markdown/Editor.vue'), + meta: { + title: t('routes.demo.editor.tinymceForm') + } + } + ] + }, + + { + path: 'tinymce', + component: getParentLayout('TinymceDemo'), + name: 'TinymceDemo', + meta: { + title: t('routes.demo.editor.tinymce') + }, + redirect: '/comp/editor/tinymce/index', + children: [ + { + path: 'index', + name: 'TinymceBasicDemo', + component: () => import('@/views/demo/editor/tinymce/index.vue'), + meta: { + title: t('routes.demo.editor.tinymceBasic') + } + }, + { + path: 'editor', + name: 'TinymceFormDemo', + component: () => import('@/views/demo/editor/tinymce/Editor.vue'), + meta: { + title: t('routes.demo.editor.tinymceForm') + } + } + ] + } + ] + }, + { + path: 'scroll', + name: 'ScrollDemo', + redirect: '/comp/scroll/basic', + component: getParentLayout('ScrollDemo'), + meta: { + title: t('routes.demo.comp.scroll') + }, + children: [ + { + path: 'basic', + name: 'BasicScrollDemo', + component: () => import('@/views/demo/comp/scroll/index.vue'), + meta: { + title: t('routes.demo.comp.scrollBasic') + } + }, + { + path: 'action', + name: 'ActionScrollDemo', + component: () => import('@/views/demo/comp/scroll/Action.vue'), + meta: { + title: t('routes.demo.comp.scrollAction') + } + }, + { + path: 'virtualScroll', + name: 'VirtualScrollDemo', + component: () => import('@/views/demo/comp/scroll/VirtualScroll.vue'), + meta: { + title: t('routes.demo.comp.virtualScroll') + } + } + ] + }, + + { + path: 'modal', + name: 'ModalDemo', + component: () => import('@/views/demo/comp/modal/index.vue'), + meta: { + title: t('routes.demo.comp.modal') + } + }, + { + path: 'drawer', + name: 'DrawerDemo', + component: () => import('@/views/demo/comp/drawer/index.vue'), + meta: { + title: t('routes.demo.comp.drawer') + } + }, + { + path: 'desc', + name: 'DescDemo', + component: () => import('@/views/demo/comp/desc/index.vue'), + meta: { + title: t('routes.demo.comp.desc') + } + }, + + { + path: 'lazy', + name: 'LazyDemo', + component: getParentLayout('LazyDemo'), + redirect: '/comp/lazy/basic', + meta: { + title: t('routes.demo.comp.lazy') + }, + children: [ + { + path: 'basic', + name: 'BasicLazyDemo', + component: () => import('@/views/demo/comp/lazy/index.vue'), + meta: { + title: t('routes.demo.comp.lazyBasic') + } + }, + { + path: 'transition', + name: 'BasicTransitionDemo', + component: () => import('@/views/demo/comp/lazy/Transition.vue'), + meta: { + title: t('routes.demo.comp.lazyTransition') + } + } + ] + }, + { + path: 'verify', + name: 'VerifyDemo', + component: getParentLayout('VerifyDemo'), + redirect: '/comp/verify/drag', + meta: { + title: t('routes.demo.comp.verify') + }, + children: [ + { + path: 'drag', + name: 'VerifyDragDemo', + component: () => import('@/views/demo/comp/verify/index.vue'), + meta: { + title: t('routes.demo.comp.verifyDrag') + } + }, + { + path: 'rotate', + name: 'VerifyRotateDemo', + component: () => import('@/views/demo/comp/verify/Rotate.vue'), + meta: { + title: t('routes.demo.comp.verifyRotate') + } + } + ] + }, + // + + { + path: 'qrcode', + name: 'QrCodeDemo', + component: () => import('@/views/demo/comp/qrcode/index.vue'), + meta: { + title: t('routes.demo.comp.qrcode') + } + }, + { + path: 'strength-meter', + name: 'StrengthMeterDemo', + component: () => import('@/views/demo/comp/strength-meter/index.vue'), + meta: { + title: t('routes.demo.comp.strength') + } + }, + { + path: 'upload', + name: 'UploadDemo', + component: () => import('@/views/demo/comp/upload/index.vue'), + meta: { + title: t('routes.demo.comp.upload') + } + }, + { + path: 'loading', + name: 'LoadingDemo', + component: () => import('@/views/demo/comp/loading/index.vue'), + meta: { + title: t('routes.demo.comp.loading') + } + }, + { + path: 'cardList', + name: 'CardListDemo', + component: () => import('@/views/demo/comp/card-list/index.vue'), + meta: { + title: t('routes.demo.comp.cardList') + } + } + ] +} + +export default comp diff --git a/src/router/routes/modules/demo/feat.ts b/src/router/routes/modules/demo/feat.ts new file mode 100644 index 00000000..f6dd8ffd --- /dev/null +++ b/src/router/routes/modules/demo/feat.ts @@ -0,0 +1,315 @@ +import type { AppRouteModule } from '@/router/types' + +import { getParentLayout, LAYOUT } from '@/router/constant' +import { t } from '@/hooks/web/useI18n' + +const feat: AppRouteModule = { + path: '/feat', + name: 'FeatDemo', + component: LAYOUT, + redirect: '/feat/icon', + meta: { + orderNo: 19, + icon: 'ion:git-compare-outline', + title: t('routes.demo.feat.feat') + }, + + children: [ + { + path: 'icon', + name: 'IconDemo', + component: () => import('@/views/demo/feat/icon/index.vue'), + meta: { + title: t('routes.demo.feat.icon') + } + }, + { + path: 'ws', + name: 'WebSocket', + component: () => import('@/views/demo/feat/ws/index.vue'), + meta: { + title: t('routes.demo.feat.ws') + } + }, + { + path: 'request', + name: 'RequestDemo', + component: () => import('@/views/demo/feat/request-demo/index.vue'), + meta: { + title: t('routes.demo.feat.requestDemo') + } + }, + { + path: 'session-timeout', + name: 'SessionTimeout', + component: () => import('@/views/demo/feat/session-timeout/index.vue'), + meta: { + title: t('routes.demo.feat.sessionTimeout') + } + }, + { + path: 'print', + name: 'Print', + component: () => import('@/views/demo/feat/print/index.vue'), + meta: { + title: t('routes.demo.feat.print') + } + }, + { + path: 'tabs', + name: 'TabsDemo', + component: () => import('@/views/demo/feat/tabs/index.vue'), + meta: { + title: t('routes.demo.feat.tabs'), + hideChildrenInMenu: true + }, + children: [ + { + path: 'detail/:id', + name: 'TabDetail', + component: () => import('@/views/demo/feat/tabs/TabDetail.vue'), + meta: { + currentActiveMenu: '/feat/tabs', + title: t('routes.demo.feat.tabDetail'), + hideMenu: true, + dynamicLevel: 3, + realPath: '/feat/tabs/detail' + } + } + ] + }, + { + path: 'breadcrumb', + name: 'BreadcrumbDemo', + redirect: '/feat/breadcrumb/flat', + component: getParentLayout('BreadcrumbDemo'), + meta: { + title: t('routes.demo.feat.breadcrumb') + }, + + children: [ + { + path: 'flat', + name: 'BreadcrumbFlatDemo', + component: () => import('@/views/demo/feat/breadcrumb/FlatList.vue'), + meta: { + title: t('routes.demo.feat.breadcrumbFlat') + } + }, + { + path: 'flatDetail', + name: 'BreadcrumbFlatDetailDemo', + component: () => import('@/views/demo/feat/breadcrumb/FlatListDetail.vue'), + meta: { + title: t('routes.demo.feat.breadcrumbFlatDetail'), + hideMenu: true, + hideTab: true, + currentActiveMenu: '/feat/breadcrumb/flat' + } + }, + { + path: 'children', + name: 'BreadcrumbChildrenDemo', + component: () => import('@/views/demo/feat/breadcrumb/ChildrenList.vue'), + meta: { + title: t('routes.demo.feat.breadcrumbChildren') + }, + children: [ + { + path: 'childrenDetail', + name: 'BreadcrumbChildrenDetailDemo', + component: () => import('@/views/demo/feat/breadcrumb/ChildrenListDetail.vue'), + meta: { + currentActiveMenu: '/feat/breadcrumb/children', + title: t('routes.demo.feat.breadcrumbChildrenDetail') + //hideTab: true, + // hideMenu: true, + } + } + ] + } + ] + }, + + { + path: 'context-menu', + name: 'ContextMenuDemo', + component: () => import('@/views/demo/feat/context-menu/index.vue'), + meta: { + title: t('routes.demo.feat.contextMenu') + } + }, + { + path: 'download', + name: 'DownLoadDemo', + component: () => import('@/views/demo/feat/download/index.vue'), + meta: { + title: t('routes.demo.feat.download') + } + }, + { + path: 'img-preview', + name: 'ImgPreview', + component: () => import('@/views/demo/feat/img-preview/index.vue'), + meta: { + title: t('routes.demo.feat.imgPreview') + } + }, + { + path: 'copy', + name: 'CopyDemo', + component: () => import('@/views/demo/feat/copy/index.vue'), + meta: { + title: t('routes.demo.feat.copy') + } + }, + { + path: 'msg', + name: 'MsgDemo', + component: () => import('@/views/demo/feat/msg/index.vue'), + meta: { + title: t('routes.demo.feat.msg') + } + }, + { + path: 'watermark', + name: 'WatermarkDemo', + component: () => import('@/views/demo/feat/watermark/index.vue'), + meta: { + title: t('routes.demo.feat.watermark') + } + }, + { + path: 'ripple', + name: 'RippleDemo', + component: () => import('@/views/demo/feat/ripple/index.vue'), + meta: { + title: t('routes.demo.feat.ripple') + } + }, + { + path: 'full-screen', + name: 'FullScreenDemo', + component: () => import('@/views/demo/feat/full-screen/index.vue'), + meta: { + title: t('routes.demo.feat.fullScreen') + } + }, + { + path: '/error-log', + name: 'ErrorLog', + component: () => import('@/views/sys/error-log/index.vue'), + meta: { + title: t('routes.demo.feat.errorLog') + } + }, + { + path: 'excel', + name: 'Excel', + redirect: '/feat/excel/customExport', + component: getParentLayout('Excel'), + meta: { + // icon: 'mdi:microsoft-excel', + title: t('routes.demo.excel.excel') + }, + + children: [ + { + path: 'customExport', + name: 'CustomExport', + component: () => import('@/views/demo/excel/CustomExport.vue'), + meta: { + title: t('routes.demo.excel.customExport') + } + }, + { + path: 'jsonExport', + name: 'JsonExport', + component: () => import('@/views/demo/excel/JsonExport.vue'), + meta: { + title: t('routes.demo.excel.jsonExport') + } + }, + { + path: 'arrayExport', + name: 'ArrayExport', + component: () => import('@/views/demo/excel/ArrayExport.vue'), + meta: { + title: t('routes.demo.excel.arrayExport') + } + }, + { + path: 'importExcel', + name: 'ImportExcel', + component: () => import('@/views/demo/excel/ImportExcel.vue'), + meta: { + title: t('routes.demo.excel.importExcel') + } + } + ] + }, + { + path: 'testTab/:id', + name: 'TestTab', + component: () => import('@/views/demo/feat/tab-params/index.vue'), + meta: { + title: t('routes.demo.feat.tab'), + carryParam: true, + hidePathForChildren: true + }, + children: [ + { + path: 'testTab/id1', + name: 'TestTab1', + component: () => import('@/views/demo/feat/tab-params/index.vue'), + meta: { + title: t('routes.demo.feat.tab1'), + carryParam: true, + ignoreRoute: true + } + }, + { + path: 'testTab/id2', + name: 'TestTab2', + component: () => import('@/views/demo/feat/tab-params/index.vue'), + meta: { + title: t('routes.demo.feat.tab2'), + carryParam: true, + ignoreRoute: true + } + } + ] + }, + { + path: 'testParam/:id', + name: 'TestParam', + component: getParentLayout('TestParam'), + meta: { + title: t('routes.demo.feat.menu'), + ignoreKeepAlive: true + }, + children: [ + { + path: 'sub1', + name: 'TestParam_1', + component: () => import('@/views/demo/feat/menu-params/index.vue'), + meta: { + title: t('routes.demo.feat.menu1'), + ignoreKeepAlive: true + } + }, + { + path: 'sub2', + name: 'TestParam_2', + component: () => import('@/views/demo/feat/menu-params/index.vue'), + meta: { + title: t('routes.demo.feat.menu2'), + ignoreKeepAlive: true + } + } + ] + } + ] +} + +export default feat diff --git a/src/router/routes/modules/demo/flow.ts b/src/router/routes/modules/demo/flow.ts new file mode 100644 index 00000000..6b901222 --- /dev/null +++ b/src/router/routes/modules/demo/flow.ts @@ -0,0 +1,28 @@ +import type { AppRouteModule } from '@/router/types' + +import { LAYOUT } from '@/router/constant' +import { t } from '@/hooks/web/useI18n' + +const charts: AppRouteModule = { + path: '/flow', + name: 'FlowDemo', + component: LAYOUT, + redirect: '/flow/flowChart', + meta: { + orderNo: 5000, + icon: 'tabler:chart-dots', + title: t('routes.demo.flow.name') + }, + children: [ + { + path: 'flowChart', + name: 'flowChartDemo', + component: () => import('@/views/demo/comp/flow-chart/index.vue'), + meta: { + title: t('routes.demo.flow.flowChart') + } + } + ] +} + +export default charts diff --git a/src/router/routes/modules/demo/iframe.ts b/src/router/routes/modules/demo/iframe.ts new file mode 100644 index 00000000..9504b28f --- /dev/null +++ b/src/router/routes/modules/demo/iframe.ts @@ -0,0 +1,48 @@ +import type { AppRouteModule } from '@/router/types' + +import { LAYOUT } from '@/router/constant' +const IFrame = () => import('@/views/sys/iframe/FrameBlank.vue') +import { t } from '@/hooks/web/useI18n' + +const iframe: AppRouteModule = { + path: '/frame', + name: 'Frame', + component: LAYOUT, + redirect: '/frame/doc', + meta: { + orderNo: 1000, + icon: 'ion:tv-outline', + title: t('routes.demo.iframe.frame') + }, + + children: [ + { + path: 'doc', + name: 'Doc', + component: IFrame, + meta: { + frameSrc: 'https://doc.vvbin.cn/', + title: t('routes.demo.iframe.doc') + } + }, + { + path: 'antv', + name: 'Antv', + component: IFrame, + meta: { + frameSrc: 'https://www.antdv.com/docs/vue/introduce-cn/', + title: t('routes.demo.iframe.antv') + } + }, + { + path: 'https://doc.vvbin.cn/', + name: 'DocExternal', + component: IFrame, + meta: { + title: t('routes.demo.iframe.docExternal') + } + } + ] +} + +export default iframe diff --git a/src/router/routes/modules/demo/level.ts b/src/router/routes/modules/demo/level.ts new file mode 100644 index 00000000..db0d71f0 --- /dev/null +++ b/src/router/routes/modules/demo/level.ts @@ -0,0 +1,68 @@ +import type { AppRouteModule } from '@/router/types' + +import { getParentLayout, LAYOUT } from '@/router/constant' +import { t } from '@/hooks/web/useI18n' + +const permission: AppRouteModule = { + path: '/level', + name: 'Level', + component: LAYOUT, + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + orderNo: 2000, + icon: 'ion:menu-outline', + title: t('routes.demo.level.level') + }, + + children: [ + { + path: 'menu1', + name: 'Menu1Demo', + component: getParentLayout('Menu1Demo'), + meta: { + title: 'Menu1' + }, + redirect: '/level/menu1/menu1-1/menu1-1-1', + children: [ + { + path: 'menu1-1', + name: 'Menu11Demo', + component: getParentLayout('Menu11Demo'), + meta: { + title: 'Menu1-1' + }, + redirect: '/level/menu1/menu1-1/menu1-1-1', + children: [ + { + path: 'menu1-1-1', + name: 'Menu111Demo', + component: () => import('@/views/demo/level/Menu111.vue'), + meta: { + title: 'Menu111' + } + } + ] + }, + { + path: 'menu1-2', + name: 'Menu12Demo', + component: () => import('@/views/demo/level/Menu12.vue'), + meta: { + title: 'Menu1-2' + } + } + ] + }, + { + path: 'menu2', + name: 'Menu2Demo', + component: () => import('@/views/demo/level/Menu2.vue'), + meta: { + title: 'Menu2' + // ignoreKeepAlive: true, + } + } + ] +} + +export default permission diff --git a/src/router/routes/modules/demo/page.ts b/src/router/routes/modules/demo/page.ts new file mode 100644 index 00000000..fb6063c0 --- /dev/null +++ b/src/router/routes/modules/demo/page.ts @@ -0,0 +1,255 @@ +import type { AppRouteModule } from '@/router/types' + +import { getParentLayout, LAYOUT } from '@/router/constant' +import { ExceptionEnum } from '@/enums/exceptionEnum' +import { t } from '@/hooks/web/useI18n' + +const ExceptionPage = () => import('@/views/sys/exception/Exception.vue') + +const page: AppRouteModule = { + path: '/page-demo', + name: 'PageDemo', + component: LAYOUT, + redirect: '/page-demo/form/basic', + meta: { + orderNo: 20, + icon: 'ion:aperture-outline', + title: t('routes.demo.page.page') + }, + children: [ + // =============================form start============================= + { + path: 'form', + name: 'FormPage', + redirect: '/page-demo/form/basic', + component: getParentLayout('FormPage'), + meta: { + title: t('routes.demo.page.form') + }, + children: [ + { + path: 'basic', + name: 'FormBasicPage', + component: () => import('@/views/demo/page/form/basic/index.vue'), + meta: { + title: t('routes.demo.page.formBasic') + } + }, + { + path: 'step', + name: 'FormStepPage', + component: () => import('@/views/demo/page/form/step/index.vue'), + meta: { + title: t('routes.demo.page.formStep') + } + }, + { + path: 'high', + name: 'FormHightPage', + component: () => import('@/views/demo/page/form/high/index.vue'), + meta: { + title: t('routes.demo.page.formHigh') + } + } + ] + }, + // =============================form end============================= + // =============================desc start============================= + { + path: 'desc', + name: 'DescPage', + component: getParentLayout('DescPage'), + redirect: '/page-demo/desc/basic', + meta: { + title: t('routes.demo.page.desc') + }, + children: [ + { + path: 'basic', + name: 'DescBasicPage', + component: () => import('@/views/demo/page/desc/basic/index.vue'), + meta: { + title: t('routes.demo.page.descBasic') + } + }, + { + path: 'high', + name: 'DescHighPage', + component: () => import('@/views/demo/page/desc/high/index.vue'), + meta: { + title: t('routes.demo.page.descHigh') + } + } + ] + }, + // =============================desc end============================= + + // =============================result start============================= + { + path: 'result', + name: 'ResultPage', + redirect: '/page-demo/result/success', + component: getParentLayout('ResultPage'), + + meta: { + title: t('routes.demo.page.result') + }, + children: [ + { + path: 'success', + name: 'ResultSuccessPage', + component: () => import('@/views/demo/page/result/success/index.vue'), + meta: { + title: t('routes.demo.page.resultSuccess') + } + }, + { + path: 'fail', + name: 'ResultFailPage', + component: () => import('@/views/demo/page/result/fail/index.vue'), + meta: { + title: t('routes.demo.page.resultFail') + } + } + ] + }, + // =============================result end============================= + + // =============================account start============================= + { + path: 'account', + name: 'AccountPage', + component: getParentLayout('AccountPage'), + redirect: '/page-demo/account/setting', + meta: { + title: t('routes.demo.page.account') + }, + children: [ + { + path: 'center', + name: 'AccountCenterPage', + component: () => import('@/views/demo/page/account/center/index.vue'), + meta: { + title: t('routes.demo.page.accountCenter') + } + }, + { + path: 'setting', + name: 'AccountSettingPage', + component: () => import('@/views/demo/page/account/setting/index.vue'), + meta: { + title: t('routes.demo.page.accountSetting') + } + } + ] + }, + // =============================account end============================= + // =============================exception start============================= + { + path: 'exception', + name: 'ExceptionPage', + component: getParentLayout('ExceptionPage'), + redirect: '/page-demo/exception/404', + meta: { + title: t('routes.demo.page.exception') + }, + children: [ + { + path: '403', + name: 'PageNotAccess', + component: ExceptionPage, + props: { + status: ExceptionEnum.PAGE_NOT_ACCESS + }, + meta: { + title: '403' + } + }, + { + path: '404', + name: 'PageNotFound', + component: ExceptionPage, + props: { + status: ExceptionEnum.PAGE_NOT_FOUND + }, + meta: { + title: '404' + } + }, + { + path: '500', + name: 'ServiceError', + component: ExceptionPage, + props: { + status: ExceptionEnum.ERROR + }, + meta: { + title: '500' + } + }, + { + path: 'net-work-error', + name: 'NetWorkError', + component: ExceptionPage, + props: { + status: ExceptionEnum.NET_WORK_ERROR + }, + meta: { + title: t('routes.demo.page.netWorkError') + } + }, + { + path: 'not-data', + name: 'NotData', + component: ExceptionPage, + props: { + status: ExceptionEnum.PAGE_NOT_DATA + }, + meta: { + title: t('routes.demo.page.notData') + } + } + ] + }, + // =============================exception end============================= + // =============================list start============================= + { + path: 'list', + name: 'ListPage', + component: getParentLayout('ListPage'), + redirect: '/page-demo/list/card', + meta: { + title: t('routes.demo.page.list') + }, + children: [ + { + path: 'basic', + name: 'ListBasicPage', + component: () => import('@/views/demo/page/list/basic/index.vue'), + meta: { + title: t('routes.demo.page.listBasic') + } + }, + { + path: 'card', + name: 'ListCardPage', + component: () => import('@/views/demo/page/list/card/index.vue'), + meta: { + title: t('routes.demo.page.listCard') + } + }, + { + path: 'search', + name: 'ListSearchPage', + component: () => import('@/views/demo/page/list/search/index.vue'), + meta: { + title: t('routes.demo.page.listSearch') + } + } + ] + } + // =============================list end============================= + ] +} + +export default page diff --git a/src/router/routes/modules/demo/permission.ts b/src/router/routes/modules/demo/permission.ts new file mode 100644 index 00000000..cb667564 --- /dev/null +++ b/src/router/routes/modules/demo/permission.ts @@ -0,0 +1,92 @@ +import type { AppRouteModule } from '@/router/types' + +import { getParentLayout, LAYOUT } from '@/router/constant' +import { RoleEnum } from '@/enums/roleEnum' +import { t } from '@/hooks/web/useI18n' + +const permission: AppRouteModule = { + path: '/permission', + name: 'Permission', + component: LAYOUT, + redirect: '/permission/front/page', + meta: { + orderNo: 15, + icon: 'ion:key-outline', + title: t('routes.demo.permission.permission') + }, + + children: [ + { + path: 'front', + name: 'PermissionFrontDemo', + component: getParentLayout('PermissionFrontDemo'), + meta: { + title: t('routes.demo.permission.front') + }, + children: [ + { + path: 'page', + name: 'FrontPageAuth', + component: () => import('@/views/demo/permission/front/index.vue'), + meta: { + title: t('routes.demo.permission.frontPage') + } + }, + { + path: 'btn', + name: 'FrontBtnAuth', + component: () => import('@/views/demo/permission/front/Btn.vue'), + meta: { + title: t('routes.demo.permission.frontBtn') + } + }, + { + path: 'auth-pageA', + name: 'FrontAuthPageA', + component: () => import('@/views/demo/permission/front/AuthPageA.vue'), + meta: { + title: t('routes.demo.permission.frontTestA'), + roles: [RoleEnum.SUPER] + } + }, + { + path: 'auth-pageB', + name: 'FrontAuthPageB', + component: () => import('@/views/demo/permission/front/AuthPageB.vue'), + meta: { + title: t('routes.demo.permission.frontTestB'), + roles: [RoleEnum.TEST] + } + } + ] + }, + { + path: 'back', + name: 'PermissionBackDemo', + component: getParentLayout('PermissionBackDemo'), + meta: { + title: t('routes.demo.permission.back') + }, + children: [ + { + path: 'page', + name: 'BackAuthPage', + component: () => import('@/views/demo/permission/back/index.vue'), + meta: { + title: t('routes.demo.permission.backPage') + } + }, + { + path: 'btn', + name: 'BackAuthBtn', + component: () => import('@/views/demo/permission/back/Btn.vue'), + meta: { + title: t('routes.demo.permission.backBtn') + } + } + ] + } + ] +} + +export default permission diff --git a/src/router/routes/modules/demo/setup.ts b/src/router/routes/modules/demo/setup.ts new file mode 100644 index 00000000..480353ba --- /dev/null +++ b/src/router/routes/modules/demo/setup.ts @@ -0,0 +1,31 @@ +import type { AppRouteModule } from '@/router/types' + +import { LAYOUT } from '@/router/constant' +import { t } from '@/hooks/web/useI18n' + +const setup: AppRouteModule = { + path: '/setup', + name: 'SetupDemo', + component: LAYOUT, + redirect: '/setup/index', + meta: { + orderNo: 90000, + hideChildrenInMenu: true, + icon: 'whh:paintroll', + title: t('routes.demo.setup.page') + }, + children: [ + { + path: 'index', + name: 'SetupDemoPage', + component: () => import('@/views/demo/setup/index.vue'), + meta: { + title: t('routes.demo.setup.page'), + icon: 'whh:paintroll', + hideMenu: true + } + } + ] +} + +export default setup diff --git a/src/router/routes/modules/demo/system.ts b/src/router/routes/modules/demo/system.ts new file mode 100644 index 00000000..e06a8a95 --- /dev/null +++ b/src/router/routes/modules/demo/system.ts @@ -0,0 +1,78 @@ +import type { AppRouteModule } from '@/router/types' + +import { LAYOUT } from '@/router/constant' +import { t } from '@/hooks/web/useI18n' + +const system: AppRouteModule = { + path: '/system', + name: 'System', + component: LAYOUT, + redirect: '/system/account', + meta: { + orderNo: 2000, + icon: 'ion:settings-outline', + title: t('routes.demo.system.moduleName') + }, + children: [ + { + path: 'account', + name: 'AccountManagement', + meta: { + title: t('routes.demo.system.account'), + ignoreKeepAlive: false + }, + component: () => import('@/views/demo/system/account/index.vue') + }, + { + path: 'account_detail/:id', + name: 'AccountDetail', + meta: { + hideMenu: true, + title: t('routes.demo.system.account_detail'), + ignoreKeepAlive: true, + showMenu: false, + currentActiveMenu: '/system/account' + }, + component: () => import('@/views/demo/system/account/AccountDetail.vue') + }, + { + path: 'role', + name: 'RoleManagement', + meta: { + title: t('routes.demo.system.role'), + ignoreKeepAlive: true + }, + component: () => import('@/views/demo/system/role/index.vue') + }, + + { + path: 'menu', + name: 'MenuManagement', + meta: { + title: t('routes.demo.system.menu'), + ignoreKeepAlive: true + }, + component: () => import('@/views/demo/system/menu/index.vue') + }, + { + path: 'dept', + name: 'DeptManagement', + meta: { + title: t('routes.demo.system.dept'), + ignoreKeepAlive: true + }, + component: () => import('@/views/demo/system/dept/index.vue') + }, + { + path: 'changePassword', + name: 'ChangePassword', + meta: { + title: t('routes.demo.system.password'), + ignoreKeepAlive: true + }, + component: () => import('@/views/demo/system/password/index.vue') + } + ] +} + +export default system diff --git a/src/router/types.ts b/src/router/types.ts new file mode 100644 index 00000000..76713ad7 --- /dev/null +++ b/src/router/types.ts @@ -0,0 +1,55 @@ +import type { RouteRecordRaw, RouteMeta } from 'vue-router' +import { RoleEnum } from '@/enums/roleEnum' +import { defineComponent } from 'vue' + +export type Component = ReturnType | (() => Promise) | (() => Promise) + +// @ts-ignore +export interface AppRouteRecordRaw extends Omit { + name: string + meta: RouteMeta + component?: Component | string + components?: Component + children?: AppRouteRecordRaw[] + props?: Recordable + fullPath?: string +} + +export interface MenuTag { + type?: 'primary' | 'error' | 'warn' | 'success' + content?: string + dot?: boolean +} + +export interface Menu { + name: string + + icon?: string + + path: string + + // path contains param, auto assignment. + paramPath?: string + + disabled?: boolean + + children?: Menu[] + + orderNo?: number + + roles?: RoleEnum[] + + meta?: Partial + + tag?: MenuTag + + hideMenu?: boolean +} + +export interface MenuModule { + orderNo?: number + menu: Menu +} + +// export type AppRouteModule = RouteModule | AppRouteRecordRaw; +export type AppRouteModule = AppRouteRecordRaw diff --git a/src/settings/componentSetting.ts b/src/settings/componentSetting.ts new file mode 100644 index 00000000..12018330 --- /dev/null +++ b/src/settings/componentSetting.ts @@ -0,0 +1,51 @@ +// Used to configure the general configuration of some components without modifying the components + +import type { SorterResult } from '../components/Table' + +export default { + // basic-table setting + table: { + // Form interface request general configuration + // support xxx.xxx.xxx + fetchSetting: { + // The field name of the current page passed to the background + pageField: 'page', + // The number field name of each page displayed in the background + sizeField: 'pageSize', + // Field name of the form data returned by the interface + listField: 'items', + // Total number of tables returned by the interface field name + totalField: 'total' + }, + // Number of pages that can be selected + pageSizeOptions: ['10', '50', '80', '100'], + // Default display quantity on one page + defaultPageSize: 10, + // Default Size + defaultSize: 'middle', + // Custom general sort function + defaultSortFn: (sortInfo: SorterResult) => { + const { field, order } = sortInfo + if (field && order) { + return { + // The sort field passed to the backend you + field, + // Sorting method passed to the background asc/desc + order + } + } else { + return {} + } + }, + // Custom general filter function + defaultFilterFn: (data: Partial>) => { + return data + } + }, + // scrollbar setting + scrollbar: { + // Whether to use native scroll bar + // After opening, the menu, modal, drawer will change the pop-up scroll bar to native + native: false + } +} diff --git a/src/settings/designSetting.ts b/src/settings/designSetting.ts new file mode 100644 index 00000000..97597432 --- /dev/null +++ b/src/settings/designSetting.ts @@ -0,0 +1,48 @@ +import { ThemeEnum } from '../enums/appEnum' + +export const prefixCls = 'vben' + +export const darkMode = ThemeEnum.LIGHT + +// app theme preset color +export const APP_PRESET_COLOR_LIST: string[] = [ + '#0960bd', + '#0084f4', + '#009688', + '#536dfe', + '#ff5c93', + '#ee4f12', + '#0096c7', + '#9c27b0', + '#ff9800' +] + +// header preset color +export const HEADER_PRESET_BG_COLOR_LIST: string[] = [ + '#ffffff', + '#151515', + '#009688', + '#5172DC', + '#018ffb', + '#409eff', + '#e74c3c', + '#24292e', + '#394664', + '#001529', + '#383f45' +] + +// sider preset color +export const SIDE_BAR_BG_COLOR_LIST: string[] = [ + '#001529', + '#212121', + '#273352', + '#ffffff', + '#191b24', + '#191a23', + '#304156', + '#001628', + '#28333E', + '#344058', + '#383f45' +] diff --git a/src/settings/encryptionSetting.ts b/src/settings/encryptionSetting.ts new file mode 100644 index 00000000..17459dc3 --- /dev/null +++ b/src/settings/encryptionSetting.ts @@ -0,0 +1,13 @@ +import { isDevMode } from '@/utils/env' + +// System default cache time, in seconds +export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7 + +// aes encryption key +export const cacheCipher = { + key: '_11111000001111@', + iv: '@11111000001111_' +} + +// Whether the system cache is encrypted using aes +export const enableStorageEncryption = !isDevMode() diff --git a/src/settings/localeSetting.ts b/src/settings/localeSetting.ts new file mode 100644 index 00000000..78b9d433 --- /dev/null +++ b/src/settings/localeSetting.ts @@ -0,0 +1,29 @@ +import type { DropMenu } from '../components/Dropdown' +import type { LocaleSetting, LocaleType } from '@/types/config' + +export const LOCALE: { [key: string]: LocaleType } = { + ZH_CN: 'zh_CN', + EN_US: 'en' +} + +export const localeSetting: LocaleSetting = { + showPicker: true, + // Locale + locale: LOCALE.ZH_CN, + // Default locale + fallback: LOCALE.ZH_CN, + // available Locales + availableLocales: [LOCALE.ZH_CN, LOCALE.EN_US] +} + +// locale list +export const localeList: DropMenu[] = [ + { + text: '简体中文', + event: LOCALE.ZH_CN + }, + { + text: 'English', + event: LOCALE.EN_US + } +] diff --git a/src/settings/projectSetting.ts b/src/settings/projectSetting.ts new file mode 100644 index 00000000..7fe8d288 --- /dev/null +++ b/src/settings/projectSetting.ts @@ -0,0 +1,182 @@ +import type { ProjectConfig } from '@/types/config' +import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '@/enums/menuEnum' +import { CacheTypeEnum } from '@/enums/cacheEnum' +import { + ContentEnum, + PermissionModeEnum, + ThemeEnum, + RouterTransitionEnum, + SettingButtonPositionEnum, + SessionTimeoutProcessingEnum +} from '@/enums/appEnum' +import { SIDE_BAR_BG_COLOR_LIST, HEADER_PRESET_BG_COLOR_LIST } from './designSetting' +import { primaryColor } from '../../build/config/themeConfig' + +// ! You need to clear the browser cache after the change +const setting: ProjectConfig = { + // Whether to show the configuration button + showSettingButton: true, + + // Whether to show the theme switch button + showDarkModeToggle: true, + + // `Settings` button position + settingButtonPosition: SettingButtonPositionEnum.AUTO, + + // Permission mode + permissionMode: PermissionModeEnum.ROUTE_MAPPING, + + // Permission-related cache is stored in sessionStorage or localStorage + permissionCacheType: CacheTypeEnum.LOCAL, + + // Session timeout processing + sessionTimeoutProcessing: SessionTimeoutProcessingEnum.ROUTE_JUMP, + + // color + themeColor: primaryColor, + + // Website gray mode, open for possible mourning dates + grayMode: false, + + // Color Weakness Mode + colorWeak: false, + + // Whether to cancel the menu, the top, the multi-tab page display, for possible embedded in other systems + fullContent: false, + + // content mode + contentMode: ContentEnum.FULL, + + // Whether to display the logo + showLogo: true, + + // Whether to show footer + showFooter: false, + + // Header configuration + headerSetting: { + // header bg color + bgColor: HEADER_PRESET_BG_COLOR_LIST[0], + // Fixed at the top + fixed: true, + // Whether to show top + show: true, + // theme + theme: ThemeEnum.LIGHT, + // Whether to enable the lock screen function + useLockPage: true, + // Whether to show the full screen button + showFullScreen: true, + // Whether to show the document button + showDoc: true, + // Whether to show the notification button + showNotice: true, + // Whether to display the menu search + showSearch: true + }, + + // Menu configuration + menuSetting: { + // sidebar menu bg color + bgColor: SIDE_BAR_BG_COLOR_LIST[0], + // Whether to fix the left menu + fixed: true, + // Menu collapse + collapsed: false, + // When sider hide because of the responsive layout + siderHidden: false, + // Whether to display the menu name when folding the menu + collapsedShowTitle: false, + // Whether it can be dragged + // Only limited to the opening of the left menu, the mouse has a drag bar on the right side of the menu + canDrag: false, + // Whether to show no dom + show: true, + // Whether to show dom + hidden: false, + // Menu width + menuWidth: 210, + // Menu mode + mode: MenuModeEnum.INLINE, + // Menu type + type: MenuTypeEnum.SIDEBAR, + // Menu theme + theme: ThemeEnum.DARK, + // Split menu + split: false, + // Top menu layout + topMenuAlign: 'center', + // Fold trigger position + trigger: TriggerEnum.HEADER, + // Turn on accordion mode, only show a menu + accordion: true, + // Switch page to close menu + closeMixSidebarOnChange: false, + // Module opening method ‘click’ |'hover' + mixSideTrigger: MixSidebarTriggerEnum.CLICK, + // Fixed expanded menu + mixSideFixed: false + }, + + // Multi-label + multiTabsSetting: { + cache: false, + // Turn on + show: true, + // Is it possible to drag and drop sorting tabs + canDrag: true, + // Turn on quick actions + showQuick: true, + // Whether to show the refresh button + showRedo: true, + // Whether to show the collapse button + showFold: true + }, + + // Transition Setting + transitionSetting: { + // Whether to open the page switching animation + // The disabled state will also disable pageLoading + enable: true, + + // Route basic switching animation + basicTransition: RouterTransitionEnum.FADE_SIDE, + + // Whether to open page switching loading + // Only open when enable=true + openPageLoading: true, + + // Whether to open the top progress bar + openNProgress: false + }, + + // Whether to enable KeepAlive cache is best to close during development, otherwise the cache needs to be cleared every time + openKeepAlive: true, + + // Automatic screen lock time, 0 does not lock the screen. Unit minute default 0 + lockTime: 0, + + // Whether to show breadcrumbs + showBreadCrumb: true, + + // Whether to show the breadcrumb icon + showBreadCrumbIcon: false, + + // Use error-handler-plugin + useErrorHandle: false, + + // Whether to open back to top + useOpenBackTop: true, + + // Is it possible to embed iframe pages + canEmbedIFramePage: true, + + // Whether to delete unclosed messages and notify when switching the interface + closeMessageOnSwitch: true, + + // Whether to cancel the http request that has been sent but not responded when switching the interface. + // If it is enabled, I want to overwrite a single interface. Can be set in a separate interface + removeAllHttpPending: false +} + +export default setting diff --git a/src/settings/siteSetting.ts b/src/settings/siteSetting.ts new file mode 100644 index 00000000..870bf0d4 --- /dev/null +++ b/src/settings/siteSetting.ts @@ -0,0 +1,5 @@ +export const GITHUB_URL = 'https://gitee.com/xingyu4j/vue-vben-admin' + +export const DOC_URL = 'http://vben.x-surge.com/' + +export const SITE_URL = 'http://vben.x-surge.com/' diff --git a/src/settings/sizeSetting.ts b/src/settings/sizeSetting.ts new file mode 100644 index 00000000..584f9699 --- /dev/null +++ b/src/settings/sizeSetting.ts @@ -0,0 +1,35 @@ +import type { DropMenu } from '../components/Dropdown' +import type { SizeSetting, AppSizeType } from '@/types/config' +// 'small' | 'middle' | 'large' +export const APPSIZE: { [key: string]: AppSizeType } = { + SMALL: 'small', + MIDDLE: 'middle', + LARGE: 'large' +} + +export const sizeSetting: SizeSetting = { + // 是否显示尺寸选择器 + showPicker: true, + // 当前尺寸 + size: APPSIZE.MIDDLE, + // 默认尺寸 + fallback: APPSIZE.MIDDLE, + // 允许的尺寸 + availableSizes: [APPSIZE.SMALL, APPSIZE.MIDDLE, APPSIZE.LARGE] +} + +// 尺寸列表 +export const sizeList: DropMenu[] = [ + { + text: '默认', + event: APPSIZE.MIDDLE + }, + { + text: '小型', + event: APPSIZE.SMALL + }, + { + text: '大型', + event: APPSIZE.LARGE + } +] diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 00000000..4d393cb5 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,10 @@ +import type { App } from 'vue' +import { createPinia } from 'pinia' + +const store = createPinia() + +export function setupStore(app: App) { + app.use(store) +} + +export { store } diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts new file mode 100644 index 00000000..2f758648 --- /dev/null +++ b/src/store/modules/app.ts @@ -0,0 +1,110 @@ +import type { ProjectConfig, HeaderSetting, MenuSetting, TransitionSetting, MultiTabsSetting, AppSizeType } from '@/types/config' +import type { BeforeMiniState } from '@/types/store' + +import { defineStore } from 'pinia' +import { store } from '@/store' + +import { ThemeEnum } from '@/enums/appEnum' +import { APP_DARK_MODE_KEY_, PROJ_CFG_KEY } from '@/enums/cacheEnum' +import { Persistent } from '@/utils/cache/persistent' +import { darkMode } from '@/settings/designSetting' +import { resetRouter } from '@/router' +import { deepMerge } from '@/utils' + +interface AppState { + darkMode?: ThemeEnum + // Page loading status + pageLoading: boolean + // project config + projectConfig: ProjectConfig | null + // When the window shrinks, remember some states, and restore these states when the window is restored + beforeMiniInfo: BeforeMiniState + componentSize: 'small' | 'middle' | 'large' | undefined +} +let timeId: TimeoutHandle +export const useAppStore = defineStore('app', { + state: (): AppState => ({ + darkMode: undefined, + pageLoading: false, + projectConfig: Persistent.getLocal(PROJ_CFG_KEY), + beforeMiniInfo: {}, + componentSize: 'middle' + }), + getters: { + getPageLoading(): boolean { + return this.pageLoading + }, + getDarkMode(): 'light' | 'dark' | string { + return this.darkMode || localStorage.getItem(APP_DARK_MODE_KEY_) || darkMode + }, + + getBeforeMiniInfo(): BeforeMiniState { + return this.beforeMiniInfo + }, + + getProjectConfig(): ProjectConfig { + return this.projectConfig || ({} as ProjectConfig) + }, + + getHeaderSetting(): HeaderSetting { + return this.getProjectConfig.headerSetting + }, + getMenuSetting(): MenuSetting { + return this.getProjectConfig.menuSetting + }, + getTransitionSetting(): TransitionSetting { + return this.getProjectConfig.transitionSetting + }, + getMultiTabsSetting(): MultiTabsSetting { + return this.getProjectConfig.multiTabsSetting + }, + getComponentSize(): AppSizeType | undefined { + return this.componentSize + } + }, + actions: { + setPageLoading(loading: boolean): void { + this.pageLoading = loading + }, + + setDarkMode(mode: ThemeEnum): void { + this.darkMode = mode + localStorage.setItem(APP_DARK_MODE_KEY_, mode) + }, + + setBeforeMiniInfo(state: BeforeMiniState): void { + this.beforeMiniInfo = state + }, + + setComponentSize(size: AppSizeType): void { + this.componentSize = size + }, + + setProjectConfig(config: DeepPartial): void { + this.projectConfig = deepMerge(this.projectConfig || {}, config) + Persistent.setLocal(PROJ_CFG_KEY, this.projectConfig) + }, + + async resetAllState() { + resetRouter() + Persistent.clearAll() + }, + async setPageLoadingAction(loading: boolean): Promise { + if (loading) { + clearTimeout(timeId) + // Prevent flicker + timeId = setTimeout(() => { + this.setPageLoading(loading) + }, 50) + } else { + this.setPageLoading(loading) + clearTimeout(timeId) + } + } + } +}) + +// Need to be used outside the setup +export function useAppStoreWithOut() { + return useAppStore(store) +} diff --git a/src/store/modules/errorLog.ts b/src/store/modules/errorLog.ts new file mode 100644 index 00000000..f9f2b444 --- /dev/null +++ b/src/store/modules/errorLog.ts @@ -0,0 +1,73 @@ +import type { ErrorLogInfo } from '@/types/store' + +import { defineStore } from 'pinia' +import { store } from '@/store' + +import { formatToDateTime } from '@/utils/dateUtil' +import projectSetting from '@/settings/projectSetting' + +import { ErrorTypeEnum } from '@/enums/exceptionEnum' + +export interface ErrorLogState { + errorLogInfoList: Nullable + errorLogListCount: number +} + +export const useErrorLogStore = defineStore('app-error-log', { + state: (): ErrorLogState => ({ + errorLogInfoList: null, + errorLogListCount: 0 + }), + getters: { + getErrorLogInfoList(): ErrorLogInfo[] { + return this.errorLogInfoList || [] + }, + getErrorLogListCount(): number { + return this.errorLogListCount + } + }, + actions: { + addErrorLogInfo(info: ErrorLogInfo) { + const item = { + ...info, + time: formatToDateTime(new Date()) + } + this.errorLogInfoList = [item, ...(this.errorLogInfoList || [])] + this.errorLogListCount += 1 + }, + + setErrorLogListCount(count: number): void { + this.errorLogListCount = count + }, + + /** + * Triggered after ajax request error + * @param error + * @returns + */ + addAjaxErrorInfo(error) { + const { useErrorHandle } = projectSetting + if (!useErrorHandle) { + return + } + const errInfo: Partial = { + message: error.message, + type: ErrorTypeEnum.AJAX + } + if (error.response) { + const { config: { url = '', data: params = '', method = 'get', headers = {} } = {}, data = {} } = error.response + errInfo.url = url + errInfo.name = 'Ajax Error!' + errInfo.file = '-' + errInfo.stack = JSON.stringify(data) + errInfo.detail = JSON.stringify({ params, method, headers }) + } + this.addErrorLogInfo(errInfo as ErrorLogInfo) + } + } +}) + +// Need to be used outside the setup +export function useErrorLogStoreWithOut() { + return useErrorLogStore(store) +} diff --git a/src/store/modules/locale.ts b/src/store/modules/locale.ts new file mode 100644 index 00000000..392b3339 --- /dev/null +++ b/src/store/modules/locale.ts @@ -0,0 +1,54 @@ +import type { LocaleSetting, LocaleType } from '@/types/config' + +import { defineStore } from 'pinia' +import { store } from '@/store' + +import { LOCALE_KEY } from '@/enums/cacheEnum' +import { createLocalStorage } from '@/utils/cache' +import { localeSetting } from '@/settings/localeSetting' + +const ls = createLocalStorage() + +const lsLocaleSetting = (ls.get(LOCALE_KEY) || localeSetting) as LocaleSetting + +interface LocaleState { + localInfo: LocaleSetting +} + +export const useLocaleStore = defineStore('app-locale', { + state: (): LocaleState => ({ + localInfo: lsLocaleSetting + }), + getters: { + getShowPicker(): boolean { + return !!this.localInfo?.showPicker + }, + getLocale(): LocaleType { + return this.localInfo?.locale ?? 'zh_CN' + } + }, + actions: { + /** + * Set up multilingual information and cache + * @param info multilingual info + */ + setLocaleInfo(info: Partial) { + this.localInfo = { ...this.localInfo, ...info } + ls.set(LOCALE_KEY, this.localInfo) + }, + /** + * Initialize multilingual information and load the existing configuration from the local cache + */ + initLocale() { + this.setLocaleInfo({ + ...localeSetting, + ...this.localInfo + }) + } + } +}) + +// Need to be used outside the setup +export function useLocaleStoreWithOut() { + return useLocaleStore(store) +} diff --git a/src/store/modules/lock.ts b/src/store/modules/lock.ts new file mode 100644 index 00000000..06f00dd5 --- /dev/null +++ b/src/store/modules/lock.ts @@ -0,0 +1,58 @@ +import type { LockInfo } from '@/types/store' + +import { defineStore } from 'pinia' + +import { LOCK_INFO_KEY } from '@/enums/cacheEnum' +import { Persistent } from '@/utils/cache/persistent' +import { useUserStore } from './user' + +interface LockState { + lockInfo: Nullable +} + +export const useLockStore = defineStore('app-lock', { + state: (): LockState => ({ + lockInfo: Persistent.getLocal(LOCK_INFO_KEY) + }), + getters: { + getLockInfo(): Nullable { + return this.lockInfo + } + }, + actions: { + setLockInfo(info: LockInfo) { + this.lockInfo = Object.assign({}, this.lockInfo, info) + Persistent.setLocal(LOCK_INFO_KEY, this.lockInfo, true) + }, + resetLockInfo() { + Persistent.removeLocal(LOCK_INFO_KEY, true) + this.lockInfo = null + }, + // Unlock + async unLock(password?: string) { + const userStore = useUserStore() + if (this.lockInfo?.pwd === password) { + this.resetLockInfo() + return true + } + const tryLogin = async () => { + try { + const username = userStore.getUserInfo?.username + const res = await userStore.login({ + username, + password: password!, + goHome: false, + mode: 'none' + }) + if (res) { + this.resetLockInfo() + } + return res + } catch (error) { + return false + } + } + return await tryLogin() + } + } +}) diff --git a/src/store/modules/multipleTab.ts b/src/store/modules/multipleTab.ts new file mode 100644 index 00000000..edb4b127 --- /dev/null +++ b/src/store/modules/multipleTab.ts @@ -0,0 +1,357 @@ +import type { RouteLocationNormalized, RouteLocationRaw, Router } from 'vue-router' + +import { toRaw, unref } from 'vue' +import { defineStore } from 'pinia' +import { store } from '@/store' + +import { useGo, useRedo } from '@/hooks/web/usePage' +import { Persistent } from '@/utils/cache/persistent' + +import { PageEnum } from '@/enums/pageEnum' +import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '@/router/routes/basic' +import { getRawRoute } from '@/utils' +import { MULTIPLE_TABS_KEY } from '@/enums/cacheEnum' + +import projectSetting from '@/settings/projectSetting' +import { useUserStore } from '@/store/modules/user' + +export interface MultipleTabState { + cacheTabList: Set + tabList: RouteLocationNormalized[] + lastDragEndIndex: number +} + +function handleGotoPage(router: Router) { + const go = useGo(router) + go(unref(router.currentRoute).path, true) +} + +const getToTarget = (tabItem: RouteLocationNormalized) => { + const { params, path, query } = tabItem + return { + params: params || {}, + path, + query: query || {} + } +} + +const cacheTab = projectSetting.multiTabsSetting.cache + +export const useMultipleTabStore = defineStore('app-multiple-tab', { + state: (): MultipleTabState => ({ + // Tabs that need to be cached + cacheTabList: new Set(), + // multiple tab list + tabList: cacheTab ? Persistent.getLocal(MULTIPLE_TABS_KEY) || [] : [], + // Index of the last moved tab + lastDragEndIndex: 0 + }), + getters: { + getTabList(): RouteLocationNormalized[] { + return this.tabList + }, + getCachedTabList(): string[] { + return Array.from(this.cacheTabList) + }, + getLastDragEndIndex(): number { + return this.lastDragEndIndex + } + }, + actions: { + /** + * Update the cache according to the currently opened tabs + */ + async updateCacheTab() { + const cacheMap: Set = new Set() + + for (const tab of this.tabList) { + const item = getRawRoute(tab) + // Ignore the cache + const needCache = !item.meta?.ignoreKeepAlive + if (!needCache) { + continue + } + const name = item.name as string + cacheMap.add(name) + } + this.cacheTabList = cacheMap + }, + + /** + * Refresh tabs + */ + async refreshPage(router: Router) { + const { currentRoute } = router + const route = unref(currentRoute) + const name = route.name + + const findTab = this.getCachedTabList.find((item) => item === name) + if (findTab) { + this.cacheTabList.delete(findTab) + } + const redo = useRedo(router) + await redo() + }, + clearCacheTabs(): void { + this.cacheTabList = new Set() + }, + resetState(): void { + this.tabList = [] + this.clearCacheTabs() + }, + goToPage(router: Router) { + const go = useGo(router) + const len = this.tabList.length + const { path } = unref(router.currentRoute) + + let toPath: PageEnum | string = PageEnum.BASE_HOME + + if (len > 0) { + const page = this.tabList[len - 1] + const p = page.fullPath || page.path + if (p) { + toPath = p + } + } + // Jump to the current page and report an error + path !== toPath && go(toPath as PageEnum, true) + }, + + async addTab(route: RouteLocationNormalized) { + const { path, name, fullPath, params, query, meta } = getRawRoute(route) + // 404 The page does not need to add a tab + if ( + path === PageEnum.ERROR_PAGE || + path === PageEnum.BASE_LOGIN || + !name || + [REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string) + ) { + return + } + + let updateIndex = -1 + // Existing pages, do not add tabs repeatedly + const tabHasExits = this.tabList.some((tab, index) => { + updateIndex = index + return (tab.fullPath || tab.path) === (fullPath || path) + }) + + // If the tab already exists, perform the update operation + if (tabHasExits) { + const curTab = toRaw(this.tabList)[updateIndex] + if (!curTab) { + return + } + curTab.params = params || curTab.params + curTab.query = query || curTab.query + curTab.fullPath = fullPath || curTab.fullPath + this.tabList.splice(updateIndex, 1, curTab) + } else { + // Add tab + // 获取动态路由打开数,超过 0 即代表需要控制打开数 + const dynamicLevel = meta?.dynamicLevel ?? -1 + if (dynamicLevel > 0) { + // 如果动态路由层级大于 0 了,那么就要限制该路由的打开数限制了 + // 首先获取到真实的路由,使用配置方式减少计算开销. + // const realName: string = path.match(/(\S*)\//)![1]; + const realPath = meta?.realPath ?? '' + // 获取到已经打开的动态路由数, 判断是否大于某一个值 + if (this.tabList.filter((e) => e.meta?.realPath ?? '' === realPath).length >= dynamicLevel) { + // 关闭第一个 + const index = this.tabList.findIndex((item) => item.meta.realPath === realPath) + index !== -1 && this.tabList.splice(index, 1) + } + } + this.tabList.push(route) + } + this.updateCacheTab() + cacheTab && Persistent.setLocal(MULTIPLE_TABS_KEY, this.tabList) + }, + + async closeTab(tab: RouteLocationNormalized, router: Router) { + const close = (route: RouteLocationNormalized) => { + const { fullPath, meta: { affix } = {} } = route + if (affix) { + return + } + const index = this.tabList.findIndex((item) => item.fullPath === fullPath) + index !== -1 && this.tabList.splice(index, 1) + } + + const { currentRoute, replace } = router + + const { path } = unref(currentRoute) + if (path !== tab.path) { + // Closed is not the activation tab + close(tab) + this.updateCacheTab() + return + } + + // Closed is activated atb + let toTarget: RouteLocationRaw = {} + + const index = this.tabList.findIndex((item) => item.path === path) + + // If the current is the leftmost tab + if (index === 0) { + // There is only one tab, then jump to the homepage, otherwise jump to the right tab + if (this.tabList.length === 1) { + const userStore = useUserStore() + toTarget = userStore.getUserInfo.homePath || PageEnum.BASE_HOME + } else { + // Jump to the right tab + const page = this.tabList[index + 1] + toTarget = getToTarget(page) + } + } else { + // Close the current tab + const page = this.tabList[index - 1] + toTarget = getToTarget(page) + } + close(currentRoute.value) + await replace(toTarget) + }, + + // Close according to key + async closeTabByKey(key: string, router: Router) { + const index = this.tabList.findIndex((item) => (item.fullPath || item.path) === key) + if (index !== -1) { + await this.closeTab(this.tabList[index], router) + const { currentRoute, replace } = router + // 检查当前路由是否存在于tabList中 + const isActivated = this.tabList.findIndex((item) => { + return item.fullPath === currentRoute.value.fullPath + }) + // 如果当前路由不存在于TabList中,尝试切换到其它路由 + if (isActivated === -1) { + let pageIndex + if (index > 0) { + pageIndex = index - 1 + } else if (index < this.tabList.length - 1) { + pageIndex = index + 1 + } else { + pageIndex = -1 + } + if (pageIndex >= 0) { + const page = this.tabList[index - 1] + const toTarget = getToTarget(page) + await replace(toTarget) + } + } + } + }, + + // Sort the tabs + async sortTabs(oldIndex: number, newIndex: number) { + const currentTab = this.tabList[oldIndex] + this.tabList.splice(oldIndex, 1) + this.tabList.splice(newIndex, 0, currentTab) + this.lastDragEndIndex = this.lastDragEndIndex + 1 + }, + + // Close the tab on the right and jump + async closeLeftTabs(route: RouteLocationNormalized, router: Router) { + const index = this.tabList.findIndex((item) => item.path === route.path) + + if (index > 0) { + const leftTabs = this.tabList.slice(0, index) + const pathList: string[] = [] + for (const item of leftTabs) { + const affix = item?.meta?.affix ?? false + if (!affix) { + pathList.push(item.fullPath) + } + } + this.bulkCloseTabs(pathList) + } + this.updateCacheTab() + handleGotoPage(router) + }, + + // Close the tab on the left and jump + async closeRightTabs(route: RouteLocationNormalized, router: Router) { + const index = this.tabList.findIndex((item) => item.fullPath === route.fullPath) + + if (index >= 0 && index < this.tabList.length - 1) { + const rightTabs = this.tabList.slice(index + 1, this.tabList.length) + + const pathList: string[] = [] + for (const item of rightTabs) { + const affix = item?.meta?.affix ?? false + if (!affix) { + pathList.push(item.fullPath) + } + } + this.bulkCloseTabs(pathList) + } + this.updateCacheTab() + handleGotoPage(router) + }, + + async closeAllTab(router: Router) { + this.tabList = this.tabList.filter((item) => item?.meta?.affix ?? false) + this.clearCacheTabs() + this.goToPage(router) + }, + + /** + * Close other tabs + */ + async closeOtherTabs(route: RouteLocationNormalized, router: Router) { + const closePathList = this.tabList.map((item) => item.fullPath) + + const pathList: string[] = [] + + for (const path of closePathList) { + if (path !== route.fullPath) { + const closeItem = this.tabList.find((item) => item.path === path) + if (!closeItem) { + continue + } + const affix = closeItem?.meta?.affix ?? false + if (!affix) { + pathList.push(closeItem.fullPath) + } + } + } + this.bulkCloseTabs(pathList) + this.updateCacheTab() + handleGotoPage(router) + }, + + /** + * Close tabs in bulk + */ + async bulkCloseTabs(pathList: string[]) { + this.tabList = this.tabList.filter((item) => !pathList.includes(item.fullPath)) + }, + + /** + * Set tab's title + */ + async setTabTitle(title: string, route: RouteLocationNormalized) { + const findTab = this.getTabList.find((item) => item === route) + if (findTab) { + findTab.meta.title = title + await this.updateCacheTab() + } + }, + /** + * replace tab's path + * **/ + async updateTabPath(fullPath: string, route: RouteLocationNormalized) { + const findTab = this.getTabList.find((item) => item === route) + if (findTab) { + findTab.fullPath = fullPath + findTab.path = fullPath + await this.updateCacheTab() + } + } + } +}) + +// Need to be used outside the setup +export function useMultipleTabWithOutStore() { + return useMultipleTabStore(store) +} diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts new file mode 100644 index 00000000..e1dd4854 --- /dev/null +++ b/src/store/modules/permission.ts @@ -0,0 +1,259 @@ +import type { AppRouteRecordRaw, Menu } from '@/router/types' + +import { defineStore } from 'pinia' +import { store } from '@/store' +import { useI18n } from '@/hooks/web/useI18n' +import { useUserStore } from './user' +import { useAppStoreWithOut } from './app' +import { toRaw } from 'vue' +import { transformObjToRoute, flatMultiLevelRoutes } from '@/router/helper/routeHelper' +import { transformRouteToMenu } from '@/router/helper/menuHelper' + +import projectSetting from '@/settings/projectSetting' + +import { PermissionModeEnum } from '@/enums/appEnum' + +import { asyncRoutes } from '@/router/routes' +import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '@/router/routes/basic' + +import { filter } from '@/utils/helper/treeHelper' + +import { getMenuList } from '@/api/sys/menu' +import { getPermCode } from '@/api/sys/user' + +import { useMessage } from '@/hooks/web/useMessage' +import { PageEnum } from '@/enums/pageEnum' + +interface PermissionState { + // Permission code list + // 权限代码列表 + permCodeList: string[] | number[] + // Whether the route has been dynamically added + // 路由是否动态添加 + isDynamicAddedRoute: boolean + // To trigger a menu update + // 触发菜单更新 + lastBuildMenuTime: number + // Backstage menu list + // 后台菜单列表 + backMenuList: Menu[] + // 菜单列表 + frontMenuList: Menu[] +} + +export const usePermissionStore = defineStore('app-permission', { + state: (): PermissionState => ({ + // 权限代码列表 + permCodeList: [], + // Whether the route has been dynamically added + // 路由是否动态添加 + isDynamicAddedRoute: false, + // To trigger a menu update + // 触发菜单更新 + lastBuildMenuTime: 0, + // Backstage menu list + // 后台菜单列表 + backMenuList: [], + // menu List + // 菜单列表 + frontMenuList: [] + }), + getters: { + getPermCodeList(): string[] | number[] { + return this.permCodeList + }, + getBackMenuList(): Menu[] { + return this.backMenuList + }, + getFrontMenuList(): Menu[] { + return this.frontMenuList + }, + getLastBuildMenuTime(): number { + return this.lastBuildMenuTime + }, + getIsDynamicAddedRoute(): boolean { + return this.isDynamicAddedRoute + } + }, + actions: { + setPermCodeList(codeList: string[]) { + this.permCodeList = codeList + }, + + setBackMenuList(list: Menu[]) { + this.backMenuList = list + list?.length > 0 && this.setLastBuildMenuTime() + }, + + setFrontMenuList(list: Menu[]) { + this.frontMenuList = list + }, + + setLastBuildMenuTime() { + this.lastBuildMenuTime = new Date().getTime() + }, + + setDynamicAddedRoute(added: boolean) { + this.isDynamicAddedRoute = added + }, + resetState(): void { + this.isDynamicAddedRoute = false + this.permCodeList = [] + this.backMenuList = [] + this.lastBuildMenuTime = 0 + }, + async changePermissionCode() { + const codeList = await getPermCode() + this.setPermCodeList(codeList) + }, + + // 构建路由 + async buildRoutesAction(): Promise { + const { t } = useI18n() + const userStore = useUserStore() + const appStore = useAppStoreWithOut() + + let routes: AppRouteRecordRaw[] = [] + const roleList = toRaw(userStore.getRoleList) || [] + const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig + + // 路由过滤器 在 函数filter 作为回调传入遍历使用 + const routeFilter = (route: AppRouteRecordRaw) => { + const { meta } = route + // 抽出角色 + const { roles } = meta || {} + if (!roles) return true + // 进行角色权限判断 + return roleList.some((role) => roles.includes(role)) + } + + const routeRemoveIgnoreFilter = (route: AppRouteRecordRaw) => { + const { meta } = route + // ignoreRoute 为true 则路由仅用于菜单生成,不会在实际的路由表中出现 + const { ignoreRoute } = meta || {} + // arr.filter 返回 true 表示该元素通过测试 + return !ignoreRoute + } + + /** + * @description 根据设置的首页path,修正routes中的affix标记(固定首页) + * */ + const patchHomeAffix = (routes: AppRouteRecordRaw[]) => { + if (!routes || routes.length === 0) return + let homePath: string = userStore.getUserInfo.homePath || PageEnum.BASE_HOME + + function patcher(routes: AppRouteRecordRaw[], parentPath = '') { + if (parentPath) parentPath = parentPath + '/' + routes.forEach((route: AppRouteRecordRaw) => { + const { path, children, redirect } = route + const currentPath = path.startsWith('/') ? path : parentPath + path + if (currentPath === homePath) { + if (redirect) { + homePath = route.redirect! as string + } else { + route.meta = Object.assign({}, route.meta, { affix: true }) + throw new Error('end') + } + } + children && children.length > 0 && patcher(children, currentPath) + }) + } + + try { + patcher(routes) + } catch (e) { + // 已处理完毕跳出循环 + } + return + } + + switch (permissionMode) { + // 角色权限 + case PermissionModeEnum.ROLE: + // 对非一级路由进行过滤 + routes = filter(asyncRoutes, routeFilter) + // 对一级路由根据角色权限过滤 + routes = routes.filter(routeFilter) + // Convert multi-level routing to level 2 routing + // 将多级路由转换为 2 级路由 + routes = flatMultiLevelRoutes(routes) + break + + // 路由映射, 默认进入该case + case PermissionModeEnum.ROUTE_MAPPING: + // 对非一级路由进行过滤 + routes = filter(asyncRoutes, routeFilter) + // 对一级路由再次根据角色权限过滤 + routes = routes.filter(routeFilter) + // 将路由转换成菜单 + const menuList = transformRouteToMenu(routes, true) + // 移除掉 ignoreRoute: true 的路由 非一级路由 + routes = filter(routes, routeRemoveIgnoreFilter) + // 移除掉 ignoreRoute: true 的路由 一级路由; + routes = routes.filter(routeRemoveIgnoreFilter) + // 对菜单进行排序 + menuList.sort((a, b) => { + return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0) + }) + + // 设置菜单列表 + this.setFrontMenuList(menuList) + + // Convert multi-level routing to level 2 routing + // 将多级路由转换为 2 级路由 + routes = flatMultiLevelRoutes(routes) + break + + // If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below + // 如果确定不需要做后台动态权限,请在下方注释整个判断 + case PermissionModeEnum.BACK: + const { createMessage } = useMessage() + + createMessage.loading({ + content: t('sys.app.menuLoading'), + duration: 1 + }) + + // !Simulate to obtain permission codes from the background, + // 模拟从后台获取权限码, + // this function may only need to be executed once, and the actual project can be put at the right time by itself + // 这个功能可能只需要执行一次,实际项目可以自己放在合适的时间 + let routeList: AppRouteRecordRaw[] = [] + try { + await this.changePermissionCode() + routeList = (await getMenuList()) as AppRouteRecordRaw[] + } catch (error) { + console.error(error) + } + + // Dynamically introduce components + // 动态引入组件 + routeList = transformObjToRoute(routeList) + + // Background routing to menu structure + // 后台路由到菜单结构 + const backMenuList = transformRouteToMenu(routeList) + this.setBackMenuList(backMenuList) + + // remove meta.ignoreRoute item + // 删除 meta.ignoreRoute 项 + routeList = filter(routeList, routeRemoveIgnoreFilter) + routeList = routeList.filter(routeRemoveIgnoreFilter) + + routeList = flatMultiLevelRoutes(routeList) + routes = [PAGE_NOT_FOUND_ROUTE, ...routeList] + break + } + + routes.push(ERROR_LOG_ROUTE) + patchHomeAffix(routes) + return routes + } + } +}) + +// Need to be used outside the setup +// 需要在设置之外使用 +export function usePermissionStoreWithOut() { + return usePermissionStore(store) +} diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts new file mode 100644 index 00000000..25f74b8d --- /dev/null +++ b/src/store/modules/user.ts @@ -0,0 +1,176 @@ +import type { UserInfo } from '@/types/store' +import type { ErrorMessageMode } from '@/types/axios' +import { defineStore } from 'pinia' +import { store } from '@/store' +import { RoleEnum } from '@/enums/roleEnum' +import { PageEnum } from '@/enums/pageEnum' +import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from '@/enums/cacheEnum' +import { getAuthCache, setAuthCache } from '@/utils/auth' +import { GetUserInfoModel, LoginParams } from '@/api/sys/model/userModel' +import { doLogout, getUserInfo, loginApi } from '@/api/sys/user' +import { useI18n } from '@/hooks/web/useI18n' +import { useMessage } from '@/hooks/web/useMessage' +import { router } from '@/router' +import { usePermissionStore } from '@/store/modules/permission' +import { RouteRecordRaw } from 'vue-router' +import { PAGE_NOT_FOUND_ROUTE } from '@/router/routes/basic' +import { isArray } from '@/utils/is' +import { h } from 'vue' + +interface UserState { + userInfo: Nullable + token?: string + roleList: RoleEnum[] + sessionTimeout?: boolean + lastUpdateTime: number +} + +export const useUserStore = defineStore('app-user', { + state: (): UserState => ({ + // user info + userInfo: null, + // token + token: undefined, + // roleList + roleList: [], + // Whether the login expired + sessionTimeout: false, + // Last fetch time + lastUpdateTime: 0 + }), + getters: { + getUserInfo(): UserInfo { + return this.userInfo || getAuthCache(USER_INFO_KEY) || {} + }, + getToken(): string { + return this.token || getAuthCache(TOKEN_KEY) + }, + getRoleList(): RoleEnum[] { + return this.roleList.length > 0 ? this.roleList : getAuthCache(ROLES_KEY) + }, + getSessionTimeout(): boolean { + return !!this.sessionTimeout + }, + getLastUpdateTime(): number { + return this.lastUpdateTime + } + }, + actions: { + setToken(info: string | undefined) { + this.token = info ? info : '' // for null or undefined value + setAuthCache(TOKEN_KEY, info) + }, + setRoleList(roleList: RoleEnum[]) { + this.roleList = roleList + setAuthCache(ROLES_KEY, roleList) + }, + setUserInfo(info: UserInfo | null) { + this.userInfo = info + this.lastUpdateTime = new Date().getTime() + setAuthCache(USER_INFO_KEY, info) + }, + setSessionTimeout(flag: boolean) { + this.sessionTimeout = flag + }, + resetState() { + this.userInfo = null + this.token = '' + this.roleList = [] + this.sessionTimeout = false + }, + /** + * @description: login + */ + async login( + params: LoginParams & { + goHome?: boolean + mode?: ErrorMessageMode + } + ): Promise { + try { + const { goHome = true, mode, ...loginParams } = params + const data = await loginApi(loginParams, mode) + const { token } = data + + // save token + this.setToken(token) + return this.afterLoginAction(goHome) + } catch (error) { + return Promise.reject(error) + } + }, + async afterLoginAction(goHome?: boolean): Promise { + if (!this.getToken) return null + // get user info + const userInfo = await this.getUserInfoAction() + + const sessionTimeout = this.sessionTimeout + if (sessionTimeout) { + this.setSessionTimeout(false) + } else { + const permissionStore = usePermissionStore() + if (!permissionStore.isDynamicAddedRoute) { + const routes = await permissionStore.buildRoutesAction() + routes.forEach((route) => { + router.addRoute(route as unknown as RouteRecordRaw) + }) + router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw) + permissionStore.setDynamicAddedRoute(true) + } + goHome && (await router.replace(userInfo?.homePath || PageEnum.BASE_HOME)) + } + return userInfo + }, + async getUserInfoAction(): Promise { + if (!this.getToken) return null + const userInfo = await getUserInfo() + const { roles = [] } = userInfo + if (isArray(roles)) { + const roleList = roles.map((item) => item.value) as RoleEnum[] + this.setRoleList(roleList) + } else { + userInfo.roles = [] + this.setRoleList([]) + } + this.setUserInfo(userInfo) + return userInfo + }, + /** + * @description: logout + */ + async logout(goLogin = false) { + if (this.getToken) { + try { + await doLogout() + } catch { + console.log('注销Token失败') + } + } + this.setToken(undefined) + this.setSessionTimeout(false) + this.setUserInfo(null) + goLogin && router.push(PageEnum.BASE_LOGIN) + }, + + /** + * @description: Confirm before logging out + */ + confirmLoginOut() { + const { createConfirm } = useMessage() + const { t } = useI18n() + createConfirm({ + iconType: 'warning', + title: () => h('span', t('sys.app.logoutTip')), + content: () => h('span', t('sys.app.logoutMessage')), + onOk: async () => { + await this.logout(true) + } + }) + } + } +}) + +// Need to be used outside the setup +export function useUserStoreWithOut() { + return useUserStore(store) +} diff --git a/src/types/axios.d.ts b/src/types/axios.d.ts new file mode 100644 index 00000000..0d8ceb6a --- /dev/null +++ b/src/types/axios.d.ts @@ -0,0 +1,56 @@ +export type ErrorMessageMode = 'none' | 'modal' | 'message' | undefined +export type SuccessMessageMode = ErrorMessageMode + +export interface RequestOptions { + // Splicing request parameters to url + joinParamsToUrl?: boolean + // Format request parameter time + formatDate?: boolean + // Whether to process the request result + isTransformResponse?: boolean + // Whether to return native response headers + // For example: use this attribute when you need to get the response headers + isReturnNativeResponse?: boolean + // Whether to join url + joinPrefix?: boolean + // Interface address, use the default apiUrl if you leave it blank + apiUrl?: string + // 请求拼接路径 + urlPrefix?: string + // Error message prompt type + errorMessageMode?: ErrorMessageMode + // Success message prompt type + successMessageMode?: SuccessMessageMode + // Whether to add a timestamp + joinTime?: boolean + ignoreCancelToken?: boolean + // Whether to send token in header + withToken?: boolean + // 请求重试机制 + retryRequest?: RetryRequest +} + +export interface RetryRequest { + isOpenRetry: boolean + count: number + waitTime: number +} +export interface Result { + code: number + type: 'success' | 'error' | 'warning' + message: string + result: T +} + +// multipart/form-data: upload file +export interface UploadFileParams { + // Other parameters + data?: Recordable + // File parameter interface field name + name?: string + // file name + file: File | Blob + // file name + filename?: string + [key: string]: any +} diff --git a/src/types/config.d.ts b/src/types/config.d.ts new file mode 100644 index 00000000..5ba8bad3 --- /dev/null +++ b/src/types/config.d.ts @@ -0,0 +1,174 @@ +import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '@/enums/menuEnum' +import { + ContentEnum, + PermissionModeEnum, + ThemeEnum, + RouterTransitionEnum, + SettingButtonPositionEnum, + SessionTimeoutProcessingEnum +} from '@/enums/appEnum' + +import { CacheTypeEnum } from '@/enums/cacheEnum' + +export type LocaleType = 'zh_CN' | 'en' | 'ru' | 'ja' | 'ko' + +export type AppSizeType = 'small' | 'middle' | 'large' + +export interface MenuSetting { + bgColor: string + fixed: boolean + collapsed: boolean + siderHidden: boolean + canDrag: boolean + show: boolean + hidden: boolean + split: boolean + menuWidth: number + mode: MenuModeEnum + type: MenuTypeEnum + theme: ThemeEnum + topMenuAlign: 'start' | 'center' | 'end' + trigger: TriggerEnum + accordion: boolean + closeMixSidebarOnChange: boolean + collapsedShowTitle: boolean + mixSideTrigger: MixSidebarTriggerEnum + mixSideFixed: boolean +} + +export interface MultiTabsSetting { + cache: boolean + show: boolean + showQuick: boolean + canDrag: boolean + showRedo: boolean + showFold: boolean +} + +export interface HeaderSetting { + bgColor: string + fixed: boolean + show: boolean + theme: ThemeEnum + // Turn on full screen + showFullScreen: boolean + // Whether to show the lock screen + useLockPage: boolean + // Show document button + showDoc: boolean + // Show message center button + showNotice: boolean + showSearch: boolean +} + +export interface LocaleSetting { + showPicker: boolean + // Current language + locale: LocaleType + // default language + fallback: LocaleType + // available Locales + availableLocales: LocaleType[] +} + +export interface SizeSetting { + showPicker: boolean + // Current size + size: AppSizeType + // default size + fallback: AppSizeType + // available size + availableSizes: AppSizeType[] +} + +export interface TransitionSetting { + // Whether to open the page switching animation + enable: boolean + // Route basic switching animation + basicTransition: RouterTransitionEnum + // Whether to open page switching loading + openPageLoading: boolean + // Whether to open the top progress bar + openNProgress: boolean +} + +export interface ProjectConfig { + // Storage location of permission related information + permissionCacheType: CacheTypeEnum + // Whether to show the configuration button + showSettingButton: boolean + // Whether to show the theme switch button + showDarkModeToggle: boolean + // Configure where the button is displayed + settingButtonPosition: SettingButtonPositionEnum + // Permission mode + permissionMode: PermissionModeEnum + // Session timeout processing + sessionTimeoutProcessing: SessionTimeoutProcessingEnum + // Website gray mode, open for possible mourning dates + grayMode: boolean + // Whether to turn on the color weak mode + colorWeak: boolean + // Theme color + themeColor: string + + // The main interface is displayed in full screen, the menu is not displayed, and the top + fullContent: boolean + // content width + contentMode: ContentEnum + // Whether to display the logo + showLogo: boolean + // Whether to show the global footer + showFooter: boolean + // menuType: MenuTypeEnum; + headerSetting: HeaderSetting + // menuSetting + menuSetting: MenuSetting + // Multi-tab settings + multiTabsSetting: MultiTabsSetting + // Animation configuration + transitionSetting: TransitionSetting + // pageLayout whether to enable keep-alive + openKeepAlive: boolean + // Lock screen time + lockTime: number + // Show breadcrumbs + showBreadCrumb: boolean + // Show breadcrumb icon + showBreadCrumbIcon: boolean + // Use error-handler-plugin + useErrorHandle: boolean + // Whether to open back to top + useOpenBackTop: boolean + // Is it possible to embed iframe pages + canEmbedIFramePage: boolean + // Whether to delete unclosed messages and notify when switching the interface + closeMessageOnSwitch: boolean + // Whether to cancel the http request that has been sent but not responded when switching the interface. + removeAllHttpPending: boolean +} + +export interface GlobConfig { + // Site title + title: string + // Service interface url + apiUrl: string + // Upload url + uploadUrl?: string + // Service interface url prefix + urlPrefix?: string + // Project abbreviation + shortName: string +} +export interface GlobEnvConfig { + // Site title + VITE_GLOB_APP_TITLE: string + // Service interface url + VITE_GLOB_API_URL: string + // Service interface url prefix + VITE_GLOB_API_URL_PREFIX?: string + // Project abbreviation + VITE_GLOB_APP_SHORT_NAME: string + // Upload url + VITE_GLOB_UPLOAD_URL?: string +} diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 00000000..440861f2 --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,91 @@ +import type { ComponentRenderProxy, VNode, VNodeChild, ComponentPublicInstance, FunctionalComponent, PropType as VuePropType } from 'vue' + +declare global { + const __APP_INFO__: { + pkg: { + name: string + version: string + dependencies: Recordable + devDependencies: Recordable + } + lastBuildTime: string + } + // declare interface Window { + // // Global vue app instance + // __APP__: App; + // } + + // vue + declare type PropType = VuePropType + declare type VueNode = VNodeChild | JSX.Element + + export type Writable = { + -readonly [P in keyof T]: T[P] + } + + declare type Nullable = T | null + declare type NonNullable = T extends null | undefined ? never : T + declare type Recordable = Record + declare type ReadonlyRecordable = { + readonly [key: string]: T + } + declare type Indexable = { + [key: string]: T + } + declare type DeepPartial = { + [P in keyof T]?: DeepPartial + } + declare type TimeoutHandle = ReturnType + declare type IntervalHandle = ReturnType + + declare interface ChangeEvent extends Event { + target: HTMLInputElement + } + + declare interface WheelEvent { + path?: EventTarget[] + } + interface ImportMetaEnv extends ViteEnv { + __: unknown + } + + declare interface ViteEnv { + VITE_PORT: number + VITE_USE_MOCK: boolean + VITE_USE_PWA: boolean + VITE_PUBLIC_PATH: string + VITE_PROXY: [string, string][] + VITE_GLOB_APP_TITLE: string + VITE_GLOB_APP_SHORT_NAME: string + VITE_USE_CDN: boolean + VITE_DROP_CONSOLE: boolean + VITE_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none' + VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean + VITE_LEGACY: boolean + VITE_GENERATE_UI: string + } + + declare function parseInt(s: string | number, radix?: number): number + + declare function parseFloat(string: string | number): number + + namespace JSX { + // tslint:disable no-empty-interface + type Element = VNode + // tslint:disable no-empty-interface + type ElementClass = ComponentRenderProxy + interface ElementAttributesProperty { + $props: any + } + interface IntrinsicElements { + [elem: string]: any + } + interface IntrinsicAttributes { + [elem: string]: any + } + } +} + +declare module 'vue' { + export type JSXComponent = { new (): ComponentPublicInstance } | FunctionalComponent +} diff --git a/src/types/index.d.ts b/src/types/index.d.ts new file mode 100644 index 00000000..d721cba0 --- /dev/null +++ b/src/types/index.d.ts @@ -0,0 +1,27 @@ +declare interface Fn { + (...arg: T[]): R +} + +declare interface PromiseFn { + (...arg: T[]): Promise +} + +declare type RefType = T | null + +declare type LabelValueOptions = { + label: string + value: any + [key: string]: string | number | boolean +}[] + +declare type EmitType = (event: string, ...args: any[]) => void + +declare type TargetContext = '_self' | '_blank' + +declare interface ComponentElRef { + $el: T +} + +declare type ComponentRef = ComponentElRef | null + +declare type ElRef = Nullable diff --git a/src/types/module.d.ts b/src/types/module.d.ts new file mode 100644 index 00000000..1c5a0c4b --- /dev/null +++ b/src/types/module.d.ts @@ -0,0 +1,16 @@ +declare module '*.vue' { + import { DefineComponent } from 'vue' + const Component: DefineComponent<{}, {}, any> + export default Component +} + +declare module 'ant-design-vue/es/locale/*' { + import { Locale } from 'ant-design-vue/types/locale-provider' + const locale: Locale & ReadonlyRecordable + export default locale as Locale & ReadonlyRecordable +} + +declare module 'virtual:*' { + const result: any + export default result +} diff --git a/src/types/store.d.ts b/src/types/store.d.ts new file mode 100644 index 00000000..f4e946d2 --- /dev/null +++ b/src/types/store.d.ts @@ -0,0 +1,48 @@ +import { ErrorTypeEnum } from '@/enums/exceptionEnum' +import { MenuModeEnum, MenuTypeEnum } from '@/enums/menuEnum' +import { RoleInfo } from '@/api/sys/model/userModel' + +// Lock screen information +export interface LockInfo { + // Password required + pwd?: string | undefined + // Is it locked? + isLock?: boolean +} + +// Error-log information +export interface ErrorLogInfo { + // Type of error + type: ErrorTypeEnum + // Error file + file: string + // Error name + name?: string + // Error message + message: string + // Error stack + stack?: string + // Error detail + detail: string + // Error url + url: string + // Error time + time?: string +} + +export interface UserInfo { + userId: string | number + username: string + realName: string + avatar: string + desc?: string + homePath?: string + roles: RoleInfo[] +} + +export interface BeforeMiniState { + menuCollapsed?: boolean + menuSplit?: boolean + menuMode?: MenuModeEnum + menuType?: MenuTypeEnum +} diff --git a/src/types/utils.d.ts b/src/types/utils.d.ts new file mode 100644 index 00000000..e306d02c --- /dev/null +++ b/src/types/utils.d.ts @@ -0,0 +1,5 @@ +import type { ComputedRef, Ref } from 'vue' + +export type DynamicProps = { + [P in keyof T]: Ref | T[P] | ComputedRef +} diff --git a/src/types/vue-router.d.ts b/src/types/vue-router.d.ts new file mode 100644 index 00000000..92a890e0 --- /dev/null +++ b/src/types/vue-router.d.ts @@ -0,0 +1,47 @@ +import { RoleEnum } from '@/enums/roleEnum' + +export {} + +declare module 'vue-router' { + interface RouteMeta extends Record { + orderNo?: number + // title + title: string + // dynamic router level. + dynamicLevel?: number + // dynamic router real route path (For performance). + realPath?: string + // Whether to ignore permissions + ignoreAuth?: boolean + // role info + roles?: RoleEnum[] + // Whether not to cache + ignoreKeepAlive?: boolean + // Is it fixed on tab + affix?: boolean + // icon on tab + icon?: string + frameSrc?: string + // current page transition + transitionName?: string + // Whether the route has been dynamically added + hideBreadcrumb?: boolean + // Hide submenu + hideChildrenInMenu?: boolean + // Carrying parameters + carryParam?: boolean + // Used internally to mark single-level menus + single?: boolean + // Currently active menu + currentActiveMenu?: string + // Never show in tab + hideTab?: boolean + // Never show in menu + hideMenu?: boolean + isLink?: boolean + // only build for Menu + ignoreRoute?: boolean + // Hide path for children + hidePathForChildren?: boolean + } +} diff --git a/src/utils/auth/index.ts b/src/utils/auth/index.ts new file mode 100644 index 00000000..e67f9f33 --- /dev/null +++ b/src/utils/auth/index.ts @@ -0,0 +1,26 @@ +import { Persistent, BasicKeys } from '@/utils/cache/persistent' +import { CacheTypeEnum } from '@/enums/cacheEnum' +import projectSetting from '@/settings/projectSetting' +import { TOKEN_KEY } from '@/enums/cacheEnum' + +const { permissionCacheType } = projectSetting +const isLocal = permissionCacheType === CacheTypeEnum.LOCAL + +export function getToken() { + return getAuthCache(TOKEN_KEY) +} + +export function getAuthCache(key: BasicKeys) { + const fn = isLocal ? Persistent.getLocal : Persistent.getSession + return fn(key) as T +} + +export function setAuthCache(key: BasicKeys, value) { + const fn = isLocal ? Persistent.setLocal : Persistent.setSession + return fn(key, value, true) +} + +export function clearAuthCache(immediate = true) { + const fn = isLocal ? Persistent.clearLocal : Persistent.clearSession + return fn(immediate) +} diff --git a/src/utils/bem.ts b/src/utils/bem.ts new file mode 100644 index 00000000..05ad29a0 --- /dev/null +++ b/src/utils/bem.ts @@ -0,0 +1,52 @@ +import { prefixCls } from '@/settings/designSetting' + +type Mod = string | { [key: string]: any } +type Mods = Mod | Mod[] + +export type BEM = ReturnType + +function genBem(name: string, mods?: Mods): string { + if (!mods) { + return '' + } + + if (typeof mods === 'string') { + return ` ${name}--${mods}` + } + + if (Array.isArray(mods)) { + return mods.reduce((ret, item) => ret + genBem(name, item), '') + } + + return Object.keys(mods).reduce((ret, key) => ret + (mods[key] ? genBem(name, key) : ''), '') +} + +/** + * bem helper + * b() // 'button' + * b('text') // 'button__text' + * b({ disabled }) // 'button button--disabled' + * b('text', { disabled }) // 'button__text button__text--disabled' + * b(['disabled', 'primary']) // 'button button--disabled button--primary' + */ +export function buildBEM(name: string) { + return (el?: Mods, mods?: Mods): Mods => { + if (el && typeof el !== 'string') { + mods = el + el = '' + } + + el = el ? `${name}__${el}` : name + + return `${el}${genBem(el, mods)}` + } +} + +export function createBEM(name: string) { + return [buildBEM(`${prefixCls}-${name}`)] +} + +export function createNamespace(name: string) { + const prefixedName = `${prefixCls}-${name}` + return [prefixedName, buildBEM(prefixedName)] as const +} diff --git a/src/utils/cache/index.ts b/src/utils/cache/index.ts new file mode 100644 index 00000000..67d1552b --- /dev/null +++ b/src/utils/cache/index.ts @@ -0,0 +1,32 @@ +import { getStorageShortName } from '@/utils/env' +import { createStorage as create, CreateStorageParams } from './storageCache' +import { enableStorageEncryption } from '@/settings/encryptionSetting' +import { DEFAULT_CACHE_TIME } from '@/settings/encryptionSetting' + +export type Options = Partial + +const createOptions = (storage: Storage, options: Options = {}): Options => { + return { + // No encryption in debug mode + hasEncrypt: enableStorageEncryption, + storage, + prefixKey: getStorageShortName(), + ...options + } +} + +export const WebStorage = create(createOptions(sessionStorage)) + +export const createStorage = (storage: Storage = sessionStorage, options: Options = {}) => { + return create(createOptions(storage, options)) +} + +export const createSessionStorage = (options: Options = {}) => { + return createStorage(sessionStorage, { ...options, timeout: DEFAULT_CACHE_TIME }) +} + +export const createLocalStorage = (options: Options = {}) => { + return createStorage(localStorage, { ...options, timeout: DEFAULT_CACHE_TIME }) +} + +export default WebStorage diff --git a/src/utils/cache/memory.ts b/src/utils/cache/memory.ts new file mode 100644 index 00000000..bfb0a331 --- /dev/null +++ b/src/utils/cache/memory.ts @@ -0,0 +1,107 @@ +export interface Cache { + value?: V + timeoutId?: ReturnType + time?: number + alive?: number +} + +const NOT_ALIVE = 0 + +export class Memory { + private cache: { [key in keyof T]?: Cache } = {} + private alive: number + + constructor(alive = NOT_ALIVE) { + // Unit second + this.alive = alive * 1000 + } + + get getCache() { + return this.cache + } + + setCache(cache) { + this.cache = cache + } + + // get(key: K) { + // const item = this.getItem(key); + // const time = item?.time; + // if (!isNullOrUnDef(time) && time < new Date().getTime()) { + // this.remove(key); + // } + // return item?.value ?? undefined; + // } + + get(key: K) { + return this.cache[key] + } + + set(key: K, value: V, expires?: number) { + let item = this.get(key) + + if (!expires || (expires as number) <= 0) { + expires = this.alive + } + if (item) { + if (item.timeoutId) { + clearTimeout(item.timeoutId) + item.timeoutId = undefined + } + item.value = value + } else { + item = { value, alive: expires } + this.cache[key] = item + } + + if (!expires) { + return value + } + const now = new Date().getTime() + /** + * Prevent overflow of the setTimeout Maximum delay value + * Maximum delay value 2,147,483,647 ms + * https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value + */ + item.time = expires > now ? expires : now + expires + item.timeoutId = setTimeout( + () => { + this.remove(key) + }, + expires > now ? expires - now : expires + ) + + return value + } + + remove(key: K) { + const item = this.get(key) + Reflect.deleteProperty(this.cache, key) + if (item) { + clearTimeout(item.timeoutId!) + return item.value + } + } + + resetCache(cache: { [K in keyof T]: Cache }) { + Object.keys(cache).forEach((key) => { + const k = key as any as keyof T + const item = cache[k] + if (item && item.time) { + const now = new Date().getTime() + const expire = item.time + if (expire > now) { + this.set(k, item.value, expire) + } + } + }) + } + + clear() { + Object.keys(this.cache).forEach((key) => { + const item = this.cache[key] + item.timeoutId && clearTimeout(item.timeoutId) + }) + this.cache = {} + } +} diff --git a/src/utils/cache/persistent.ts b/src/utils/cache/persistent.ts new file mode 100644 index 00000000..90ccba31 --- /dev/null +++ b/src/utils/cache/persistent.ts @@ -0,0 +1,132 @@ +import type { LockInfo, UserInfo } from '@/types/store' +import type { ProjectConfig } from '@/types/config' +import type { RouteLocationNormalized } from 'vue-router' + +import { createLocalStorage, createSessionStorage } from '@/utils/cache' +import { Memory } from './memory' +import { + TOKEN_KEY, + USER_INFO_KEY, + ROLES_KEY, + LOCK_INFO_KEY, + PROJ_CFG_KEY, + APP_LOCAL_CACHE_KEY, + APP_SESSION_CACHE_KEY, + MULTIPLE_TABS_KEY +} from '@/enums/cacheEnum' +import { DEFAULT_CACHE_TIME } from '@/settings/encryptionSetting' +import { toRaw } from 'vue' +import { pick, omit } from 'lodash-es' + +interface BasicStore { + [TOKEN_KEY]: string | number | null | undefined + [USER_INFO_KEY]: UserInfo + [ROLES_KEY]: string[] + [LOCK_INFO_KEY]: LockInfo + [PROJ_CFG_KEY]: ProjectConfig + [MULTIPLE_TABS_KEY]: RouteLocationNormalized[] +} + +type LocalStore = BasicStore + +type SessionStore = BasicStore + +export type BasicKeys = keyof BasicStore +type LocalKeys = keyof LocalStore +type SessionKeys = keyof SessionStore + +const ls = createLocalStorage() +const ss = createSessionStorage() + +const localMemory = new Memory(DEFAULT_CACHE_TIME) +const sessionMemory = new Memory(DEFAULT_CACHE_TIME) + +function initPersistentMemory() { + const localCache = ls.get(APP_LOCAL_CACHE_KEY) + const sessionCache = ss.get(APP_SESSION_CACHE_KEY) + localCache && localMemory.resetCache(localCache) + sessionCache && sessionMemory.resetCache(sessionCache) +} + +export class Persistent { + static getLocal(key: LocalKeys) { + return localMemory.get(key)?.value as Nullable + } + + static setLocal(key: LocalKeys, value: LocalStore[LocalKeys], immediate = false): void { + localMemory.set(key, toRaw(value)) + immediate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache) + } + + static removeLocal(key: LocalKeys, immediate = false): void { + localMemory.remove(key) + immediate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache) + } + + static clearLocal(immediate = false): void { + localMemory.clear() + immediate && ls.clear() + } + + static getSession(key: SessionKeys) { + return sessionMemory.get(key)?.value as Nullable + } + + static setSession(key: SessionKeys, value: SessionStore[SessionKeys], immediate = false): void { + sessionMemory.set(key, toRaw(value)) + immediate && ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache) + } + + static removeSession(key: SessionKeys, immediate = false): void { + sessionMemory.remove(key) + immediate && ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache) + } + static clearSession(immediate = false): void { + sessionMemory.clear() + immediate && ss.clear() + } + + static clearAll(immediate = false) { + sessionMemory.clear() + localMemory.clear() + if (immediate) { + ls.clear() + ss.clear() + } + } +} + +window.addEventListener('beforeunload', function () { + // TOKEN_KEY 在登录或注销时已经写入到storage了,此处为了解决同时打开多个窗口时token不同步的问题 + // LOCK_INFO_KEY 在锁屏和解锁时写入,此处也不应修改 + ls.set(APP_LOCAL_CACHE_KEY, { + ...omit(localMemory.getCache, LOCK_INFO_KEY), + ...pick(ls.get(APP_LOCAL_CACHE_KEY), [TOKEN_KEY, USER_INFO_KEY, LOCK_INFO_KEY]) + }) + ss.set(APP_SESSION_CACHE_KEY, { + ...omit(sessionMemory.getCache, LOCK_INFO_KEY), + ...pick(ss.get(APP_SESSION_CACHE_KEY), [TOKEN_KEY, USER_INFO_KEY, LOCK_INFO_KEY]) + }) +}) + +function storageChange(e: any) { + const { key, newValue, oldValue } = e + + if (!key) { + Persistent.clearAll() + return + } + + if (!!newValue && !!oldValue) { + if (APP_LOCAL_CACHE_KEY === key) { + Persistent.clearLocal() + } + if (APP_SESSION_CACHE_KEY === key) { + Persistent.clearSession() + } + } +} + +window.addEventListener('storage', storageChange) + +initPersistentMemory() diff --git a/src/utils/cache/storageCache.ts b/src/utils/cache/storageCache.ts new file mode 100644 index 00000000..f973d046 --- /dev/null +++ b/src/utils/cache/storageCache.ts @@ -0,0 +1,109 @@ +import { cacheCipher } from '@/settings/encryptionSetting' +import type { EncryptionParams } from '@/utils/cipher' +import { AesEncryption } from '@/utils/cipher' +import { isNullOrUnDef } from '@/utils/is' + +export interface CreateStorageParams extends EncryptionParams { + prefixKey: string + storage: Storage + hasEncrypt: boolean + timeout?: Nullable +} +export const createStorage = ({ + prefixKey = '', + storage = sessionStorage, + key = cacheCipher.key, + iv = cacheCipher.iv, + timeout = null, + hasEncrypt = true +}: Partial = {}) => { + if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) { + throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!') + } + + const encryption = new AesEncryption({ key, iv }) + + /** + * Cache class + * Construction parameters can be passed into sessionStorage, localStorage, + * @class Cache + * @example + */ + const WebStorage = class WebStorage { + private storage: Storage + private prefixKey?: string + private encryption: AesEncryption + private hasEncrypt: boolean + /** + * + * @param {*} storage + */ + constructor() { + this.storage = storage + this.prefixKey = prefixKey + this.encryption = encryption + this.hasEncrypt = hasEncrypt + } + + private getKey(key: string) { + return `${this.prefixKey}${key}`.toUpperCase() + } + + /** + * Set cache + * @param {string} key + * @param {*} value + * @param {*} expire Expiration time in seconds + * @memberof Cache + */ + set(key: string, value: any, expire: number | null = timeout) { + const stringData = JSON.stringify({ + value, + time: Date.now(), + expire: !isNullOrUnDef(expire) ? new Date().getTime() + expire * 1000 : null + }) + const stringifyValue = this.hasEncrypt ? this.encryption.encryptByAES(stringData) : stringData + this.storage.setItem(this.getKey(key), stringifyValue) + } + + /** + * Read cache + * @param {string} key + * @param {*} def + * @memberof Cache + */ + get(key: string, def: any = null): any { + const val = this.storage.getItem(this.getKey(key)) + if (!val) return def + + try { + const decVal = this.hasEncrypt ? this.encryption.decryptByAES(val) : val + const data = JSON.parse(decVal) + const { value, expire } = data + if (isNullOrUnDef(expire) || expire >= new Date().getTime()) { + return value + } + this.remove(key) + } catch (e) { + return def + } + } + + /** + * Delete cache based on key + * @param {string} key + * @memberof Cache + */ + remove(key: string) { + this.storage.removeItem(this.getKey(key)) + } + + /** + * Delete all caches of this instance + */ + clear(): void { + this.storage.clear() + } + } + return new WebStorage() +} diff --git a/src/utils/cipher.ts b/src/utils/cipher.ts new file mode 100644 index 00000000..315a09b6 --- /dev/null +++ b/src/utils/cipher.ts @@ -0,0 +1,55 @@ +import { encrypt, decrypt } from 'crypto-js/aes' +import { parse } from 'crypto-js/enc-utf8' +import pkcs7 from 'crypto-js/pad-pkcs7' +import ECB from 'crypto-js/mode-ecb' +import md5 from 'crypto-js/md5' +import UTF8 from 'crypto-js/enc-utf8' +import Base64 from 'crypto-js/enc-base64' + +export interface EncryptionParams { + key: string + iv: string +} + +export class AesEncryption { + private key + private iv + + constructor(opt: Partial = {}) { + const { key, iv } = opt + if (key) { + this.key = parse(key) + } + if (iv) { + this.iv = parse(iv) + } + } + + get getOptions() { + return { + mode: ECB, + padding: pkcs7, + iv: this.iv + } + } + + encryptByAES(cipherText: string) { + return encrypt(cipherText, this.key, this.getOptions).toString() + } + + decryptByAES(cipherText: string) { + return decrypt(cipherText, this.key, this.getOptions).toString(UTF8) + } +} + +export function encryptByBase64(cipherText: string) { + return UTF8.parse(cipherText).toString(Base64) +} + +export function decodeByBase64(cipherText: string) { + return Base64.parse(cipherText).toString(UTF8) +} + +export function encryptByMd5(password: string) { + return md5(password).toString() +} diff --git a/src/utils/color.ts b/src/utils/color.ts new file mode 100644 index 00000000..7b763be3 --- /dev/null +++ b/src/utils/color.ts @@ -0,0 +1,145 @@ +/** + * 判断是否 十六进制颜色值. + * 输入形式可为 #fff000 #f00 + * + * @param String color 十六进制颜色值 + * @return Boolean + */ +export function isHexColor(color: string) { + const reg = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/ + return reg.test(color) +} + +/** + * RGB 颜色值转换为 十六进制颜色值. + * r, g, 和 b 需要在 [0, 255] 范围内 + * + * @return String 类似#ff00ff + * @param r + * @param g + * @param b + */ +export function rgbToHex(r: number, g: number, b: number) { + // tslint:disable-next-line:no-bitwise + const hex = ((r << 16) | (g << 8) | b).toString(16) + return '#' + new Array(Math.abs(hex.length - 7)).join('0') + hex +} + +/** + * Transform a HEX color to its RGB representation + * @param {string} hex The color to transform + * @returns The RGB representation of the passed color + */ +export function hexToRGB(hex: string) { + let sHex = hex.toLowerCase() + if (isHexColor(hex)) { + if (sHex.length === 4) { + let sColorNew = '#' + for (let i = 1; i < 4; i += 1) { + sColorNew += sHex.slice(i, i + 1).concat(sHex.slice(i, i + 1)) + } + sHex = sColorNew + } + const sColorChange: number[] = [] + for (let i = 1; i < 7; i += 2) { + sColorChange.push(parseInt('0x' + sHex.slice(i, i + 2))) + } + return 'RGB(' + sColorChange.join(',') + ')' + } + return sHex +} + +export function colorIsDark(color: string) { + if (!isHexColor(color)) return + const [r, g, b] = hexToRGB(color) + .replace(/(?:\(|\)|rgb|RGB)*/g, '') + .split(',') + .map((item) => Number(item)) + return r * 0.299 + g * 0.578 + b * 0.114 < 192 +} + +/** + * Darkens a HEX color given the passed percentage + * @param {string} color The color to process + * @param {number} amount The amount to change the color by + * @returns {string} The HEX representation of the processed color + */ +export function darken(color: string, amount: number) { + color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color + amount = Math.trunc((255 * amount) / 100) + return `#${subtractLight(color.substring(0, 2), amount)}${subtractLight(color.substring(2, 4), amount)}${subtractLight( + color.substring(4, 6), + amount + )}` +} + +/** + * Lightens a 6 char HEX color according to the passed percentage + * @param {string} color The color to change + * @param {number} amount The amount to change the color by + * @returns {string} The processed color represented as HEX + */ +export function lighten(color: string, amount: number) { + color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color + amount = Math.trunc((255 * amount) / 100) + return `#${addLight(color.substring(0, 2), amount)}${addLight(color.substring(2, 4), amount)}${addLight(color.substring(4, 6), amount)}` +} + +/* Suma el porcentaje indicado a un color (RR, GG o BB) hexadecimal para aclararlo */ +/** + * Sums the passed percentage to the R, G or B of a HEX color + * @param {string} color The color to change + * @param {number} amount The amount to change the color by + * @returns {string} The processed part of the color + */ +function addLight(color: string, amount: number) { + const cc = parseInt(color, 16) + amount + const c = cc > 255 ? 255 : cc + return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}` +} + +/** + * Calculates luminance of an rgb color + * @param {number} r red + * @param {number} g green + * @param {number} b blue + */ +function luminanace(r: number, g: number, b: number) { + const a = [r, g, b].map((v) => { + v /= 255 + return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4) + }) + return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722 +} + +/** + * Calculates contrast between two rgb colors + * @param {string} rgb1 rgb color 1 + * @param {string} rgb2 rgb color 2 + */ +function contrast(rgb1: string[], rgb2: number[]) { + return (luminanace(~~rgb1[0], ~~rgb1[1], ~~rgb1[2]) + 0.05) / (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05) +} + +/** + * Determines what the best text color is (black or white) based con the contrast with the background + * @param hexColor - Last selected color by the user + */ +export function calculateBestTextColor(hexColor: string) { + const rgbColor = hexToRGB(hexColor.substring(1)) + const contrastWithBlack = contrast(rgbColor.split(','), [0, 0, 0]) + + return contrastWithBlack >= 12 ? '#000000' : '#FFFFFF' +} + +/** + * Subtracts the indicated percentage to the R, G or B of a HEX color + * @param {string} color The color to change + * @param {number} amount The amount to change the color by + * @returns {string} The processed part of the color + */ +function subtractLight(color: string, amount: number) { + const cc = parseInt(color, 16) - amount + const c = cc < 0 ? 0 : cc + return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}` +} diff --git a/src/utils/dateUtil.ts b/src/utils/dateUtil.ts new file mode 100644 index 00000000..36b87e34 --- /dev/null +++ b/src/utils/dateUtil.ts @@ -0,0 +1,17 @@ +/** + * Independent time operation tool to facilitate subsequent switch to dayjs + */ +import dayjs from 'dayjs' + +const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss' +const DATE_FORMAT = 'YYYY-MM-DD' + +export function formatToDateTime(date?: dayjs.ConfigType, format = DATE_TIME_FORMAT): string { + return dayjs(date).format(format) +} + +export function formatToDate(date?: dayjs.ConfigType, format = DATE_FORMAT): string { + return dayjs(date).format(format) +} + +export const dateUtil = dayjs diff --git a/src/utils/domUtils.ts b/src/utils/domUtils.ts new file mode 100644 index 00000000..d91184f0 --- /dev/null +++ b/src/utils/domUtils.ts @@ -0,0 +1,172 @@ +import type { FunctionArgs } from '@vueuse/core' +import { upperFirst } from 'lodash-es' + +export interface ViewportOffsetResult { + left: number + top: number + right: number + bottom: number + rightIncludeBody: number + bottomIncludeBody: number +} + +export function getBoundingClientRect(element: Element): DOMRect | number { + if (!element || !element.getBoundingClientRect) { + return 0 + } + return element.getBoundingClientRect() +} + +function trim(string: string) { + return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '') +} + +/* istanbul ignore next */ +export function hasClass(el: Element, cls: string) { + if (!el || !cls) return false + if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.') + if (el.classList) { + return el.classList.contains(cls) + } else { + return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1 + } +} + +/* istanbul ignore next */ +export function addClass(el: Element, cls: string) { + if (!el) return + let curClass = el.className + const classes = (cls || '').split(' ') + + for (let i = 0, j = classes.length; i < j; i++) { + const clsName = classes[i] + if (!clsName) continue + + if (el.classList) { + el.classList.add(clsName) + } else if (!hasClass(el, clsName)) { + curClass += ' ' + clsName + } + } + if (!el.classList) { + el.className = curClass + } +} + +/* istanbul ignore next */ +export function removeClass(el: Element, cls: string) { + if (!el || !cls) return + const classes = cls.split(' ') + let curClass = ' ' + el.className + ' ' + + for (let i = 0, j = classes.length; i < j; i++) { + const clsName = classes[i] + if (!clsName) continue + + if (el.classList) { + el.classList.remove(clsName) + } else if (hasClass(el, clsName)) { + curClass = curClass.replace(' ' + clsName + ' ', ' ') + } + } + if (!el.classList) { + el.className = trim(curClass) + } +} +/** + * Get the left and top offset of the current element + * left: the distance between the leftmost element and the left side of the document + * top: the distance from the top of the element to the top of the document + * right: the distance from the far right of the element to the right of the document + * bottom: the distance from the bottom of the element to the bottom of the document + * rightIncludeBody: the distance between the leftmost element and the right side of the document + * bottomIncludeBody: the distance from the bottom of the element to the bottom of the document + * + * @description: + */ +export function getViewportOffset(element: Element): ViewportOffsetResult { + const doc = document.documentElement + + const docScrollLeft = doc.scrollLeft + const docScrollTop = doc.scrollTop + const docClientLeft = doc.clientLeft + const docClientTop = doc.clientTop + + const pageXOffset = window.pageXOffset + const pageYOffset = window.pageYOffset + + const box = getBoundingClientRect(element) + + const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect + + const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0) + const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0) + const offsetLeft = retLeft + pageXOffset + const offsetTop = rectTop + pageYOffset + + const left = offsetLeft - scrollLeft + const top = offsetTop - scrollTop + + const clientWidth = window.document.documentElement.clientWidth + const clientHeight = window.document.documentElement.clientHeight + return { + left: left, + top: top, + right: clientWidth - rectWidth - left, + bottom: clientHeight - rectHeight - top, + rightIncludeBody: clientWidth - left, + bottomIncludeBody: clientHeight - top + } +} + +export function hackCss(attr: string, value: string) { + const prefix: string[] = ['webkit', 'Moz', 'ms', 'OT'] + + const styleObj: any = {} + prefix.forEach((item) => { + styleObj[`${item}${upperFirst(attr)}`] = value + }) + return { + ...styleObj, + [attr]: value + } +} + +/* istanbul ignore next */ +export function on(element: Element | HTMLElement | Document | Window, event: string, handler: EventListenerOrEventListenerObject): void { + if (element && event && handler) { + element.addEventListener(event, handler, false) + } +} + +/* istanbul ignore next */ +export function off(element: Element | HTMLElement | Document | Window, event: string, handler: Fn): void { + if (element && event && handler) { + element.removeEventListener(event, handler, false) + } +} + +/* istanbul ignore next */ +export function once(el: HTMLElement, event: string, fn: EventListener): void { + const listener = function (this: any, ...args: unknown[]) { + if (fn) { + fn.apply(this, args) + } + off(el, event, listener) + } + on(el, event, listener) +} + +export function useRafThrottle(fn: T): T { + let locked = false + // @ts-ignore + return function (...args: any[]) { + if (locked) return + locked = true + window.requestAnimationFrame(() => { + // @ts-ignore + fn.apply(this, args) + locked = false + }) + } +} diff --git a/src/utils/env.ts b/src/utils/env.ts new file mode 100644 index 00000000..e74f69c1 --- /dev/null +++ b/src/utils/env.ts @@ -0,0 +1,77 @@ +import type { GlobEnvConfig } from '@/types/config' + +import { warn } from '@/utils/log' +import pkg from '../../package.json' +import { getConfigFileName } from '../../build/getConfigFileName' + +export function getCommonStoragePrefix() { + const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig() + return `${VITE_GLOB_APP_SHORT_NAME}__${getEnv()}`.toUpperCase() +} + +// Generate cache key according to version +export function getStorageShortName() { + return `${getCommonStoragePrefix()}${`__${pkg.version}`}__`.toUpperCase() +} + +export function getAppEnvConfig() { + const ENV_NAME = getConfigFileName(import.meta.env) + + const ENV = (import.meta.env.DEV + ? // Get the global configuration (the configuration will be extracted independently when packaging) + (import.meta.env as unknown as GlobEnvConfig) + : window[ENV_NAME as any]) as unknown as GlobEnvConfig + + const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL, VITE_GLOB_APP_SHORT_NAME, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL } = ENV + + if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) { + warn( + `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.` + ) + } + + return { + VITE_GLOB_APP_TITLE, + VITE_GLOB_API_URL, + VITE_GLOB_APP_SHORT_NAME, + VITE_GLOB_API_URL_PREFIX, + VITE_GLOB_UPLOAD_URL + } +} + +/** + * @description: Development mode + */ +export const devMode = 'development' + +/** + * @description: Production mode + */ +export const prodMode = 'production' + +/** + * @description: Get environment variables + * @returns: + * @example: + */ +export function getEnv(): string { + return import.meta.env.MODE +} + +/** + * @description: Is it a development mode + * @returns: + * @example: + */ +export function isDevMode(): boolean { + return import.meta.env.DEV +} + +/** + * @description: Is it a production mode + * @returns: + * @example: + */ +export function isProdMode(): boolean { + return import.meta.env.PROD +} diff --git a/src/utils/event/index.ts b/src/utils/event/index.ts new file mode 100644 index 00000000..3b56534c --- /dev/null +++ b/src/utils/event/index.ts @@ -0,0 +1,42 @@ +import ResizeObserver from 'resize-observer-polyfill' + +const isServer = typeof window === 'undefined' + +/* istanbul ignore next */ +function resizeHandler(entries: any[]) { + for (const entry of entries) { + const listeners = entry.target.__resizeListeners__ || [] + if (listeners.length) { + listeners.forEach((fn: () => any) => { + fn() + }) + } + } +} + +/* istanbul ignore next */ +export function addResizeListener(element: any, fn: () => any) { + if (isServer) return + if (!element.__resizeListeners__) { + element.__resizeListeners__ = [] + element.__ro__ = new ResizeObserver(resizeHandler) + element.__ro__.observe(element) + } + element.__resizeListeners__.push(fn) +} + +/* istanbul ignore next */ +export function removeResizeListener(element: any, fn: () => any) { + if (!element || !element.__resizeListeners__) return + element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1) + if (!element.__resizeListeners__.length) { + element.__ro__.disconnect() + } +} + +export function triggerWindowResize() { + const event = document.createEvent('HTMLEvents') + event.initEvent('resize', true, true) + ;(event as any).eventType = 'message' + window.dispatchEvent(event) +} diff --git a/src/utils/factory/createAsyncComponent.tsx b/src/utils/factory/createAsyncComponent.tsx new file mode 100644 index 00000000..ca772900 --- /dev/null +++ b/src/utils/factory/createAsyncComponent.tsx @@ -0,0 +1,63 @@ +import { + defineAsyncComponent + // FunctionalComponent, CSSProperties +} from 'vue' +import { Spin } from 'ant-design-vue' +import { noop } from '@/utils' + +// const Loading: FunctionalComponent<{ size: 'small' | 'default' | 'large' }> = (props) => { +// const style: CSSProperties = { +// position: 'absolute', +// display: 'flex', +// justifyContent: 'center', +// alignItems: 'center', +// }; +// return ( +//
+// +//
+// ); +// }; + +interface Options { + size?: 'default' | 'small' | 'large' + delay?: number + timeout?: number + loading?: boolean + retry?: boolean +} + +export function createAsyncComponent(loader: Fn, options: Options = {}) { + const { size = 'small', delay = 100, timeout = 30000, loading = false, retry = true } = options + return defineAsyncComponent({ + loader, + loadingComponent: loading ? : undefined, + // The error component will be displayed if a timeout is + // provided and exceeded. Default: Infinity. + // TODO + timeout, + // errorComponent + // Defining if component is suspensible. Default: true. + // suspensible: false, + delay, + /** + * + * @param {*} error Error message object + * @param {*} retry A function that indicating whether the async component should retry when the loader promise rejects + * @param {*} fail End of failure + * @param {*} attempts Maximum allowed retries number + */ + onError: !retry + ? noop + : (error, retry, fail, attempts) => { + if (error.message.match(/fetch/) && attempts <= 3) { + // retry on fetch errors, 3 max attempts + retry() + } else { + // Note that retry/fail are like resolve/reject of a promise: + // one of them must be called for the error handling to continue. + fail() + } + } + }) +} diff --git a/src/utils/file/base64Conver.ts b/src/utils/file/base64Conver.ts new file mode 100644 index 00000000..b94225de --- /dev/null +++ b/src/utils/file/base64Conver.ts @@ -0,0 +1,41 @@ +/** + * @description: base64 to blob + */ +export function dataURLtoBlob(base64Buf: string): Blob { + const arr = base64Buf.split(',') + const typeItem = arr[0] + const mime = typeItem.match(/:(.*?);/)![1] + const bstr = window.atob(arr[1]) + let n = bstr.length + const u8arr = new Uint8Array(n) + while (n--) { + u8arr[n] = bstr.charCodeAt(n) + } + return new Blob([u8arr], { type: mime }) +} + +/** + * img url to base64 + * @param url + */ +export function urlToBase64(url: string, mineType?: string): Promise { + return new Promise((resolve, reject) => { + let canvas = document.createElement('CANVAS') as Nullable + const ctx = canvas!.getContext('2d') + + const img = new Image() + img.crossOrigin = '' + img.onload = function () { + if (!canvas || !ctx) { + return reject() + } + canvas.height = img.height + canvas.width = img.width + ctx.drawImage(img, 0, 0) + const dataURL = canvas.toDataURL(mineType || 'image/png') + canvas = null + resolve(dataURL) + } + img.src = url + }) +} diff --git a/src/utils/file/download.ts b/src/utils/file/download.ts new file mode 100644 index 00000000..e328363c --- /dev/null +++ b/src/utils/file/download.ts @@ -0,0 +1,88 @@ +import { openWindow } from '..' +import { dataURLtoBlob, urlToBase64 } from './base64Conver' + +/** + * Download online pictures + * @param url + * @param filename + * @param mime + * @param bom + */ +export function downloadByOnlineUrl(url: string, filename: string, mime?: string, bom?: BlobPart) { + urlToBase64(url).then((base64) => { + downloadByBase64(base64, filename, mime, bom) + }) +} + +/** + * Download pictures based on base64 + * @param buf + * @param filename + * @param mime + * @param bom + */ +export function downloadByBase64(buf: string, filename: string, mime?: string, bom?: BlobPart) { + const base64Buf = dataURLtoBlob(buf) + downloadByData(base64Buf, filename, mime, bom) +} + +/** + * Download according to the background interface file stream + * @param {*} data + * @param {*} filename + * @param {*} mime + * @param {*} bom + */ +export function downloadByData(data: BlobPart, filename: string, mime?: string, bom?: BlobPart) { + const blobData = typeof bom !== 'undefined' ? [bom, data] : [data] + const blob = new Blob(blobData, { type: mime || 'application/octet-stream' }) + + const blobURL = window.URL.createObjectURL(blob) + const tempLink = document.createElement('a') + tempLink.style.display = 'none' + tempLink.href = blobURL + tempLink.setAttribute('download', filename) + if (typeof tempLink.download === 'undefined') { + tempLink.setAttribute('target', '_blank') + } + document.body.appendChild(tempLink) + tempLink.click() + document.body.removeChild(tempLink) + window.URL.revokeObjectURL(blobURL) +} + +/** + * Download file according to file address + * @param {*} sUrl + */ +export function downloadByUrl({ url, target = '_blank', fileName }: { url: string; target?: TargetContext; fileName?: string }): boolean { + const isChrome = window.navigator.userAgent.toLowerCase().indexOf('chrome') > -1 + const isSafari = window.navigator.userAgent.toLowerCase().indexOf('safari') > -1 + + if (/(iP)/g.test(window.navigator.userAgent)) { + console.error('Your browser does not support download!') + return false + } + if (isChrome || isSafari) { + const link = document.createElement('a') + link.href = url + link.target = target + + if (link.download !== undefined) { + link.download = fileName || url.substring(url.lastIndexOf('/') + 1, url.length) + } + + if (document.createEvent) { + const e = document.createEvent('MouseEvents') + e.initEvent('click', true, true) + link.dispatchEvent(e) + return true + } + } + if (url.indexOf('?') === -1) { + url += '?download' + } + + openWindow(url, { target }) + return true +} diff --git a/src/utils/helper/treeHelper.ts b/src/utils/helper/treeHelper.ts new file mode 100644 index 00000000..b99f8e2d --- /dev/null +++ b/src/utils/helper/treeHelper.ts @@ -0,0 +1,197 @@ +interface TreeHelperConfig { + id: string + children: string + pid: string +} + +// 默认配置 +const DEFAULT_CONFIG: TreeHelperConfig = { + id: 'id', + children: 'children', + pid: 'pid' +} + +// 获取配置。 Object.assign 从一个或多个源对象复制到目标对象 +const getConfig = (config: Partial) => Object.assign({}, DEFAULT_CONFIG, config) + +// tree from list +// 列表中的树 +export function listToTree(list: any[], config: Partial = {}): T[] { + const conf = getConfig(config) as TreeHelperConfig + const nodeMap = new Map() + const result: T[] = [] + const { id, children, pid } = conf + + for (const node of list) { + node[children] = node[children] || [] + nodeMap.set(node[id], node) + } + for (const node of list) { + const parent = nodeMap.get(node[pid]) + ;(parent ? parent[children] : result).push(node) + } + return result +} + +export function treeToList(tree: any, config: Partial = {}): T { + config = getConfig(config) + const { children } = config + const result: any = [...tree] + for (let i = 0; i < result.length; i++) { + if (!result[i][children!]) continue + result.splice(i + 1, 0, ...result[i][children!]) + } + return result +} + +export function findNode(tree: any, func: Fn, config: Partial = {}): T | null { + config = getConfig(config) + const { children } = config + const list = [...tree] + for (const node of list) { + if (func(node)) return node + node[children!] && list.push(...node[children!]) + } + return null +} + +export function findNodeAll(tree: any, func: Fn, config: Partial = {}): T[] { + config = getConfig(config) + const { children } = config + const list = [...tree] + const result: T[] = [] + for (const node of list) { + func(node) && result.push(node) + node[children!] && list.push(...node[children!]) + } + return result +} + +export function findPath(tree: any, func: Fn, config: Partial = {}): T | T[] | null { + config = getConfig(config) + const path: T[] = [] + const list = [...tree] + const visitedSet = new Set() + const { children } = config + while (list.length) { + const node = list[0] + if (visitedSet.has(node)) { + path.pop() + list.shift() + } else { + visitedSet.add(node) + node[children!] && list.unshift(...node[children!]) + path.push(node) + if (func(node)) { + return path + } + } + } + return null +} + +export function findPathAll(tree: any, func: Fn, config: Partial = {}) { + config = getConfig(config) + const path: any[] = [] + const list = [...tree] + const result: any[] = [] + const visitedSet = new Set(), + { children } = config + while (list.length) { + const node = list[0] + if (visitedSet.has(node)) { + path.pop() + list.shift() + } else { + visitedSet.add(node) + node[children!] && list.unshift(...node[children!]) + path.push(node) + func(node) && result.push([...path]) + } + } + return result +} + +export function filter( + tree: T[], + func: (n: T) => boolean, + // Partial 将 T 中的所有属性设为可选 + config: Partial = {} +): T[] { + // 获取配置 + config = getConfig(config) + const children = config.children as string + + function listFilter(list: T[]) { + return list + .map((node: any) => ({ ...node })) + .filter((node) => { + // 递归调用 对含有children项 进行再次调用自身函数 listFilter + node[children] = node[children] && listFilter(node[children]) + // 执行传入的回调 func 进行过滤 + return func(node) || (node[children] && node[children].length) + }) + } + + return listFilter(tree) +} + +export function forEach(tree: T[], func: (n: T) => any, config: Partial = {}): void { + config = getConfig(config) + const list: any[] = [...tree] + const { children } = config + for (let i = 0; i < list.length; i++) { + //func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿 + if (func(list[i])) { + return + } + children && list[i][children] && list.splice(i + 1, 0, ...list[i][children]) + } +} + +/** + * @description: Extract tree specified structure + * @description: 提取树指定结构 + */ +export function treeMap(treeData: T[], opt: { children?: string; conversion: Fn }): T[] { + return treeData.map((item) => treeMapEach(item, opt)) +} + +/** + * @description: Extract tree specified structure + * @description: 提取树指定结构 + */ +export function treeMapEach(data: any, { children = 'children', conversion }: { children?: string; conversion: Fn }) { + const haveChildren = Array.isArray(data[children]) && data[children].length > 0 + const conversionData = conversion(data) || {} + if (haveChildren) { + return { + ...conversionData, + [children]: data[children].map((i: number) => + treeMapEach(i, { + children, + conversion + }) + ) + } + } else { + return { + ...conversionData + } + } +} + +/** + * 递归遍历树结构 + * @param treeDatas 树 + * @param callBack 回调 + * @param parentNode 父节点 + */ +export function eachTree(treeDatas: any[], callBack: Fn, parentNode = {}) { + treeDatas.forEach((element) => { + const newNode = callBack(element, parentNode) || element + if (element.children) { + eachTree(element.children, callBack, newNode) + } + }) +} diff --git a/src/utils/helper/tsxHelper.tsx b/src/utils/helper/tsxHelper.tsx new file mode 100644 index 00000000..e8e422d5 --- /dev/null +++ b/src/utils/helper/tsxHelper.tsx @@ -0,0 +1,35 @@ +import { Slots } from 'vue' +import { isFunction } from '@/utils/is' + +/** + * @description: Get slot to prevent empty error + */ +export function getSlot(slots: Slots, slot = 'default', data?: any) { + if (!slots || !Reflect.has(slots, slot)) { + return null + } + if (!isFunction(slots[slot])) { + console.error(`${slot} is not a function!`) + return null + } + const slotFn = slots[slot] + if (!slotFn) return null + return slotFn(data) +} + +/** + * extends slots + * @param slots + * @param excludeKeys + */ +export function extendSlots(slots: Slots, excludeKeys: string[] = []) { + const slotKeys = Object.keys(slots) + const ret: any = {} + slotKeys.map((key) => { + if (excludeKeys.includes(key)) { + return null + } + ret[key] = (data?: any) => getSlot(slots, key, data) + }) + return ret +} diff --git a/src/utils/http/axios/Axios.ts b/src/utils/http/axios/Axios.ts new file mode 100644 index 00000000..041c110d --- /dev/null +++ b/src/utils/http/axios/Axios.ts @@ -0,0 +1,232 @@ +import type { AxiosInstance, AxiosResponse, AxiosError, InternalAxiosRequestConfig, AxiosRequestConfig } from 'axios' +import type { RequestOptions, Result, UploadFileParams } from '@/types/axios' +import type { CreateAxiosOptions } from './axiosTransform' +import axios from 'axios' +import qs from 'qs' +import { AxiosCanceler } from './axiosCancel' +import { isFunction } from '@/utils/is' +import { cloneDeep } from 'lodash-es' +import { ContentTypeEnum } from '@/enums/httpEnum' +import { RequestEnum } from '@/enums/httpEnum' + +export * from './axiosTransform' + +/** + * @description: axios module + */ +export class VAxios { + private axiosInstance: AxiosInstance + private readonly options: CreateAxiosOptions + + constructor(options: CreateAxiosOptions) { + this.options = options + this.axiosInstance = axios.create(options) + this.setupInterceptors() + } + + /** + * @description: Create axios instance + */ + private createAxios(config: CreateAxiosOptions): void { + this.axiosInstance = axios.create(config) + } + + private getTransform() { + const { transform } = this.options + return transform + } + + getAxios(): AxiosInstance { + return this.axiosInstance + } + + /** + * @description: Reconfigure axios + */ + configAxios(config: CreateAxiosOptions) { + if (!this.axiosInstance) { + return + } + this.createAxios(config) + } + + /** + * @description: Set general header + */ + setHeader(headers: any): void { + if (!this.axiosInstance) { + return + } + Object.assign(this.axiosInstance.defaults.headers, headers) + } + + /** + * @description: Interceptor configuration 拦截器配置 + */ + private setupInterceptors() { + const transform = this.getTransform() + if (!transform) { + return + } + const { requestInterceptors, requestInterceptorsCatch, responseInterceptors, responseInterceptorsCatch } = transform + + const axiosCanceler = new AxiosCanceler() + + // Request interceptor configuration processing + this.axiosInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => { + // If cancel repeat request is turned on, then cancel repeat request is prohibited + // @ts-ignore + const { ignoreCancelToken } = config.requestOptions + const ignoreCancel = ignoreCancelToken !== undefined ? ignoreCancelToken : this.options.requestOptions?.ignoreCancelToken + + !ignoreCancel && axiosCanceler.addPending(config) + if (requestInterceptors && isFunction(requestInterceptors)) { + config = requestInterceptors(config, this.options) + } + return config + }, undefined) + + // Request interceptor error capture + requestInterceptorsCatch && + isFunction(requestInterceptorsCatch) && + this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch) + + // Response result interceptor processing + this.axiosInstance.interceptors.response.use((res: AxiosResponse) => { + res && axiosCanceler.removePending(res.config) + if (responseInterceptors && isFunction(responseInterceptors)) { + res = responseInterceptors(res) + } + return res + }, undefined) + + // Response result interceptor error capture + responseInterceptorsCatch && + isFunction(responseInterceptorsCatch) && + this.axiosInstance.interceptors.response.use(undefined, (error) => { + // @ts-ignore + return responseInterceptorsCatch(this.axiosInstance, error) + }) + } + + /** + * @description: File Upload + */ + uploadFile(config: AxiosRequestConfig, params: UploadFileParams) { + const formData = new window.FormData() + const customFilename = params.name || 'file' + + if (params.filename) { + formData.append(customFilename, params.file, params.filename) + } else { + formData.append(customFilename, params.file) + } + + if (params.data) { + Object.keys(params.data).forEach((key) => { + const value = params.data![key] + if (Array.isArray(value)) { + value.forEach((item) => { + formData.append(`${key}[]`, item) + }) + return + } + + formData.append(key, params.data![key]) + }) + } + + return this.axiosInstance.request({ + ...config, + method: 'POST', + data: formData, + headers: { + 'Content-type': ContentTypeEnum.FORM_DATA, + ignoreCancelToken: true + } + }) + } + + // support form-data + supportFormData(config: AxiosRequestConfig) { + const headers = config.headers || this.options.headers + const contentType = headers?.['Content-Type'] || headers?.['content-type'] + + if ( + contentType !== ContentTypeEnum.FORM_URLENCODED || + !Reflect.has(config, 'data') || + config.method?.toUpperCase() === RequestEnum.GET + ) { + return config + } + + return { + ...config, + data: qs.stringify(config.data, { arrayFormat: 'brackets' }) + } + } + + get(config: AxiosRequestConfig, options?: RequestOptions): Promise { + return this.request({ ...config, method: 'GET' }, options) + } + + post(config: AxiosRequestConfig, options?: RequestOptions): Promise { + return this.request({ ...config, method: 'POST' }, options) + } + + put(config: AxiosRequestConfig, options?: RequestOptions): Promise { + return this.request({ ...config, method: 'PUT' }, options) + } + + delete(config: AxiosRequestConfig, options?: RequestOptions): Promise { + return this.request({ ...config, method: 'DELETE' }, options) + } + + request(config: AxiosRequestConfig, options?: RequestOptions): Promise { + let conf: CreateAxiosOptions = cloneDeep(config) + // cancelToken 如果被深拷贝,会导致最外层无法使用cancel方法来取消请求 + if (config.cancelToken) { + conf.cancelToken = config.cancelToken + } + const transform = this.getTransform() + + const { requestOptions } = this.options + + const opt: RequestOptions = Object.assign({}, requestOptions, options) + + const { beforeRequestHook, requestCatchHook, transformResponseHook } = transform || {} + if (beforeRequestHook && isFunction(beforeRequestHook)) { + conf = beforeRequestHook(conf, opt) + } + conf.requestOptions = opt + + conf = this.supportFormData(conf) + + return new Promise((resolve, reject) => { + this.axiosInstance + .request>(conf) + .then((res: AxiosResponse) => { + if (transformResponseHook && isFunction(transformResponseHook)) { + try { + const ret = transformResponseHook(res, opt) + resolve(ret) + } catch (err) { + reject(err || new Error('request error!')) + } + return + } + resolve(res as unknown as Promise) + }) + .catch((e: Error | AxiosError) => { + if (requestCatchHook && isFunction(requestCatchHook)) { + reject(requestCatchHook(e, opt)) + return + } + if (axios.isAxiosError(e)) { + // rewrite error message from axios in here + } + reject(e) + }) + }) + } +} diff --git a/src/utils/http/axios/axiosCancel.ts b/src/utils/http/axios/axiosCancel.ts new file mode 100644 index 00000000..e874af50 --- /dev/null +++ b/src/utils/http/axios/axiosCancel.ts @@ -0,0 +1,60 @@ +import type { InternalAxiosRequestConfig, Canceler } from 'axios' +import axios from 'axios' +import { isFunction } from '@/utils/is' + +// Used to store the identification and cancellation function of each request +let pendingMap = new Map() + +export const getPendingUrl = (config: InternalAxiosRequestConfig) => [config.method, config.url].join('&') + +export class AxiosCanceler { + /** + * Add request + * @param {Object} config + */ + addPending(config: InternalAxiosRequestConfig) { + this.removePending(config) + const url = getPendingUrl(config) + config.cancelToken = + config.cancelToken || + new axios.CancelToken((cancel) => { + if (!pendingMap.has(url)) { + // If there is no current request in pending, add it + pendingMap.set(url, cancel) + } + }) + } + + /** + * @description: Clear all pending + */ + removeAllPending() { + pendingMap.forEach((cancel) => { + cancel && isFunction(cancel) && cancel() + }) + pendingMap.clear() + } + + /** + * Removal request + * @param {Object} config + */ + removePending(config: InternalAxiosRequestConfig) { + const url = getPendingUrl(config) + + if (pendingMap.has(url)) { + // If there is a current request identifier in pending, + // the current request needs to be cancelled and removed + const cancel = pendingMap.get(url) + cancel && cancel(url) + pendingMap.delete(url) + } + } + + /** + * @description: reset + */ + reset(): void { + pendingMap = new Map() + } +} diff --git a/src/utils/http/axios/axiosRetry.ts b/src/utils/http/axios/axiosRetry.ts new file mode 100644 index 00000000..e30c1ec4 --- /dev/null +++ b/src/utils/http/axios/axiosRetry.ts @@ -0,0 +1,30 @@ +import { AxiosError, AxiosInstance } from 'axios' +/** + * 请求重试机制 + */ + +export class AxiosRetry { + /** + * 重试 + */ + retry(axiosInstance: AxiosInstance, error: AxiosError) { + // @ts-ignore + const { config } = error.response + const { waitTime, count } = config?.requestOptions?.retryRequest + config.__retryCount = config.__retryCount || 0 + if (config.__retryCount >= count) { + return Promise.reject(error) + } + config.__retryCount += 1 + //请求返回后config的header不正确造成重试请求失败,删除返回headers采用默认headers + delete config.headers + return this.delay(waitTime).then(() => axiosInstance(config)) + } + + /** + * 延迟 + */ + private delay(waitTime: number) { + return new Promise((resolve) => setTimeout(resolve, waitTime)) + } +} diff --git a/src/utils/http/axios/axiosTransform.ts b/src/utils/http/axios/axiosTransform.ts new file mode 100644 index 00000000..453ed10d --- /dev/null +++ b/src/utils/http/axios/axiosTransform.ts @@ -0,0 +1,49 @@ +/** + * Data processing class, can be configured according to the project + */ +import type { InternalAxiosRequestConfig, AxiosResponse, AxiosRequestConfig } from 'axios' +import type { RequestOptions, Result } from '@/types/axios' + +export interface CreateAxiosOptions extends AxiosRequestConfig { + authenticationScheme?: string + transform?: AxiosTransform + requestOptions?: RequestOptions +} + +export abstract class AxiosTransform { + /** + * @description: Process configuration before request + * @description: Process configuration before request + */ + beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig + + /** + * @description: 处理响应数据 + */ + transformResponseHook?: (res: AxiosResponse, options: RequestOptions) => any + + /** + * @description: 请求失败处理 + */ + requestCatchHook?: (e: Error, options: RequestOptions) => Promise + + /** + * @description: 请求之前的拦截器 + */ + requestInterceptors?: (config: InternalAxiosRequestConfig, options: CreateAxiosOptions) => InternalAxiosRequestConfig + + /** + * @description: 请求之后的拦截器 + */ + responseInterceptors?: (res: AxiosResponse) => AxiosResponse + + /** + * @description: 请求之前的拦截器错误处理 + */ + requestInterceptorsCatch?: (error: Error) => void + + /** + * @description: 请求之后的拦截器错误处理 + */ + responseInterceptorsCatch?: (axiosInstance: AxiosResponse, error: Error) => void +} diff --git a/src/utils/http/axios/checkStatus.ts b/src/utils/http/axios/checkStatus.ts new file mode 100644 index 00000000..99a40e02 --- /dev/null +++ b/src/utils/http/axios/checkStatus.ts @@ -0,0 +1,76 @@ +import type { ErrorMessageMode } from '@/types/axios' +import { useMessage } from '@/hooks/web/useMessage' +import { useI18n } from '@/hooks/web/useI18n' +// import router from '@/router'; +// import { PageEnum } from '@/enums/pageEnum'; +import { useUserStoreWithOut } from '@/store/modules/user' +import projectSetting from '@/settings/projectSetting' +import { SessionTimeoutProcessingEnum } from '@/enums/appEnum' + +const { createMessage, createErrorModal } = useMessage() +const error = createMessage.error! +const stp = projectSetting.sessionTimeoutProcessing + +export function checkStatus(status: number, msg: string, errorMessageMode: ErrorMessageMode = 'message'): void { + const { t } = useI18n() + const userStore = useUserStoreWithOut() + let errMessage = '' + + switch (status) { + case 400: + errMessage = `${msg}` + break + // 401: Not logged in + // Jump to the login page if not logged in, and carry the path of the current page + // Return to the current page after successful login. This step needs to be operated on the login page. + case 401: + userStore.setToken(undefined) + errMessage = msg || t('sys.api.errMsg401') + if (stp === SessionTimeoutProcessingEnum.PAGE_COVERAGE) { + userStore.setSessionTimeout(true) + } else { + userStore.logout(true) + } + break + case 403: + errMessage = t('sys.api.errMsg403') + break + // 404请求不存在 + case 404: + errMessage = t('sys.api.errMsg404') + break + case 405: + errMessage = t('sys.api.errMsg405') + break + case 408: + errMessage = t('sys.api.errMsg408') + break + case 500: + errMessage = t('sys.api.errMsg500') + break + case 501: + errMessage = t('sys.api.errMsg501') + break + case 502: + errMessage = t('sys.api.errMsg502') + break + case 503: + errMessage = t('sys.api.errMsg503') + break + case 504: + errMessage = t('sys.api.errMsg504') + break + case 505: + errMessage = t('sys.api.errMsg505') + break + default: + } + + if (errMessage) { + if (errorMessageMode === 'modal') { + createErrorModal({ title: t('sys.api.errorTip'), content: errMessage }) + } else if (errorMessageMode === 'message') { + error({ content: errMessage, key: `global_error_message_status_${status}` }) + } + } +} diff --git a/src/utils/http/axios/helper.ts b/src/utils/http/axios/helper.ts new file mode 100644 index 00000000..fe25932c --- /dev/null +++ b/src/utils/http/axios/helper.ts @@ -0,0 +1,45 @@ +import { isObject, isString } from '@/utils/is' + +const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss' + +export function joinTimestamp(join: boolean, restful: T): T extends true ? string : object + +export function joinTimestamp(join: boolean, restful = false): string | object { + if (!join) { + return restful ? '' : {} + } + const now = new Date().getTime() + if (restful) { + return `?_t=${now}` + } + return { _t: now } +} + +/** + * @description: Format request parameter time + */ +export function formatRequestDate(params: Recordable) { + if (Object.prototype.toString.call(params) !== '[object Object]') { + return + } + + for (const key in params) { + const format = params[key]?.format ?? null + if (format && typeof format === 'function') { + params[key] = params[key].format(DATE_TIME_FORMAT) + } + if (isString(key)) { + const value = params[key] + if (value) { + try { + params[key] = isString(value) ? value.trim() : value + } catch (error: any) { + throw new Error(error) + } + } + } + if (isObject(params[key])) { + formatRequestDate(params[key]) + } + } +} diff --git a/src/utils/http/axios/index.ts b/src/utils/http/axios/index.ts new file mode 100644 index 00000000..2b8d6f29 --- /dev/null +++ b/src/utils/http/axios/index.ts @@ -0,0 +1,277 @@ +// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动 +// The axios configuration can be changed according to the project, just change the file, other files can be left unchanged + +import type { AxiosResponse } from 'axios' +import { clone } from 'lodash-es' +import type { RequestOptions, Result } from '@/types/axios' +import type { AxiosTransform, CreateAxiosOptions } from './axiosTransform' +import { VAxios } from './Axios' +import { checkStatus } from './checkStatus' +import { useGlobSetting } from '@/hooks/setting' +import { useMessage } from '@/hooks/web/useMessage' +import { RequestEnum, ResultEnum, ContentTypeEnum } from '@/enums/httpEnum' +import { isString, isUnDef, isNull, isEmpty } from '@/utils/is' +import { getToken } from '@/utils/auth' +import { setObjToUrlParams, deepMerge } from '@/utils' +import { useErrorLogStoreWithOut } from '@/store/modules/errorLog' +import { useI18n } from '@/hooks/web/useI18n' +import { joinTimestamp, formatRequestDate } from './helper' +import { useUserStoreWithOut } from '@/store/modules/user' +import { AxiosRetry } from '@/utils/http/axios/axiosRetry' +import axios from 'axios' + +const globSetting = useGlobSetting() +const urlPrefix = globSetting.urlPrefix +const { createMessage, createErrorModal, createSuccessModal } = useMessage() + +/** + * @description: 数据处理,方便区分多种处理方式 + */ +const transform: AxiosTransform = { + /** + * @description: 处理响应数据。如果数据不是预期格式,可直接抛出错误 + */ + transformResponseHook: (res: AxiosResponse, options: RequestOptions) => { + const { t } = useI18n() + const { isTransformResponse, isReturnNativeResponse } = options + // 是否返回原生响应头 比如:需要获取响应头时使用该属性 + if (isReturnNativeResponse) { + return res + } + // 不进行任何处理,直接返回 + // 用于页面代码可能需要直接获取code,data,message这些信息时开启 + if (!isTransformResponse) { + return res.data + } + // 错误的时候返回 + + const { data } = res + if (!data) { + // return '[HTTP] Request has no return value'; + throw new Error(t('sys.api.apiRequestFailed')) + } + // 这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式 + const { code, result, message } = data + + // 这里逻辑可以根据项目进行修改 + const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS + if (hasSuccess) { + let successMsg = message + + if (isNull(successMsg) || isUnDef(successMsg) || isEmpty(successMsg)) { + successMsg = t(`sys.api.operationSuccess`) + } + + if (options.successMessageMode === 'modal') { + createSuccessModal({ title: t('sys.api.successTip'), content: successMsg }) + } else if (options.successMessageMode === 'message') { + createMessage.success(successMsg) + } + return result + } + + // 在此处根据自己项目的实际情况对不同的code执行不同的操作 + // 如果不希望中断当前请求,请return数据,否则直接抛出异常即可 + let timeoutMsg = '' + switch (code) { + case ResultEnum.TIMEOUT: + timeoutMsg = t('sys.api.timeoutMessage') + const userStore = useUserStoreWithOut() + userStore.setToken(undefined) + userStore.logout(true) + break + default: + if (message) { + timeoutMsg = message + } + } + + // errorMessageMode='modal'的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误 + // errorMessageMode='none' 一般是调用时明确表示不希望自动弹出错误提示 + if (options.errorMessageMode === 'modal') { + createErrorModal({ title: t('sys.api.errorTip'), content: timeoutMsg }) + } else if (options.errorMessageMode === 'message') { + createMessage.error(timeoutMsg) + } + + throw new Error(timeoutMsg || t('sys.api.apiRequestFailed')) + }, + + // 请求之前处理config + beforeRequestHook: (config, options) => { + const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options + + if (joinPrefix) { + config.url = `${urlPrefix}${config.url}` + } + + if (apiUrl && isString(apiUrl)) { + config.url = `${apiUrl}${config.url}` + } + const params = config.params || {} + const data = config.data || false + formatDate && data && !isString(data) && formatRequestDate(data) + if (config.method?.toUpperCase() === RequestEnum.GET) { + if (!isString(params)) { + // 给 get 请求加上时间戳参数,避免从缓存中拿数据。 + config.params = Object.assign(params || {}, joinTimestamp(joinTime, false)) + } else { + // 兼容restful风格 + config.url = config.url + params + `${joinTimestamp(joinTime, true)}` + config.params = undefined + } + } else { + if (!isString(params)) { + formatDate && formatRequestDate(params) + if (Reflect.has(config, 'data') && config.data && (Object.keys(config.data).length > 0 || config.data instanceof FormData)) { + config.data = data + config.params = params + } else { + // 非GET请求如果没有提供data,则将params视为data + config.data = params + config.params = undefined + } + if (joinParamsToUrl) { + config.url = setObjToUrlParams(config.url as string, Object.assign({}, config.params, config.data)) + } + } else { + // 兼容restful风格 + config.url = config.url + params + config.params = undefined + } + } + return config + }, + + /** + * @description: 请求拦截器处理 + */ + requestInterceptors: (config, options) => { + // 请求之前处理config + const token = getToken() + if (token && (config as Recordable)?.requestOptions?.withToken !== false) { + // jwt token + ;(config as Recordable).headers.Authorization = options.authenticationScheme ? `${options.authenticationScheme} ${token}` : token + } + return config + }, + + /** + * @description: 响应拦截器处理 + */ + responseInterceptors: (res: AxiosResponse) => { + return res + }, + + /** + * @description: 响应错误处理 + */ + responseInterceptorsCatch: (axiosInstance: AxiosResponse, error: any) => { + const { t } = useI18n() + const errorLogStore = useErrorLogStoreWithOut() + errorLogStore.addAjaxErrorInfo(error) + const { response, code, message, config } = error || {} + const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none' + const msg: string = response?.data?.error?.message ?? '' + const err: string = error?.toString?.() ?? '' + let errMessage = '' + + if (axios.isCancel(error)) { + return Promise.reject(error) + } + + try { + if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) { + errMessage = t('sys.api.apiTimeoutMessage') + } + if (err?.includes('Network Error')) { + errMessage = t('sys.api.networkExceptionMsg') + } + + if (errMessage) { + if (errorMessageMode === 'modal') { + createErrorModal({ title: t('sys.api.errorTip'), content: errMessage }) + } else if (errorMessageMode === 'message') { + createMessage.error(errMessage) + } + return Promise.reject(error) + } + } catch (error) { + throw new Error(error as unknown as string) + } + + checkStatus(error?.response?.status, msg, errorMessageMode) + + // 添加自动重试机制 保险起见 只针对GET请求 + const retryRequest = new AxiosRetry() + const { isOpenRetry } = config.requestOptions.retryRequest + config.method?.toUpperCase() === RequestEnum.GET && + isOpenRetry && + // @ts-ignore + retryRequest.retry(axiosInstance, error) + return Promise.reject(error) + } +} + +function createAxios(opt?: Partial) { + return new VAxios( + // 深度合并 + deepMerge( + { + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes + // authentication schemes,e.g: Bearer + // authenticationScheme: 'Bearer', + authenticationScheme: '', + timeout: 10 * 1000, + // 基础接口地址 + // baseURL: globSetting.apiUrl, + + headers: { 'Content-Type': ContentTypeEnum.JSON }, + // 如果是form-data格式 + // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED }, + // 数据处理方式 + transform: clone(transform), + // 配置项,下面的选项都可以在独立的接口请求中覆盖 + requestOptions: { + // 默认将prefix 添加到url + joinPrefix: true, + // 是否返回原生响应头 比如:需要获取响应头时使用该属性 + isReturnNativeResponse: false, + // 需要对返回数据进行处理 + isTransformResponse: true, + // post请求的时候添加参数到url + joinParamsToUrl: false, + // 格式化提交参数时间 + formatDate: true, + // 消息提示类型 + errorMessageMode: 'message', + // 接口地址 + apiUrl: globSetting.apiUrl, + // 接口拼接地址 + urlPrefix: urlPrefix, + // 是否加入时间戳 + joinTime: true, + // 忽略重复请求 + ignoreCancelToken: true, + // 是否携带token + withToken: true, + retryRequest: { + isOpenRetry: true, + count: 5, + waitTime: 100 + } + } + }, + opt || {} + ) + ) +} +export const defHttp = createAxios() + +// other api url +// export const otherHttp = createAxios({ +// requestOptions: { +// apiUrl: 'xxx', +// urlPrefix: 'xxx', +// }, +// }); diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 00000000..38e0caad --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,107 @@ +import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router' +import type { App, Component } from 'vue' + +import { unref } from 'vue' +import { isObject } from '@/utils/is' +import { cloneDeep } from 'lodash-es' + +export const noop = () => {} + +/** + * @description: Set ui mount node + */ +export function getPopupContainer(node?: HTMLElement): HTMLElement { + return (node?.parentNode as HTMLElement) ?? document.body +} + +/** + * Add the object as a parameter to the URL + * @param baseUrl url + * @param obj + * @returns {string} + * eg: + * let obj = {a: '3', b: '4'} + * setObjToUrlParams('www.baidu.com', obj) + * ==>www.baidu.com?a=3&b=4 + */ +export function setObjToUrlParams(baseUrl: string, obj: any): string { + let parameters = '' + for (const key in obj) { + parameters += key + '=' + encodeURIComponent(obj[key]) + '&' + } + parameters = parameters.replace(/&$/, '') + return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters +} + +// 深度合并 +export function deepMerge(src: any = {}, target: any = {}): T { + let key: string + const res: any = cloneDeep(src) + for (key in target) { + res[key] = isObject(res[key]) ? deepMerge(res[key], target[key]) : target[key] + } + return res +} + +export function openWindow(url: string, opt?: { target?: TargetContext | string; noopener?: boolean; noreferrer?: boolean }) { + const { target = '__blank', noopener = true, noreferrer = true } = opt || {} + const feature: string[] = [] + + noopener && feature.push('noopener=yes') + noreferrer && feature.push('noreferrer=yes') + + window.open(url, target, feature.join(',')) +} + +// dynamic use hook props +export function getDynamicProps, U>(props: T): Partial { + const ret: Recordable = {} + + Object.keys(props).map((key) => { + ret[key] = unref((props as Recordable)[key]) + }) + + return ret as Partial +} + +export function getRawRoute(route: RouteLocationNormalized): RouteLocationNormalized { + if (!route) return route + const { matched, ...opt } = route + return { + ...opt, + matched: (matched + ? matched.map((item) => ({ + meta: item.meta, + name: item.name, + path: item.path + })) + : undefined) as RouteRecordNormalized[] + } +} + +// https://github.com/vant-ui/vant/issues/8302 +type EventShim = { + new (...args: any[]): { + $props: { + onClick?: (...args: any[]) => void + } + } +} + +export type WithInstall = T & { + install(app: App): void +} & EventShim + +export type CustomComponent = Component & { displayName?: string } + +export const withInstall = (component: T, alias?: string) => { + ;(component as Record).install = (app: App) => { + const compName = component.name || component.displayName + if (!compName) return + app.component(compName, component) + if (alias) { + app.config.globalProperties[alias] = component + } + } + return component as WithInstall +} diff --git a/src/utils/is.ts b/src/utils/is.ts new file mode 100644 index 00000000..43933566 --- /dev/null +++ b/src/utils/is.ts @@ -0,0 +1,98 @@ +const toString = Object.prototype.toString + +export function is(val: unknown, type: string) { + return toString.call(val) === `[object ${type}]` +} + +export function isDef(val?: T): val is T { + return typeof val !== 'undefined' +} + +export function isUnDef(val?: T): val is T { + return !isDef(val) +} + +export function isObject(val: any): val is Record { + return val !== null && is(val, 'Object') +} + +export function isEmpty(val: T): val is T { + if (isArray(val) || isString(val)) { + return val.length === 0 + } + + if (val instanceof Map || val instanceof Set) { + return val.size === 0 + } + + if (isObject(val)) { + return Object.keys(val).length === 0 + } + + return false +} + +export function isDate(val: unknown): val is Date { + return is(val, 'Date') +} + +export function isNull(val: unknown): val is null { + return val === null +} + +export function isNullAndUnDef(val: unknown): val is null | undefined { + return isUnDef(val) && isNull(val) +} + +export function isNullOrUnDef(val: unknown): val is null | undefined { + return isUnDef(val) || isNull(val) +} + +export function isNumber(val: unknown): val is number { + return is(val, 'Number') +} + +export function isPromise(val: unknown): val is Promise { + return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch) +} + +export function isString(val: unknown): val is string { + return is(val, 'String') +} + +export function isFunction(val: unknown): val is Function { + return typeof val === 'function' +} + +export function isBoolean(val: unknown): val is boolean { + return is(val, 'Boolean') +} + +export function isRegExp(val: unknown): val is RegExp { + return is(val, 'RegExp') +} + +export function isArray(val: any): val is Array { + return val && Array.isArray(val) +} + +export function isWindow(val: any): val is Window { + return typeof window !== 'undefined' && is(val, 'Window') +} + +export function isElement(val: unknown): val is Element { + return isObject(val) && !!val.tagName +} + +export function isMap(val: unknown): val is Map { + return is(val, 'Map') +} + +export const isServer = typeof window === 'undefined' + +export const isClient = !isServer + +export function isUrl(path: string): boolean { + const reg = /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/ + return reg.test(path) +} diff --git a/src/utils/lib/echarts.ts b/src/utils/lib/echarts.ts new file mode 100644 index 00000000..61f9f593 --- /dev/null +++ b/src/utils/lib/echarts.ts @@ -0,0 +1,49 @@ +import * as echarts from 'echarts/core' + +import { BarChart, LineChart, PieChart, MapChart, PictorialBarChart, RadarChart, ScatterChart } from 'echarts/charts' + +import { + TitleComponent, + TooltipComponent, + GridComponent, + PolarComponent, + AriaComponent, + ParallelComponent, + LegendComponent, + RadarComponent, + ToolboxComponent, + DataZoomComponent, + VisualMapComponent, + TimelineComponent, + CalendarComponent, + GraphicComponent +} from 'echarts/components' + +import { SVGRenderer } from 'echarts/renderers' + +echarts.use([ + LegendComponent, + TitleComponent, + TooltipComponent, + GridComponent, + PolarComponent, + AriaComponent, + ParallelComponent, + BarChart, + LineChart, + PieChart, + MapChart, + RadarChart, + SVGRenderer, + PictorialBarChart, + RadarComponent, + ToolboxComponent, + DataZoomComponent, + VisualMapComponent, + TimelineComponent, + CalendarComponent, + GraphicComponent, + ScatterChart +]) + +export default echarts diff --git a/src/utils/log.ts b/src/utils/log.ts new file mode 100644 index 00000000..6f0d9d4a --- /dev/null +++ b/src/utils/log.ts @@ -0,0 +1,9 @@ +const projectName = import.meta.env.VITE_GLOB_APP_TITLE + +export function warn(message: string) { + console.warn(`[${projectName} warn]:${message}`) +} + +export function error(message: string) { + throw new Error(`[${projectName} error]:${message}`) +} diff --git a/src/utils/mitt.ts b/src/utils/mitt.ts new file mode 100644 index 00000000..421af464 --- /dev/null +++ b/src/utils/mitt.ts @@ -0,0 +1,101 @@ +/** + * copy to https://github.com/developit/mitt + * Expand clear method + */ + +export type EventType = string | symbol + +// An event handler can take an optional event argument +// and should not return a value +export type Handler = (event?: T) => void +export type WildcardHandler = (type: EventType, event?: any) => void + +// An array of all currently registered event handlers for a type +export type EventHandlerList = Array +export type WildCardEventHandlerList = Array + +// A map of event types and their corresponding event handlers. +export type EventHandlerMap = Map + +export interface Emitter { + all: EventHandlerMap + + on(type: EventType, handler: Handler): void + on(type: '*', handler: WildcardHandler): void + + off(type: EventType, handler: Handler): void + off(type: '*', handler: WildcardHandler): void + + emit(type: EventType, event?: T): void + emit(type: '*', event?: any): void + clear(): void +} + +/** + * Mitt: Tiny (~200b) functional event emitter / pubsub. + * @name mitt + * @returns {Mitt} + */ +export default function mitt(all?: EventHandlerMap): Emitter { + all = all || new Map() + + return { + /** + * A Map of event names to registered handler functions. + */ + all, + + /** + * Register an event handler for the given type. + * @param {string|symbol} type Type of event to listen for, or `"*"` for all events + * @param {Function} handler Function to call in response to given event + * @memberOf mitt + */ + on(type: EventType, handler: Handler) { + const handlers = all?.get(type) + const added = handlers && handlers.push(handler) + if (!added) { + all?.set(type, [handler]) + } + }, + + /** + * Remove an event handler for the given type. + * @param {string|symbol} type Type of event to unregister `handler` from, or `"*"` + * @param {Function} handler Handler function to remove + * @memberOf mitt + */ + off(type: EventType, handler: Handler) { + const handlers = all?.get(type) + if (handlers) { + handlers.splice(handlers.indexOf(handler) >>> 0, 1) + } + }, + + /** + * Invoke all handlers for the given type. + * If present, `"*"` handlers are invoked after type-matched handlers. + * + * Note: Manually firing "*" handlers is not supported. + * + * @param {string|symbol} type The event type to invoke + * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler + * @memberOf mitt + */ + emit(type: EventType, evt: T) { + ;((all?.get(type) || []) as EventHandlerList).slice().map((handler) => { + handler(evt) + }) + ;((all?.get('*') || []) as WildCardEventHandlerList).slice().map((handler) => { + handler(type, evt) + }) + }, + + /** + * Clear all + */ + clear() { + this.all.clear() + } + } +} diff --git a/src/utils/propTypes.ts b/src/utils/propTypes.ts new file mode 100644 index 00000000..049eb204 --- /dev/null +++ b/src/utils/propTypes.ts @@ -0,0 +1,34 @@ +import { CSSProperties, VNodeChild } from 'vue' +import { createTypes, VueTypeValidableDef, VueTypesInterface } from 'vue-types' + +export type VueNode = VNodeChild | JSX.Element + +type PropTypes = VueTypesInterface & { + readonly style: VueTypeValidableDef + readonly VNodeChild: VueTypeValidableDef + // readonly trueBool: VueTypeValidableDef; +} + +const propTypes = createTypes({ + func: undefined, + bool: undefined, + string: undefined, + number: undefined, + object: undefined, + integer: undefined +}) as PropTypes + +// propTypes.extend([ +// { +// name: 'style', +// getter: true, +// type: [String, Object], +// default: undefined +// }, +// { +// name: 'VNodeChild', +// getter: true, +// type: undefined +// } +// ]) +export { propTypes } diff --git a/src/utils/props.ts b/src/utils/props.ts new file mode 100644 index 00000000..ddd77546 --- /dev/null +++ b/src/utils/props.ts @@ -0,0 +1,158 @@ +// copy from element-plus + +import { warn } from 'vue' +import { isObject } from '@/utils/is' +import { fromPairs } from 'lodash-es' +import type { ExtractPropTypes } from 'vue' +import type { Mutable } from './types' + +const wrapperKey = Symbol() +export type PropWrapper = { [wrapperKey]: T } + +export const propKey = Symbol() + +type ResolveProp = ExtractPropTypes<{ + key: { type: T; required: true } +}>['key'] +type ResolvePropType = ResolveProp extends { type: infer V } ? V : ResolveProp +type ResolvePropTypeWithReadonly = Readonly extends Readonly> ? ResolvePropType : ResolvePropType + +type IfUnknown = [unknown] extends [T] ? V : T + +export type BuildPropOption, R, V, C> = { + type?: T + values?: readonly V[] + required?: R + default?: R extends true ? never : D extends Record | Array ? () => D : (() => D) | D + validator?: ((val: any) => val is C) | ((val: any) => boolean) +} + +type _BuildPropType = + | (T extends PropWrapper ? T[typeof wrapperKey] : [V] extends [never] ? ResolvePropTypeWithReadonly : never) + | V + | C +export type BuildPropType = _BuildPropType, IfUnknown, IfUnknown> + +type _BuildPropDefault = [T] extends [ + // eslint-disable-next-line @typescript-eslint/ban-types + Record | Array | Function +] + ? D + : D extends () => T + ? ReturnType + : D + +export type BuildPropDefault = R extends true + ? { readonly default?: undefined } + : { + readonly default: Exclude extends never ? undefined : Exclude<_BuildPropDefault, undefined> + } +export type BuildPropReturn = { + readonly type: PropType> + readonly required: IfUnknown + readonly validator: ((val: unknown) => boolean) | undefined + [propKey]: true +} & BuildPropDefault, IfUnknown, IfUnknown> + +/** + * @description Build prop. It can better optimize prop types + * @description 生成 prop,能更好地优化类型 + * @example + // limited options + // the type will be PropType<'light' | 'dark'> + buildProp({ + type: String, + values: ['light', 'dark'], + } as const) + * @example + // limited options and other types + // the type will be PropType<'small' | 'medium' | number> + buildProp({ + type: [String, Number], + values: ['small', 'medium'], + validator: (val: unknown): val is number => typeof val === 'number', + } as const) + @link see more: https://github.com/element-plus/element-plus/pull/3341 + */ +export function buildProp = never, R extends boolean = false, V = never, C = never>( + option: BuildPropOption, + key?: string +): BuildPropReturn { + // filter native prop type and nested prop, e.g `null`, `undefined` (from `buildProps`) + if (!isObject(option) || !!option[propKey]) return option as any + + const { values, required, default: defaultValue, type, validator } = option + + const _validator = + values || validator + ? (val: unknown) => { + let valid = false + let allowedValues: unknown[] = [] + + if (values) { + allowedValues = [...values, defaultValue] + valid ||= allowedValues.includes(val) + } + if (validator) valid ||= validator(val) + + if (!valid && allowedValues.length > 0) { + const allowValuesText = [...new Set(allowedValues)].map((value) => JSON.stringify(value)).join(', ') + warn( + `Invalid prop: validation failed${ + key ? ` for prop "${key}"` : '' + }. Expected one of [${allowValuesText}], got value ${JSON.stringify(val)}.` + ) + } + return valid + } + : undefined + + return { + type: typeof type === 'object' && type && Object.getOwnPropertySymbols(type).includes(wrapperKey) ? type[wrapperKey] : type, + required: !!required, + default: defaultValue, + validator: _validator, + [propKey]: true + } as unknown as BuildPropReturn +} + +type NativePropType = [((...args: any) => any) | { new (...args: any): any } | undefined | null] + +export const buildProps = < + O extends { + [K in keyof O]: O[K] extends BuildPropReturn + ? O[K] + : [O[K]] extends NativePropType + ? O[K] + : O[K] extends BuildPropOption + ? D extends BuildPropType + ? BuildPropOption + : never + : never + } +>( + props: O +) => + fromPairs(Object.entries(props).map(([key, option]) => [key, buildProp(option as any, key)])) as unknown as { + [K in keyof O]: O[K] extends { [propKey]: boolean } + ? O[K] + : [O[K]] extends NativePropType + ? O[K] + : O[K] extends BuildPropOption< + infer T, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + infer _D, + infer R, + infer V, + infer C + > + ? BuildPropReturn + : never + } + +export const definePropType = (val: any) => ({ [wrapperKey]: val } as PropWrapper) + +export const keyOf = (arr: T) => Object.keys(arr as any) as Array +export const mutable = >(val: T) => val as Mutable + +export const componentSize = ['large', 'medium', 'small', 'mini'] as const diff --git a/src/utils/types.ts b/src/utils/types.ts new file mode 100644 index 00000000..04b52f60 --- /dev/null +++ b/src/utils/types.ts @@ -0,0 +1,41 @@ +// copy from element-plus + +import type { CSSProperties, Plugin } from 'vue' + +type OptionalKeys> = { + [K in keyof T]: T extends Record ? never : K +}[keyof T] + +type RequiredKeys> = Exclude> + +type MonoArgEmitter = (evt: K, arg?: T[K]) => void + +type BiArgEmitter = (evt: K, arg: T[K]) => void + +export type EventEmitter> = MonoArgEmitter> & BiArgEmitter> + +export type AnyFunction = (...args: any[]) => T + +export type PartialReturnType unknown> = Partial> + +export type SFCWithInstall = T & Plugin + +export type Nullable = T | null + +export type RefElement = Nullable + +export type CustomizedHTMLElement = HTMLElement & T + +export type Indexable = { + [key: string]: T +} + +export type Hash = Indexable + +export type TimeoutHandle = ReturnType + +export type ComponentSize = 'large' | 'medium' | 'small' | 'mini' + +export type StyleValue = string | CSSProperties | Array + +export type Mutable = { -readonly [P in keyof T]: T[P] } diff --git a/src/utils/uuid.ts b/src/utils/uuid.ts new file mode 100644 index 00000000..4e101222 --- /dev/null +++ b/src/utils/uuid.ts @@ -0,0 +1,28 @@ +const hexList: string[] = [] +for (let i = 0; i <= 15; i++) { + hexList[i] = i.toString(16) +} + +export function buildUUID(): string { + let uuid = '' + for (let i = 1; i <= 36; i++) { + if (i === 9 || i === 14 || i === 19 || i === 24) { + uuid += '-' + } else if (i === 15) { + uuid += 4 + } else if (i === 20) { + uuid += hexList[(Math.random() * 4) | 8] + } else { + uuid += hexList[(Math.random() * 16) | 0] + } + } + return uuid.replace(/-/g, '') +} + +let unique = 0 +export function buildShortUUID(prefix = ''): string { + const time = Date.now() + const random = Math.floor(Math.random() * 1000000000) + unique++ + return prefix + '_' + random + unique + String(time) +} diff --git a/src/views/dashboard/analysis/components/GrowCard.vue b/src/views/dashboard/analysis/components/GrowCard.vue new file mode 100644 index 00000000..fcb98690 --- /dev/null +++ b/src/views/dashboard/analysis/components/GrowCard.vue @@ -0,0 +1,38 @@ + + diff --git a/src/views/dashboard/analysis/components/SalesProductPie.vue b/src/views/dashboard/analysis/components/SalesProductPie.vue new file mode 100644 index 00000000..f6e72580 --- /dev/null +++ b/src/views/dashboard/analysis/components/SalesProductPie.vue @@ -0,0 +1,59 @@ + + diff --git a/src/views/dashboard/analysis/components/SiteAnalysis.vue b/src/views/dashboard/analysis/components/SiteAnalysis.vue new file mode 100644 index 00000000..6ac3084c --- /dev/null +++ b/src/views/dashboard/analysis/components/SiteAnalysis.vue @@ -0,0 +1,33 @@ + + diff --git a/src/views/dashboard/analysis/components/VisitAnalysis.vue b/src/views/dashboard/analysis/components/VisitAnalysis.vue new file mode 100644 index 00000000..13229ca5 --- /dev/null +++ b/src/views/dashboard/analysis/components/VisitAnalysis.vue @@ -0,0 +1,81 @@ + + diff --git a/src/views/dashboard/analysis/components/VisitAnalysisBar.vue b/src/views/dashboard/analysis/components/VisitAnalysisBar.vue new file mode 100644 index 00000000..11ee02a8 --- /dev/null +++ b/src/views/dashboard/analysis/components/VisitAnalysisBar.vue @@ -0,0 +1,45 @@ + + diff --git a/src/views/dashboard/analysis/components/VisitRadar.vue b/src/views/dashboard/analysis/components/VisitRadar.vue new file mode 100644 index 00000000..9047986c --- /dev/null +++ b/src/views/dashboard/analysis/components/VisitRadar.vue @@ -0,0 +1,89 @@ + + diff --git a/src/views/dashboard/analysis/components/VisitSource.vue b/src/views/dashboard/analysis/components/VisitSource.vue new file mode 100644 index 00000000..88c49a54 --- /dev/null +++ b/src/views/dashboard/analysis/components/VisitSource.vue @@ -0,0 +1,76 @@ + + diff --git a/src/views/dashboard/analysis/components/props.ts b/src/views/dashboard/analysis/components/props.ts new file mode 100644 index 00000000..610bcf6f --- /dev/null +++ b/src/views/dashboard/analysis/components/props.ts @@ -0,0 +1,16 @@ +import { PropType } from 'vue' + +export interface BasicProps { + width: string + height: string +} +export const basicProps = { + width: { + type: String as PropType, + default: '100%' + }, + height: { + type: String as PropType, + default: '280px' + } +} diff --git a/src/views/dashboard/analysis/data.ts b/src/views/dashboard/analysis/data.ts new file mode 100644 index 00000000..090970d1 --- /dev/null +++ b/src/views/dashboard/analysis/data.ts @@ -0,0 +1,43 @@ +export interface GrowCardItem { + icon: string + title: string + value: number + total: number + color: string + action: string +} + +export const growCardList: GrowCardItem[] = [ + { + title: '访问数', + icon: 'visit-count|svg', + value: 2000, + total: 120000, + color: 'green', + action: '月' + }, + { + title: '成交额', + icon: 'total-sales|svg', + value: 20000, + total: 500000, + color: 'blue', + action: '月' + }, + { + title: '下载数', + icon: 'download-count|svg', + value: 8000, + total: 120000, + color: 'orange', + action: '周' + }, + { + title: '成交数', + icon: 'transaction|svg', + value: 5000, + total: 50000, + color: 'purple', + action: '年' + } +] diff --git a/src/views/dashboard/analysis/index.vue b/src/views/dashboard/analysis/index.vue new file mode 100644 index 00000000..8719769a --- /dev/null +++ b/src/views/dashboard/analysis/index.vue @@ -0,0 +1,25 @@ + + diff --git a/src/views/dashboard/workbench/components/DynamicInfo.vue b/src/views/dashboard/workbench/components/DynamicInfo.vue new file mode 100644 index 00000000..9558badc --- /dev/null +++ b/src/views/dashboard/workbench/components/DynamicInfo.vue @@ -0,0 +1,31 @@ + + diff --git a/src/views/dashboard/workbench/components/ProjectCard.vue b/src/views/dashboard/workbench/components/ProjectCard.vue new file mode 100644 index 00000000..f61c9bc9 --- /dev/null +++ b/src/views/dashboard/workbench/components/ProjectCard.vue @@ -0,0 +1,24 @@ + + diff --git a/src/views/dashboard/workbench/components/QuickNav.vue b/src/views/dashboard/workbench/components/QuickNav.vue new file mode 100644 index 00000000..844d1753 --- /dev/null +++ b/src/views/dashboard/workbench/components/QuickNav.vue @@ -0,0 +1,15 @@ + + diff --git a/src/views/dashboard/workbench/components/SaleRadar.vue b/src/views/dashboard/workbench/components/SaleRadar.vue new file mode 100644 index 00000000..e58c7797 --- /dev/null +++ b/src/views/dashboard/workbench/components/SaleRadar.vue @@ -0,0 +1,89 @@ + + diff --git a/src/views/dashboard/workbench/components/WorkbenchHeader.vue b/src/views/dashboard/workbench/components/WorkbenchHeader.vue new file mode 100644 index 00000000..5fe095e2 --- /dev/null +++ b/src/views/dashboard/workbench/components/WorkbenchHeader.vue @@ -0,0 +1,33 @@ + + diff --git a/src/views/dashboard/workbench/components/data.ts b/src/views/dashboard/workbench/components/data.ts new file mode 100644 index 00000000..b6371cff --- /dev/null +++ b/src/views/dashboard/workbench/components/data.ts @@ -0,0 +1,156 @@ +interface GroupItem { + title: string + icon: string + color: string + desc: string + date: string + group: string +} + +interface NavItem { + title: string + icon: string + color: string +} + +interface DynamicInfoItem { + avatar: string + name: string + date: string + desc: string +} + +export const navItems: NavItem[] = [ + { + title: '首页', + icon: 'ion:home-outline', + color: '#1fdaca' + }, + { + title: '仪表盘', + icon: 'ion:grid-outline', + color: '#bf0c2c' + }, + { + title: '组件', + icon: 'ion:layers-outline', + color: '#e18525' + }, + { + title: '系统管理', + icon: 'ion:settings-outline', + color: '#3fb27f' + }, + { + title: '权限管理', + icon: 'ion:key-outline', + color: '#4daf1bc9' + }, + { + title: '图表', + icon: 'ion:bar-chart-outline', + color: '#00d8ff' + } +] + +export const dynamicInfoItems: DynamicInfoItem[] = [ + { + avatar: 'dynamic-avatar-1|svg', + name: '威廉', + date: '刚刚', + desc: `在
开源组 创建了项目 Vue` + }, + { + avatar: 'dynamic-avatar-2|svg', + name: '艾文', + date: '1个小时前', + desc: `关注了 威廉 ` + }, + { + avatar: 'dynamic-avatar-3|svg', + name: '克里斯', + date: '1天前', + desc: `发布了 个人动态 ` + }, + { + avatar: 'dynamic-avatar-4|svg', + name: 'Vben', + date: '2天前', + desc: `发表文章 如何编写一个Vite插件 ` + }, + { + avatar: 'dynamic-avatar-5|svg', + name: '皮特', + date: '3天前', + desc: `回复了 杰克 的问题 如何进行项目优化?` + }, + { + avatar: 'dynamic-avatar-6|svg', + name: '杰克', + date: '1周前', + desc: `关闭了问题 如何运行项目 ` + }, + { + avatar: 'dynamic-avatar-1|svg', + name: '威廉', + date: '1周前', + desc: `发布了 个人动态 ` + }, + { + avatar: 'dynamic-avatar-1|svg', + name: '威廉', + date: '2021-04-01 20:00', + desc: `推送了代码到 Github` + } +] + +export const groupItems: GroupItem[] = [ + { + title: 'Github', + icon: 'carbon:logo-github', + color: '', + desc: '不要等待机会,而要创造机会。', + group: '开源组', + date: '2021-04-01' + }, + { + title: 'Vue', + icon: 'ion:logo-vue', + color: '#3fb27f', + desc: '现在的你决定将来的你。', + group: '算法组', + date: '2021-04-01' + }, + { + title: 'Html5', + icon: 'ion:logo-html5', + color: '#e18525', + desc: '没有什么才能比努力更重要。', + group: '上班摸鱼', + date: '2021-04-01' + }, + { + title: 'Angular', + icon: 'ion:logo-angular', + color: '#bf0c2c', + desc: '热情和欲望可以突破一切难关。', + group: 'UI', + date: '2021-04-01' + }, + { + title: 'React', + icon: 'bx:bxl-react', + color: '#00d8ff', + desc: '健康的身体是实现目标的基石。', + group: '技术牛', + date: '2021-04-01' + }, + { + title: 'Js', + icon: 'ion:logo-javascript', + color: '#EBD94E', + desc: '路是走出来的,而不是空想出来的。', + group: '架构组', + date: '2021-04-01' + } +] diff --git a/src/views/dashboard/workbench/index.vue b/src/views/dashboard/workbench/index.vue new file mode 100644 index 00000000..abbf46be --- /dev/null +++ b/src/views/dashboard/workbench/index.vue @@ -0,0 +1,36 @@ + + diff --git a/src/views/demo/charts/Line.vue b/src/views/demo/charts/Line.vue new file mode 100644 index 00000000..6415f46b --- /dev/null +++ b/src/views/demo/charts/Line.vue @@ -0,0 +1,107 @@ + + diff --git a/src/views/demo/charts/Map.vue b/src/views/demo/charts/Map.vue new file mode 100644 index 00000000..0cbece8a --- /dev/null +++ b/src/views/demo/charts/Map.vue @@ -0,0 +1,65 @@ + + diff --git a/src/views/demo/charts/Pie.vue b/src/views/demo/charts/Pie.vue new file mode 100644 index 00000000..02e3bcef --- /dev/null +++ b/src/views/demo/charts/Pie.vue @@ -0,0 +1,125 @@ + + diff --git a/src/views/demo/charts/SaleRadar.vue b/src/views/demo/charts/SaleRadar.vue new file mode 100644 index 00000000..264fc614 --- /dev/null +++ b/src/views/demo/charts/SaleRadar.vue @@ -0,0 +1,90 @@ + + diff --git a/src/views/demo/charts/china.json b/src/views/demo/charts/china.json new file mode 100644 index 00000000..96d37e57 --- /dev/null +++ b/src/views/demo/charts/china.json @@ -0,0 +1,839 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": "710000", + "properties": { + "id": "710000", + "cp": [121.509062, 24.044332], + "name": "台湾", + "childNum": 6 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@°Ü¯Û"], + ["@@ƛĴÕƊÉɼģºðʀ\\ƎsÆNŌÔĚäœnÜƤɊĂǀĆĴžĤNJŨxĚĮǂƺòƌ‚–âÔ®ĮXŦţƸZûЋƕƑGđ¨ĭMó·ęcëƝɉlÝƯֹÅŃ^Ó·śŃNjƏďíåɛGɉ™¿@ăƑŽ¥ĘWǬÏĶŁâ"], + ["@@\\p|WoYG¿¥I†j@¢"], + ["@@…¡‰@ˆV^RqˆBbAŒnTXeRz¤Lž«³I"], + ["@@ÆEE—„kWqë @œ"], + ["@@fced"], + ["@@„¯ɜÄèaì¯ØǓIġĽ"], + ["@@çûĖ롖hòř "] + ], + "encodeOffsets": [ + [[122886, 24033]], + [[123335, 22980]], + [[122375, 24193]], + [[122518, 24117]], + [[124427, 22618]], + [[124862, 26043]], + [[126259, 26318]], + [[127671, 26683]] + ] + } + }, + { + "type": "Feature", + "id": "130000", + "properties": { + "id": "130000", + "cp": [114.502461, 38.045474], + "name": "河北", + "childNum": 3 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@o~†Z]‚ªr‰ºc_ħ²G¼s`jΟnüsœłNX_“M`ǽÓnUK…Ĝēs¤­©yrý§uģŒc†JŠ›e"], + ["@@U`Ts¿m‚"], + [ + "@@oºƋÄd–eVŽDJj£€J|Ådz•Ft~žKŨ¸IÆv|”‡¢r}膎onb˜}`RÎÄn°ÒdÞ²„^®’lnÐèĄlðӜ×]ªÆ}LiĂ±Ö`^°Ç¶p®đDcœŋ`–ZÔ’¶êqvFƚ†N®ĆTH®¦O’¾ŠIbÐã´BĐɢŴÆíȦp–ĐÞXR€·nndOž¤’OÀĈƒ­Qg˜µFo|gȒęSWb©osx|hYh•gŃfmÖĩnº€T̒Sp›¢dYĤ¶UĈjl’ǐpäìë|³kÛfw²Xjz~ÂqbTŠÑ„ěŨ@|oM‡’zv¢ZrÃVw¬ŧĖ¸fŒ°ÐT€ªqŽs{Sž¯r æÝlNd®²Ğ džiGʂJ™¼lr}~K¨ŸƐÌWö€™ÆŠzRš¤lêmĞL΄’@¡|q]SvK€ÑcwpÏρ†ĿćènĪWlĄkT}ˆJ”¤~ƒÈT„d„™pddʾĬŠ”ŽBVt„EÀ¢ôPĎƗè@~‚k–ü\\rÊĔÖæW_§¼F˜†´©òDòj’ˆYÈrbĞāøŀG{ƀ|¦ðrb|ÀH`pʞkv‚GpuARhÞÆǶgʊTǼƹS£¨¡ù³ŘÍ]¿Ây™ôEP xX¶¹܇O¡“gÚ¡IwÃ鑦ÅB‡Ï|Ç°…N«úmH¯‹âŸDùŽyŜžŲIÄuШDž•¸dɂ‡‚FŸƒ•›Oh‡đ©OŸ›iÃ`ww^ƒÌkŸ‘ÑH«ƇǤŗĺtFu…{Z}Ö@U‡´…ʚLg®¯Oı°ÃwŸ ^˜—€VbÉs‡ˆmA…ê]]w„§›RRl£‡ȭµu¯b{ÍDěïÿȧŽuT£ġƒěŗƃĝ“Q¨fV†Ƌ•ƅn­a@‘³@šď„yýIĹÊKšŭfċŰóŒxV@tˆƯŒJ”]eƒR¾fe|rHA˜|h~Ėƍl§ÏŠlTíb ØoˆÅbbx³^zÃĶš¶Sj®A”yÂhðk`š«P€”ˈµEF†Û¬Y¨Ļrõqi¼‰Wi°§’б´°^[ˆÀ|ĠO@ÆxO\\tŽa\\tĕtû{ġŒȧXýĪÓjùÎRb›š^ΛfK[ݏděYfíÙTyŽuUSyŌŏů@Oi½’éŅ­aVcř§ax¹XŻác‡žWU£ôãºQ¨÷Ñws¥qEH‰Ù|‰›šYQoŕÇyáĂ£MðoťÊ‰P¡mšWO¡€v†{ôvîēÜISpÌhp¨ ‘j†deŔQÖj˜X³à™Ĉ[n`Yp@Už–cM`’RKhŒEbœ”pŞlNut®Etq‚nsÁŠgA‹iú‹oH‡qCX‡”hfgu“~ϋWP½¢G^}¯ÅīGCŸÑ^ãziMáļMTÃƘrMc|O_ž¯Ŏ´|‡morDkO\\mĆJfl@cĢ¬¢aĦtRıҙ¾ùƀ^juųœK­ƒUFy™—Ɲ…›īÛ÷ąV×qƥV¿aȉd³B›qPBm›aËđŻģm“Å®VŠ¹d^K‡KoŸnYg“¯Xhqa”Ldu¥•ÍpDž¡KąÅƒkĝęěhq‡}HyÓ]¹ǧ£…Í÷¿qᵧš™g‘¤o^á¾ZE‡¤i`ij{n•ƒOl»ŸWÝĔįhg›F[¿¡—ßkOüš_‰€ū‹i„DZàUtėGylƒ}ŒÓM}€jpEC~¡FtoQi‘šHkk{Ãmï‚" + ] + ], + "encodeOffsets": [[[119712, 40641]], [[121616, 39981]], [[116462, 37237]]] + } + }, + { + "type": "Feature", + "id": "140000", + "properties": { + "id": "140000", + "cp": [111.849248, 36.857014], + "name": "山西", + "childNum": 1 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@Þĩ҃S‰ra}Á€yWix±Üe´lè“ßÓǏok‘ćiµVZģ¡coœ‘TS˹ĪmnÕńe–hZg{gtwªpXaĚThȑp{¶Eh—®RćƑP¿£‘Pmc¸mQÝW•ďȥoÅîɡųAďä³aωJ‘½¥PG­ąSM­™…EÅruµé€‘Yӎ•Ō_d›ĒCo­Èµ]¯_²ÕjāŽK~©ÅØ^ԛkïçămϑk]­±ƒcݯÑÃmQÍ~_a—pm…~ç¡q“ˆu{JÅŧ·Ls}–EyÁÆcI{¤IiCfUc•ƌÃp§]웫vD@¡SÀ‘µM‚ÅwuŽYY‡¡DbÑc¡hƒ×]nkoQdaMç~eD•ÛtT‰©±@¥ù@É¡‰ZcW|WqOJmĩl«ħşvOÓ«IqăV—¥ŸD[mI~Ó¢cehiÍ]Ɠ~ĥqXŠ·eƷœn±“}v•[ěďŽŕ]_‘œ•`‰¹ƒ§ÕōI™o©b­s^}Ét±ū«³p£ÿ·Wµ|¡¥ăFÏs׌¥ŅxŸÊdÒ{ºvĴÎêÌɊ²¶€ü¨|ÞƸµȲ‘LLúÉƎ¤ϊęĔV`„_bª‹S^|ŸdŠzY|dz¥p†ZbÆ£¶ÒK}tĦÔņƠ‚PYzn€ÍvX¶Ěn ĠÔ„zý¦ª˜÷žÑĸَUȌ¸‚dòÜJð´’ìúNM¬ŒXZ´‘¤ŊǸ_tldIš{¦ƀðĠȤ¥NehXnYG‚‡R° ƬDj¬¸|CĞ„Kq‚ºfƐiĺ©ª~ĆOQª ¤@ìǦɌ²æBŒÊ”TœŸ˜ʂōĖ’šĴŞ–ȀœÆÿȄlŤĒö„t”νî¼ĨXhŒ‘˜|ªM¤Ðz" + ], + "encodeOffsets": [[116874, 41716]] + } + }, + { + "type": "Feature", + "id": "150000", + "properties": { + "id": "150000", + "cp": [111.670801, 41.818311], + "name": "内蒙古", + "childNum": 2 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + "@@¯PqƒFB…‰|S•³C|kñ•H‹d‘iÄ¥sˆʼnő…PóÑÑE^‘ÅPpy_YtS™hQ·aHwsOnʼnÚs©iqj›‰€USiº]ïWš‰«gW¡A–Rë¥_ŽsgÁnUI«m‰…„‹]j‡vV¼euhwqA„aW˜ƒ_µj…»çjioQR¹ēÃßt@r³[ÛlćË^ÍÉáG“›OUۗOB±•XŸkŇ¹£k|e]ol™ŸkVͼÕqtaÏõjgÁ£§U^Œ”RLˆËnX°Ç’Bz†^~wfvˆypV ¯„ƫĉ˭ȫƗŷɿÿĿƑ˃ĝÿÃǃßËőó©ǐȍŒĖM×ÍEyx‹þp]Évïè‘vƀnÂĴÖ@‚‰†V~Ĉv¦wĖt—ējyÄDXÄxGQuv_›i¦aBçw‘˛wD™©{ŸtāmQ€{EJ§KPśƘƿ¥@‰sCT•É}ɃwˆƇy±ŸgÑ“}T[÷kÐ禫…SÒ¥¸ëBX½‰HáŵÀğtSÝÂa[ƣ°¯¦P]£ġ“–“Òk®G²„èQ°óMq}EŠóƐÇ\\ƒ‡@áügQ͋u¥Fƒ“T՛¿Jû‡]|mvāÎYua^WoÀa·­ząÒot׶CLƗi¯¤mƎHNJ¤îìɾŊìTdåwsRÖgĒųúÍġäÕ}Q¶—ˆ¿A•†‹[¡Œ{d×uQAƒ›M•xV‹vMOmăl«ct[wº_šÇʊŽŸjb£ĦS_é“QZ“_lwgOiýe`YYLq§IÁˆdz£ÙË[ÕªuƏ³ÍT—s·bÁĽäė[›b[ˆŗfãcn¥îC¿÷µ[ŏÀQ­ōšĉm¿Á^£mJVm‡—L[{Ï_£›F¥Ö{ŹA}…×Wu©ÅaųijƳhB{·TQqÙIķˑZđ©Yc|M¡…L•eVUóK_QWk’_ĥ‘¿ãZ•»X\\ĴuUƒè‡lG®ěłTĠğDєOrÍd‚ÆÍz]‹±…ŭ©ŸÅ’]ŒÅÐ}UË¥©Tċ™ïxgckfWgi\\ÏĒ¥HkµE˜ë{»ÏetcG±ahUiñiWsɁˆ·c–C‚Õk]wȑ|ća}w…VaĚ᠞ŒG°ùnM¬¯†{ȈÐÆA’¥ÄêJxÙ¢”hP¢Ûˆº€µwWOŸóFŽšÁz^ÀŗÎú´§¢T¤ǻƺSė‰ǵhÝÅQgvBHouʝl_o¿Ga{ïq{¥|ſĿHĂ÷aĝÇq‡Z‘ñiñC³ª—…»E`¨åXēÕqÉû[l•}ç@čƘóO¿¡ƒFUsA‰“ʽīccšocƒ‚ƒÇS}„“£‡IS~ălkĩXçmĈ…ŀЂoÐdxÒuL^T{r@¢‘žÍƒĝKén£kQ™‰yšÅõËXŷƏL§~}kqš»IHėDžjĝŸ»ÑÞoŸå°qTt|r©ÏS‹¯·eŨĕx«È[eMˆ¿yuˆ‘pN~¹ÏyN£{©’—g‹ħWí»Í¾s“əšDž_ÃĀɗ±ą™ijĉʍŌŷ—S›É“A‹±åǥɋ@럣R©ąP©}ĹªƏj¹erƒLDĝ·{i«ƫC£µsKCš…GS|úþX”gp›{ÁX¿Ÿć{ƱȏñZáĔyoÁhA™}ŅĆfdʼn„_¹„Y°ėǩÑ¡H¯¶oMQqð¡Ë™|‘Ñ`ƭŁX½·óۓxğįÅcQ‡ˆ“ƒs«tȋDžF“Ÿù^i‘t«Č¯[›hAi©á¥ÇĚ×l|¹y¯YȵƓ‹ñǙµï‚ċ™Ļ|Dœ™üȭ¶¡˜›oŽäÕG\\ďT¿Òõr¯œŸLguÏYęRƩšɷŌO\\İТæ^Ŋ IJȶȆbÜGŽĝ¬¿ĚVĎgª^íu½jÿĕęjık@Ľƒ]ėl¥Ë‡ĭûÁ„ƒėéV©±ćn©­ȇžÍq¯½•YÃÔʼn“ÉNѝÅÝy¹NqáʅDǡËñ­ƁYÅy̱os§ȋµʽǘǏƬɱà‘ưN¢ƔÊuľýľώȪƺɂļžxœZĈ}ÌʼnŪ˜ĺœŽĭFЛĽ̅ȣͽÒŵìƩÇϋÿȮǡŏçƑůĕ~Ǎ›¼ȳÐUf†dIxÿ\\G ˆzâɏÙOº·pqy£†@ŒŠqþ@Ǟ˽IBäƣzsÂZ†ÁàĻdñ°ŕzéØűzșCìDȐĴĺf®ŽÀľưø@ɜÖÞKĊŇƄ§‚͑těï͡VAġÑÑ»d³öǍÝXĉĕÖ{þĉu¸ËʅğU̎éhɹƆ̗̮ȘNJ֥ड़ࡰţાíϲäʮW¬®ҌeרūȠkɬɻ̼ãüfƠSצɩςåȈHϚÎKdzͲOðÏȆƘ¼CϚǚ࢚˼ФԂ¤ƌžĞ̪Qʤ´¼mȠJˀŸƲÀɠmǐnǔĎȆÞǠN~€ʢĜ‚¶ƌĆĘźʆȬ˪ĚĒ¸ĞGȖƴƀj`ĢçĶāàŃºēĢƒĖćšYŒÀŎüôQÐÂŎŞdžŞêƖš˜oˆDĤÕºÑǘÛˤ³̀gńƘĔÀ^žªƂ`ªt¾äƚêĦĀ¼Ð€Ĕǎ¨Ȕ»͠^ˮÊȦƤøxRrŜH¤¸ÂxDĝŒ|ø˂˜ƮÐ¬ɚwɲFjĔ²Äw°dždÀɞ_ĸdîàŎjʜêTЪŌ‡ŜWÈ|tqĢUB~´°ÎFC•ŽU¼pĀēƄN¦¾O¶ŠłKĊOj“Ě”j´ĜYp˜{¦„ˆSĚÍ\\Tš×ªV–÷Ší¨ÅDK°ßtŇĔKš¨ǵÂcḷ̌ĚǣȄĽF‡lġUĵœŇ‹ȣFʉɁƒMğįʏƶɷØŭOǽ«ƽū¹Ʊő̝Ȩ§ȞʘĖiɜɶʦ}¨֪ࠜ̀ƇǬ¹ǨE˦ĥªÔêFŽxúQ„Er´W„rh¤Ɛ \\talĈDJ˜Ü|[Pll̚¸ƎGú´Pž¬W¦†^¦–H]prR“n|or¾wLVnÇIujkmon£cX^Bh`¥V”„¦U¤¸}€xRj–[^xN[~ªŠxQ„‚[`ªHÆÂExx^wšN¶Ê˜|¨ì†˜€MrœdYp‚oRzNy˜ÀDs~€bcfÌ`L–¾n‹|¾T‚°c¨È¢a‚r¤–`[|òDŞĔöxElÖdH„ÀI`„Ď\\Àì~ƎR¼tf•¦^¢ķ¶e”ÐÚMŒptgj–„ɡČÅyġLû™ŇV®ŠÄÈƀ†Ď°P|ªVV†ªj–¬ĚÒêp¬–E|ŬÂc|ÀtƐK fˆ{ĘFĒœƌXƲąo½Ę‘\\¥–o}›Ûu£ç­kX‘{uĩ«āíÓUŅßŢq€Ť¥lyň[€oi{¦‹L‡ń‡ðFȪȖ”ĒL„¿Ì‹ˆfŒ£K£ʺ™oqNŸƒwğc`ue—tOj×°KJ±qƒÆġm‰Ěŗos¬…qehqsuœƒH{¸kH¡Š…ÊRǪÇƌbȆ¢´ä܍¢NìÉʖ¦â©Ġu¦öČ^â£Ăh–šĖMÈÄw‚\\fŦ°W ¢¾luŸD„wŠ\\̀ʉÌÛM…Ā[bӞEn}¶Vc…ê“sƒ" + ] + ], + "encodeOffsets": [[[129102, 52189]]] + } + }, + { + "type": "Feature", + "id": "210000", + "properties": { + "id": "210000", + "cp": [123.429096, 41.796767], + "name": "辽宁", + "childNum": 16 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@L–Ž@@s™a"], + ["@@MnNm"], + ["@@d‚c"], + ["@@eÀ‚C@b‚“‰"], + ["@@f‡…Xwkbr–Ä`qg"], + ["@@^jtW‘Q"], + ["@@~ Y]c"], + ["@@G`ĔN^_¿Z‚ÃM"], + ["@@iX¶B‹Y"], + ["@@„YƒZ"], + ["@@L_{Epf"], + ["@@^WqCT\\"], + ["@@\\[“‹§t|”¤_"], + ["@@m`n_"], + ["@@Ïxnj{q_×^Giip"], + [ + "@@@œé^B†‡ntˆaÊU—˜Ÿ]x ¯ÄPIJ­°h€ʙK³†VˆÕ@Y~†|EvĹsDŽ¦­L^p²ŸÒG ’Ël]„xxÄ_˜fT¤Ď¤cŽœP„–C¨¸TVjbgH²sdÎdHt`Bˆ—²¬GJję¶[ÐhjeXdlwhšðSȦªVÊπ‹Æ‘Z˜ÆŶ®²†^ŒÎyÅÎcPqń“ĚDMħĜŁH­ˆk„çvV[ij¼W–‚YÀäĦ’‘`XlžR`žôLUVžfK–¢†{NZdĒª’YĸÌÚJRr¸SA|ƴgŴĴÆbvªØX~†źBŽ|¦ÕœEž¤Ð`\\|Kˆ˜UnnI]¤ÀÂĊnŎ™R®Ő¿¶\\ÀøíDm¦ÎbŨab‰œaĘ\\ľã‚¸a˜tÎSƐ´©v\\ÖÚÌǴ¤Â‡¨JKr€Z_Z€fjþhPkx€`Y”’RIŒjJcVf~sCN¤ ˆE‚œhæm‰–sHy¨SðÑÌ\\\\ŸĐRZk°IS§fqŒßýáЍÙÉÖ[^¯ǤŲ„ê´\\¦¬ĆPM¯£Ÿˆ»uïpùzEx€žanµyoluqe¦W^£ÊL}ñrkqWňûP™‰UP¡ôJŠoo·ŒU}£Œ„[·¨@XŒĸŸ“‹‹DXm­Ûݏº‡›GU‹CÁª½{íĂ^cj‡k“¶Ã[q¤“LÉö³cux«zZfƒ²BWÇ®Yß½ve±ÃC•ý£W{Ú^’q^sÑ·¨‹ÍOt“¹·C¥‡GD›rí@wÕKţ݋˜Ÿ«V·i}xËÍ÷‘i©ĝ‡ɝǡ]ƒˆ{c™±OW‹³Ya±Ÿ‰_穂Hžĕoƫ€Ňqƒr³‰Lys[„ñ³¯OS–ďOMisZ†±ÅFC¥Pq{‚Ã[Pg}\\—¿ghćO…•k^ģÁFıĉĥM­oEqqZûěʼn³F‘¦oĵ—hŸÕP{¯~TÍlª‰N‰ßY“Ð{Ps{ÃVU™™eĎwk±ʼnVÓ½ŽJãÇÇ»Jm°dhcÀff‘dF~ˆ€ĀeĖ€d`sx² šƒ®EżĀdQ‹Âd^~ăÔHˆ¦\\›LKpĄVez¤NP ǹӗR™ÆąJSh­a[¦´Âghwm€BÐ¨źhI|žVVŽ—Ž|p] Â¼èNä¶ÜBÖ¼“L`‚¼bØæŒKV”ŸpoœúNZÞÒKxpw|ÊEMnzEQšŽIZ”ŽZ‡NBˆčÚFÜçmĩ‚WĪñt‘ÞĵÇñZ«uD‚±|Əlij¥ãn·±PmÍa‰–da‡ CL‡Ǒkùó¡³Ï«QaċϑOÃ¥ÕđQȥċƭy‹³ÃA" + ] + ], + "encodeOffsets": [ + [[123686, 41445]], + [[126019, 40435]], + [[124393, 40128]], + [[126117, 39963]], + [[125322, 40140]], + [[126686, 40700]], + [[126041, 40374]], + [[125584, 40168]], + [[125453, 40165]], + [[125362, 40214]], + [[125280, 40291]], + [[125774, 39997]], + [[125976, 40496]], + [[125822, 39993]], + [[125509, 40217]], + [[122731, 40949]] + ] + } + }, + { + "type": "Feature", + "id": "220000", + "properties": { "id": "220000", "cp": [125.3245, 43.886841], "name": "吉林", "childNum": 1 }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@‘p䔳PClƒFbbÍzš€wBG’ĭ€Z„Åi“»ƒlY­ċ²SgŽkÇ£—^S‰“qd¯•‹R…©éŽ£¯S†\\cZ¹iűƏCuƍÓX‡oR}“M^o•£…R}oªU­F…uuXHlEŕ‡€Ï©¤ÛmTŽþ¤D–²ÄufàÀ­XXȱAe„yYw¬dvõ´KÊ£”\\rµÄl”iˆdā]|DÂVŒœH¹ˆÞ®ÜWnŒC”Œķ W‹§@\\¸‹ƒ~¤‹Vp¸‰póIO¢ŠVOšŇürXql~òÉK]¤¥Xrfkvzpm¶bwyFoúvð‡¼¤ N°ąO¥«³[ƒéǡű_°Õ\\ÚÊĝŽþâőàerR¨­JYlďQ[ ÏYëЧTGz•tnŠß¡gFkMŸāGÁ¤ia É‰™È¹`\\xs€¬dĆkNnuNUŠ–užP@‚vRY¾•–\\¢…ŒGªóĄ~RãÖÎĢù‚đŴÕhQŽxtcæëSɽʼníëlj£ƍG£nj°KƘµDsØÑpyƸ®¿bXp‚]vbÍZuĂ{nˆ^IüœÀSք”¦EŒvRÎûh@℈[‚Əȉô~FNr¯ôçR±ƒ­HÑl•’Ģ–^¤¢‚OðŸŒævxsŒ]ÞÁTĠs¶¿âƊGW¾ìA¦·TѬ†è¥€ÏÐJ¨¼ÒÖ¼ƒƦɄxÊ~S–tD@ŠĂ¼Ŵ¡jlºWžvЉˆzƦZЎ²CH— „Axiukd‹ŒGgetqmcžÛ£Ozy¥cE}|…¾cZ…k‚‰¿uŐã[oxGikfeäT@…šSUwpiÚFM©’£è^ڟ‚`@v¶eň†f h˜eP¶žt“äOlÔUgƒÞzŸU`lœ}ÔÆUvØ_Ō¬Öi^ĉi§²ÃŠB~¡Ĉ™ÚEgc|DC_Ȧm²rBx¼MÔ¦ŮdĨÃâYx‘ƘDVÇĺĿg¿cwÅ\\¹˜¥Yĭlœ¤žOv†šLjM_a W`zļMž·\\swqÝSA‡š—q‰Śij¯Š‘°kŠRē°wx^Đkǂғ„œž“œŽ„‹\\]˜nrĂ}²ĊŲÒøãh·M{yMzysěnĒġV·°“G³¼XÀ““™¤¹i´o¤ŃšŸÈ`̃DzÄUĞd\\i֚ŒˆmÈBĤÜɲDEh LG¾ƀľ{WaŒYÍȏĢĘÔRîĐj‹}Ǟ“ccj‡oUb½š{“h§Ǿ{K‹ƖµÎ÷žGĀÖŠåưÎs­l›•yiē«‹`姝H¥Ae^§„GK}iã\\c]v©ģZ“mÃ|“[M}ģTɟĵ‘Â`À–çm‰‘FK¥ÚíÁbXš³ÌQґHof{‰]e€pt·GŋĜYünĎųVY^’˜ydõkÅZW„«WUa~U·Sb•wGçǑ‚“iW^q‹F‚“›uNĝ—·Ew„‹UtW·Ýďæ©PuqEzwAV•—XR‰ãQ`­©GŒM‡ehc›c”ďϝd‡©ÑW_ϗYƅŒ»…é\\ƒɹ~ǙG³mØ©BšuT§Ĥ½¢Ã_ý‘L¡‘ýŸqT^rme™\\Pp•ZZbƒyŸ’uybQ—efµ]UhĿDCmûvašÙNSkCwn‰cćfv~…Y‹„ÇG" + ], + "encodeOffsets": [[130196, 42528]] + } + }, + { + "type": "Feature", + "id": "230000", + "properties": { + "id": "230000", + "cp": [128.642464, 46.756967], + "name": "黑龙江", + "childNum": 2 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + "@@UƒµNÿ¥īè灋•HÍøƕ¶LŒǽ|g¨|”™Ža¾pViˆdd”~ÈiŒíďÓQġėǐZ΋ŽXb½|ſÃH½ŸKFgɱCģÛÇA‡n™‹jÕc[VĝDZÃ˄Ç_™ £ń³pŽj£º”š¿”»WH´¯”U¸đĢmžtĜyzzNN|g¸÷äűѱĉā~mq^—Œ[ƒ”››”ƒǁÑďlw]¯xQĔ‰¯l‰’€°řĴrŠ™˜BˆÞTxr[tŽ¸ĻN_yŸX`biN™Ku…P›£k‚ZĮ—¦[ºxÆÀdhŽĹŀUÈƗCw’áZħÄŭcÓ¥»NAw±qȥnD`{ChdÙFćš}¢‰A±Äj¨]ĊÕjŋ«×`VuÓś~_kŷVÝyh„“VkÄãPs”Oµ—fŸge‚Ň…µf@u_Ù ÙcŸªNªÙEojVx™T@†ãSefjlwH\\pŏäÀvŠŽlY†½d{†F~¦dyz¤PÜndsrhf‹HcŒvlwjFœ£G˜±DύƥY‡yϊu¹XikĿ¦ÏqƗǀOŜ¨LI|FRĂn sª|Cš˜zxAè¥bœfudTrFWÁ¹Am|˜ĔĕsķÆF‡´Nš‰}ć…UŠÕ@Áijſmužç’uð^ÊýowŒFzØÎĕNőžǏȎôªÌŒDŽàĀÄ˄ĞŀƒʀĀƘŸˮȬƬĊ°ƒUŸzou‡xe]}Ž…AyȑW¯ÌmK‡“Q]‹Īºif¸ÄX|sZt|½ÚUΠlkš^p{f¤lˆºlÆW –€A²˜PVܜPH”Êâ]ÎĈÌÜk´\\@qàsĔÄQºpRij¼èi†`¶—„bXƒrBgxfv»ŽuUiˆŒ^v~”J¬mVp´£Œ´VWrnP½ì¢BX‚¬h™ŠðX¹^TjVœŠriªj™tŊÄm€tPGx¸bgRšŽsT`ZozÆO]’ÒFô҆Oƒ‡ŊŒvŞ”p’cGŒêŠsx´DR–Œ{A†„EOr°Œ•žx|íœbˆ³Wm~DVjºéNN†Ëܲɶ­GƒxŷCStŸ}]ûō•SmtuÇÃĕN•™āg»šíT«u}ç½BĵÞʣ¥ëÊ¡Mێ³ãȅ¡ƋaǩÈÉQ‰†G¢·lG|›„tvgrrf«†ptęŘnŠÅĢr„I²¯LiØsPf˜_vĠd„xM prʹšL¤‹¤‡eˌƒÀđK“žïÙVY§]I‡óáĥ]ķ†Kˆ¥Œj|pŇ\\kzţ¦šnņäÔVĂîĪ¬|vW’®l¤èØr‚˜•xm¶ă~lÄƯĄ̈́öȄEÔ¤ØQĄ–Ą»ƢjȦOǺ¨ìSŖÆƬy”Qœv`–cwƒZSÌ®ü±DŽ]ŀç¬B¬©ńzƺŷɄeeOĨS’Œfm Ċ‚ƀP̎ēz©Ċ‚ÄÕÊmgŸÇsJ¥ƔˆŊśæ’΁Ñqv¿íUOµª‰ÂnĦÁ_½ä@ê텣P}Ġ[@gġ}g“ɊדûÏWXá¢užƻÌsNͽƎÁ§č՛AēeL³àydl›¦ĘVçŁpśdžĽĺſʃQíÜçÛġԏsĕ¬—Ǹ¯YßċġHµ ¡eå`ļƒrĉŘóƢFì“ĎWøxÊk†”ƈdƬv|–I|·©NqńRŀƒ¤é”eŊœŀ›ˆàŀU²ŕƀB‚Q£Ď}L¹Îk@©ĈuǰųǨ”Ú§ƈnTËÇéƟÊcfčŤ^Xm‡—HĊĕË«W·ċëx³ǔķÐċJā‚wİ_ĸ˜Ȁ^ôWr­°oú¬Ħ…ŨK~”ȰCĐ´Ƕ£’fNÎèâw¢XnŮeÂÆĶŽ¾¾xäLĴĘlļO¤ÒĨA¢Êɚ¨®‚ØCÔ ŬGƠ”ƦYĜ‡ĘÜƬDJ—g_ͥœ@čŅĻA“¶¯@wÎqC½Ĉ»NŸăëK™ďÍQ“Ùƫ[«Ãí•gßÔÇOÝáW‘ñuZ“¯ĥ€Ÿŕā¡ÑķJu¤E Ÿå¯°WKɱ_d_}}vyŸõu¬ï¹ÓU±½@gÏ¿rýD‰†g…Cd‰µ—°MFYxw¿CG£‹Rƛ½Õ{]L§{qqąš¿BÇƻğëšܭNJË|c²}Fµ}›ÙRsÓpg±ŠQNqǫŋRwŕnéÑÉKŸ†«SeYR…ŋ‹@{¤SJ}šD Ûǖ֍Ÿ]gr¡µŷjqWÛham³~S«“„›Þ]" + ] + ], + "encodeOffsets": [[[134456, 44547]]] + } + }, + { + "type": "Feature", + "id": "320000", + "properties": { + "id": "320000", + "cp": [119.767413, 33.041544], + "name": "江苏", + "childNum": 1 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@cþÅPiŠ`ZŸRu¥É\\]~°ŽY`µ†Óƒ^phÁbnÀşúŽòa–ĬºTÖŒb‚˜e¦¦€{¸ZâćNpŒ©žHr|^ˆmjhŠSEb\\afv`sz^lkŽlj‹Ätg‹¤D˜­¾Xš¿À’|ДiZ„ȀåB·î}GL¢õcßjaŸyBFµÏC^ĭ•cÙt¿sğH]j{s©HM¢ƒQnDÀ©DaÜތ·jgàiDbPufjDk`dPOîƒhw¡ĥ‡¥šG˜ŸP²ĐobºrY†„î¶aHŢ´ ]´‚rılw³r_{£DB_Ûdåuk|ˆŨ¯F Cºyr{XFy™e³Þċ‡¿Â™kĭB¿„MvÛpm`rÚã”@Ę¹hågËÖƿxnlč¶Åì½Ot¾dJlŠVJʜǀœŞqvnOŠ^ŸJ”Z‘ż·Q}ê͎ÅmµÒ]Žƍ¦Dq}¬R^èĂ´ŀĻĊIԒtžIJyQŐĠMNtœR®òLh‰›Ěs©»œ}OӌGZz¶A\\jĨFˆäOĤ˜HYš†JvÞHNiÜaϚɖnFQlšNM¤ˆB´ĄNöɂtp–Ŭdf先‹qm¿QûŠùއÚb¤uŃJŴu»¹Ą•lȖħŴw̌ŵ²ǹǠ͛hĭłƕrçü±Y™xci‡tğ®jű¢KOķ•Coy`å®VTa­_Ā]ŐÝɞï²ʯÊ^]afYǸÃĆēĪȣJđ͍ôƋĝÄ͎ī‰çÛɈǥ£­ÛmY`ó£Z«§°Ó³QafusNıDž_k}¢m[ÝóDµ—¡RLčiXy‡ÅNïă¡¸iĔϑNÌŕoēdōîåŤûHcs}~Ûwbù¹£¦ÓCt‹OPrƒE^ÒoŠg™ĉIµžÛÅʹK…¤½phMŠü`o怆ŀ" + ], + "encodeOffsets": [[121740, 32276]] + } + }, + { + "type": "Feature", + "id": "330000", + "properties": { + "id": "330000", + "cp": [120.153576, 29.287459], + "name": "浙江", + "childNum": 45 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@E^dQ]K"], + ["@@jX^j‡"], + ["@@sfŠbU‡"], + ["@@qP\\xz[ck"], + ["@@‘Rƒ¢‚FX}°[s_"], + ["@@Cbœ\\—}"], + ["@@e|v\\la{u"], + ["@@v~u}"], + ["@@QxÂF¯}"], + ["@@¹nŒvÞs¯o"], + ["@@rSkUEj"], + ["@@bi­ZŒP"], + ["@@p[}INf"], + ["@@À¿€"], + ["@@¹dnbŒ…"], + ["@@rSŸBnR"], + ["@@g~h}"], + ["@@FlEk"], + ["@@OdPc"], + ["@@v[u\\"], + ["@@FjâL~wyoo~›sµL–\\"], + ["@@¬e¹aNˆ"], + ["@@\\nÔ¡q]L³ë\\ÿ®ŒQ֎"], + ["@@ÊA­©[¬"], + ["@@KxŒv­"], + ["@@@hlIk]"], + ["@@pW{o||j"], + ["@@Md|_mC"], + ["@@¢…X£ÏylD¼XˆtH"], + ["@@hlÜ[LykAvyfw^Ež›¤"], + ["@@fp¤Mus“R"], + ["@@®_ma~•LÁ¬šZ"], + ["@@iM„xZ"], + ["@@ZcYd"], + ["@@Z~dOSo|A¿qZv"], + ["@@@`”EN¡v"], + ["@@|–TY{"], + ["@@@n@m"], + ["@@XWkCT\\"], + ["@@ºwšZRkĕWO¢"], + ["@@™X®±Grƪ\\ÔáXq{‹"], + ["@@ůTG°ĄLHm°UC‹"], + [ + "@@¤Ž€aÜx~}dtüGæţŎíĔcŖpMËВjē¢·ðĄÆMzˆjWKĎ¢Q¶˜À_꒔_Bı€i«pZ€gf€¤Nrq]§ĂN®«H±‡yƳí¾×ŸīàLłčŴǝĂíÀBŖÕªˆŠÁŖHŗʼnåqûõi¨hÜ·ƒñt»¹ýv_[«¸m‰YL¯‰Qª…mĉÅdMˆ•gÇjcº«•ęœ¬­K­´ƒB«Âącoċ\\xKd¡gěŧ«®á’[~ıxu·Å”KsËɏc¢Ù\\ĭƛëbf¹­ģSƒĜkáƉÔ­ĈZB{ŠaM‘µ‰fzʼnfåÂŧįƋǝÊĕġć£g³ne­ą»@­¦S®‚\\ßðCšh™iqªĭiAu‡A­µ”_W¥ƣO\\lċĢttC¨£t`ˆ™PZäuXßBs‡Ļyek€OđġĵHuXBšµ]׌‡­­\\›°®¬F¢¾pµ¼kŘó¬Wät’¸|@ž•L¨¸µr“ºù³Ù~§WI‹ŸZWŽ®’±Ð¨ÒÉx€`‰²pĜ•rOògtÁZ}þÙ]„’¡ŒŸFK‚wsPlU[}¦Rvn`hq¬\\”nQ´ĘRWb”‚_ rtČFI֊kŠŠĦPJ¶ÖÀÖJĈĄTĚòžC ²@Pú…Øzœ©PœCÈڜĒ±„hŖ‡l¬â~nm¨f©–iļ«m‡nt–u†ÖZÜÄj“ŠLŽ®E̜Fª²iÊxبžIÈhhst" + ], + ["@@o\\V’zRZ}y"], + ["@@†@°¡mۛGĕ¨§Ianá[ýƤjfæ‡ØL–•äGr™"] + ], + "encodeOffsets": [ + [[125592, 31553]], + [[125785, 31436]], + [[125729, 31431]], + [[125513, 31380]], + [[125223, 30438]], + [[125115, 30114]], + [[124815, 29155]], + [[124419, 28746]], + [[124095, 28635]], + [[124005, 28609]], + [[125000, 30713]], + [[125111, 30698]], + [[125078, 30682]], + [[125150, 30684]], + [[124014, 28103]], + [[125008, 31331]], + [[125411, 31468]], + [[125329, 31479]], + [[125626, 30916]], + [[125417, 30956]], + [[125254, 30976]], + [[125199, 30997]], + [[125095, 31058]], + [[125083, 30915]], + [[124885, 31015]], + [[125218, 30798]], + [[124867, 30838]], + [[124755, 30788]], + [[124802, 30809]], + [[125267, 30657]], + [[125218, 30578]], + [[125200, 30562]], + [[124968, 30474]], + [[125167, 30396]], + [[124955, 29879]], + [[124714, 29781]], + [[124762, 29462]], + [[124325, 28754]], + [[123990, 28459]], + [[125366, 31477]], + [[125115, 30363]], + [[125369, 31139]], + [[122495, 31878]], + [[125329, 30690]], + [[125192, 30787]] + ] + } + }, + { + "type": "Feature", + "id": "340000", + "properties": { "id": "340000", "cp": [117.283042, 31.26119], "name": "安徽", "childNum": 3 }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@^iuLX^"], + ["@@‚e©Ehl"], + [ + "@@°ZÆëϵmkǀwÌÕæhºgBĝâqÙĊz›ÖgņtÀÁÊÆá’hEz|WzqD¹€Ÿ°E‡ŧl{ævÜcA`¤C`|´qžxIJkq^³³ŸGšµbƒíZ…¹qpa±ď OH—¦™Ħˆx¢„gPícOl_iCveaOjCh߸i݋bÛªCC¿€m„RV§¢A|t^iĠGÀtÚs–d]ĮÐDE¶zAb àiödK¡~H¸íæAžǿYƒ“j{ď¿‘™À½W—®£ChŒÃsiŒkkly]_teu[bFa‰Tig‡n{]Gqªo‹ĈMYá|·¥f¥—őaSÕė™NµñĞ«ImŒ_m¿Âa]uĜp …Z_§{Cƒäg¤°r[_Yj‰ÆOdý“[ŽI[á·¥“Q_n‡ùgL¾mv™ˊBÜƶĊJhšp“c¹˜O]iŠ]œ¥ jtsggJǧw×jÉ©±›EFˍ­‰Ki”ÛÃÕYv…s•ˆm¬njĻª•§emná}k«ŕˆƒgđ²Ù›DǤ›í¡ªOy›†×Où±@DŸñSęćăÕIÕ¿IµĥO‰‰jNÕËT¡¿tNæŇàåyķrĕq§ÄĩsWÆߎF¶žX®¿‰mŒ™w…RIޓfßoG‘³¾©uyH‘į{Ɓħ¯AFnuP…ÍÔzšŒV—dàôº^Ðæd´€‡oG¤{S‰¬ćxã}›ŧ×Kǥĩ«žÕOEзÖdÖsƘѨ[’Û^Xr¢¼˜§xvěƵ`K”§ tÒ´Cvlo¸fzŨð¾NY´ı~ÉĔē…ßúLÃϖ_ÈÏ|]ÂÏFl”g`bšežž€n¾¢pU‚h~ƴĖ¶_‚r sĄ~cž”ƈ]|r c~`¼{À{ȒiJjz`îÀT¥Û³…]’u}›f…ïQl{skl“oNdŸjŸäËzDvčoQŠďHI¦rb“tHĔ~BmlRš—V_„ħTLnñH±’DžœL‘¼L˜ªl§Ťa¸ŒĚlK²€\\RòvDcÎJbt[¤€D@®hh~kt°ǾzÖ@¾ªdb„YhüóZ ň¶vHrľ\\ʗJuxAT|dmÀO„‹[ÃԋG·ĚąĐlŪÚpSJ¨ĸˆLvÞcPæķŨŽ®mАˆálŸwKhïgA¢ųƩޖ¤OȜm’°ŒK´" + ] + ], + "encodeOffsets": [[[121722, 32278]], [[119475, 30423]], [[119168, 35472]]] + } + }, + { + "type": "Feature", + "id": "350000", + "properties": { + "id": "350000", + "cp": [118.306239, 26.075302], + "name": "福建", + "childNum": 18 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@“zht´‡]"], + ["@@aj^~ĆG—©O"], + ["@@ed¨„C}}i"], + ["@@@vˆPGsQ"], + ["@@‰sBz‚ddW]Q"], + ["@@SŽ¨Q“{"], + ["@@NŽVucW"], + ["@@qptBAq"], + ["@@‰’¸[mu"], + ["@@Q\\pD]_"], + ["@@jSwUadpF"], + ["@@eXª~ƒ•"], + ["@@AjvFso"], + ["@@fT–›_Çí\\Ÿ™—v|ba¦jZÆy€°"], + ["@@IjJi"], + ["@@wJI€ˆxš«¼AoNe{M­"], + ["@@K‰±¡Óˆ”ČäeZ"], + [ + "@@k¡¹Eh~c®wBk‹UplÀ¡I•~Māe£bN¨gZý¡a±Öcp©PhžI”Ÿ¢Qq…ÇGj‹|¥U™ g[Ky¬ŏ–v@OpˆtÉEŸF„\\@ åA¬ˆV{Xģ‰ĐBy…cpě…¼³Ăp·¤ƒ¥o“hqqÚ¡ŅLsƒ^ᗞ§qlŸÀhH¨MCe»åÇGD¥zPO£čÙkJA¼ß–ėu›ĕeûҍiÁŧSW¥˜QŠûŗ½ùěcݧSùĩąSWó«íęACµ›eR—åǃRCÒÇZÍ¢‹ź±^dlsŒtjD¸•‚ZpužÔâÒH¾oLUêÃÔjjēò´ĄW‚ƛ…^Ñ¥‹ĦŸ@Çò–ŠmŒƒOw¡õyJ†yD}¢ďÑÈġfŠZd–a©º²z£šN–ƒjD°Ötj¶¬ZSÎ~¾c°¶Ðm˜x‚O¸¢Pl´žSL|¥žA†ȪĖM’ņIJg®áIJČĒü` ŽQF‡¬h|ÓJ@zµ |ê³È ¸UÖŬŬÀEttĸr‚]€˜ðŽM¤ĶIJHtÏ A’†žĬkvsq‡^aÎbvŒd–™fÊòSD€´Z^’xPsÞrv‹ƞŀ˜jJd×ŘÉ ®A–ΦĤd€xĆqAŒ†ZR”ÀMźŒnĊ»ŒİÐZ— YX–æJŠyĊ²ˆ·¶q§·–K@·{s‘Xãô«lŗ¶»o½E¡­«¢±¨Yˆ®Ø‹¶^A™vWĶGĒĢžPlzfˆļŽtàAvWYãšO_‡¤sD§ssČġ[kƤPX¦Ž`¶“ž®ˆBBvĪjv©šjx[L¥àï[F…¼ÍË»ğV`«•Ip™}ccÅĥZE‹ãoP…´B@ŠD—¸m±“z«Ƴ—¿å³BRضˆœWlâþäą`“]Z£Tc— ĹGµ¶H™m@_©—kŒ‰¾xĨ‡ôȉðX«½đCIbćqK³Á‹Äš¬OAwã»aLʼn‡ËĥW[“ÂGI—ÂNxij¤D¢ŽîĎÎB§°_JœGsƒ¥E@…¤uć…P‘å†cuMuw¢BI¿‡]zG¹guĮck\\_" + ] + ], + "encodeOffsets": [ + [[123250, 27563]], + [[122541, 27268]], + [[123020, 27189]], + [[122916, 27125]], + [[122887, 26845]], + [[122808, 26762]], + [[122568, 25912]], + [[122778, 26197]], + [[122515, 26757]], + [[122816, 26587]], + [[123388, 27005]], + [[122450, 26243]], + [[122578, 25962]], + [[121255, 25103]], + [[120987, 24903]], + [[122339, 25802]], + [[121042, 25093]], + [[122439, 26024]] + ] + } + }, + { + "type": "Feature", + "id": "360000", + "properties": { + "id": "360000", + "cp": [115.592151, 27.676493], + "name": "江西", + "childNum": 1 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@ĢĨƐgļˆ¼ÂMD~ņªe^\\^§„ý©j׍cZ†Ø¨zdÒa¶ˆlҍJŒìõ`oz÷@¤u޸´†ôęöY¼‰HČƶajlÞƩ¥éZ[”|h}^U Œ ¥p„ĄžƦO lt¸Æ €Q\\€ŠaÆ|CnÂOjt­ĚĤd’ÈŒF`’¶„@Ð딠¦ōҞ¨Sêv†HĢûXD®…QgėWiØPÞìºr¤dž€NĠ¢l–•ĄtZoœCƞÔºCxrpĠV®Ê{f_Y`_ƒeq’’®Aot`@o‚DXfkp¨|Šs¬\\D‘ÄSfè©Hn¬…^DhÆyøJh“ØxĢĀLʈ„ƠPżċĄwȠ̦G®ǒĤäTŠÆ~ĦwŠ«|TF¡Šn€c³Ïå¹]ĉđxe{ÎӐ†vOEm°BƂĨİ|G’vz½ª´€H’àp”eJ݆Qšxn‹ÀŠW­žEµàXÅĪt¨ÃĖrÄwÀFÎ|ňÓMå¼ibµ¯»åDT±m[“r«_gŽmQu~¥V\\OkxtL E¢‹ƒ‘Ú^~ýê‹Pó–qo슱_Êw§ÑªåƗā¼‹mĉŹ‹¿NQ“…YB‹ąrwģcÍ¥B•Ÿ­ŗÊcØiI—žƝĿuŒqtāwO]‘³YCñTeɕš‹caub͈]trlu€ī…B‘ПGsĵıN£ï—^ķqss¿FūūV՟·´Ç{éĈý‰ÿ›OEˆR_ŸđûIċâJh­ŅıN‘ȩĕB…¦K{Tk³¡OP·wn—µÏd¯}½TÍ«YiµÕsC¯„iM•¤™­•¦¯P|ÿUHv“he¥oFTu‰õ\\ŽOSs‹MòđƇiaºćXŸĊĵà·çhƃ÷ǜ{‘ígu^›đg’m[×zkKN‘¶Õ»lčÓ{XSƉv©_ÈëJbVk„ĔVÀ¤P¾ºÈMÖxlò~ªÚàGĂ¢B„±’ÌŒK˜y’áV‡¼Ã~­…`g›ŸsÙfI›Ƌlę¹e|–~udjˆuTlXµf`¿JdŠ[\\˜„L‚‘²" + ], + "encodeOffsets": [[116689, 26234]] + } + }, + { + "type": "Feature", + "id": "370000", + "properties": { + "id": "370000", + "cp": [118.000923, 36.275807], + "name": "山东", + "childNum": 13 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@Xjd]{K"], + ["@@itbFHy"], + ["@@HlGk"], + ["@@T‚ŒGŸy"], + ["@@K¬˜•‹U"], + ["@@WdXc"], + ["@@PtOs"], + ["@@•LnXhc"], + ["@@ppVƒu]Or"], + ["@@cdzAUa"], + ["@@udRhnCI‡"], + ["@@ˆoIƒpR„"], + [ + "@@Ľč{fzƤî’Kš–ÎMĮ]†—ZFˆ½Y]â£ph’™š¶¨râøÀ†ÎǨ¤^ºÄ”Gzˆ~grĚĜlĞƄLĆdž¢Îo¦–cv“Kb€gr°Wh”mZp ˆL]LºcU‰Æ­n”żĤÌĒœbAnrOAœ´žȊcÀbƦUØrĆUÜøœĬƞ†š˜Ez„VL®öØBkŖÝĐĖ¹ŧ̄±ÀbÎɜnb²ĦhņBĖ›žįĦåXćì@L¯´ywƕCéõė ƿ¸‘lµ¾Z|†ZWyFYŸ¨Mf~C¿`€à_RÇzwƌfQnny´INoƬˆèôº|sT„JUš›‚L„îVj„ǎ¾Ē؍‚Dz²XPn±ŴPè¸ŔLƔÜƺ_T‘üÃĤBBċȉöA´fa„˜M¨{«M`‡¶d¡ô‰Ö°šmȰBÔjjŒ´PM|”c^d¤u•ƒ¤Û´Œä«ƢfPk¶Môlˆ]Lb„}su^ke{lC‘…M•rDŠÇ­]NÑFsmoõľH‰yGă{{çrnÓE‰‹ƕZGª¹Fj¢ïW…uøCǷ돡ąuhÛ¡^Kx•C`C\\bÅxì²ĝÝ¿_N‰īCȽĿåB¥¢·IŖÕy\\‡¹kx‡Ã£Č×GDyÕ¤ÁçFQ¡„KtŵƋ]CgÏAùSed‡cÚź—ŠuYfƒyMmhUWpSyGwMPqŀ—›Á¼zK›¶†G•­Y§Ëƒ@–´śÇµƕBmœ@Io‚g——Z¯u‹TMx}C‘‰VK‚ï{éƵP—™_K«™pÛÙqċtkkù]gŽ‹Tğwo•ɁsMõ³ă‡AN£™MRkmEʕč™ÛbMjÝGu…IZ™—GPģ‡ãħE[iµBEuŸDPԛ~ª¼ętŠœ]ŒûG§€¡QMsğNPŏįzs£Ug{đJĿļā³]ç«Qr~¥CƎÑ^n¶ÆéÎR~Ż¸Y’I“] P‰umŝrƿ›‰›Iā‹[x‰edz‹L‘¯v¯s¬ÁY…~}…ťuŁŒg›ƋpÝĄ_ņī¶ÏSR´ÁP~ž¿Cyžċßdwk´Ss•X|t‰`Ä Èð€AªìÎT°¦Dd–€a^lĎDĶÚY°Ž`ĪŴǒˆ”àŠv\\ebŒZH„ŖR¬ŢƱùęO•ÑM­³FۃWp[ƒ" + ] + ], + "encodeOffsets": [ + [[123806, 39303]], + [[123821, 39266]], + [[123742, 39256]], + [[123702, 39203]], + [[123649, 39066]], + [[123847, 38933]], + [[123580, 38839]], + [[123894, 37288]], + [[123043, 36624]], + [[123344, 38676]], + [[123522, 38857]], + [[123628, 38858]], + [[118260, 36742]] + ] + } + }, + { + "type": "Feature", + "id": "410000", + "properties": { + "id": "410000", + "cp": [113.665412, 33.757975], + "name": "河南", + "childNum": 1 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@•ýL™ùµP³swIÓxcŢĞð†´E®žÚPt†ĴXØx¶˜@«ŕŕQGƒ‹Yfa[şu“ßǩ™đš_X³ijÕčC]kbc•¥CS¯ëÍB©÷‹–³­Siˆ_}m˜YTtž³xlàcȂzÀD}ÂOQ³ÐTĨ¯†ƗòËŖ[hœł‹Ŧv~††}ÂZž«¤lPǕ£ªÝŴÅR§ØnhcŒtâk‡nύ­ľŹUÓÝdKuķ‡I§oTũÙďkęĆH¸ÓŒ\\ăŒ¿PcnS{wBIvɘĽ[GqµuŸŇôYgûƒZcaŽ©@½Õǽys¯}lgg@­C\\£as€IdÍuCQñ[L±ęk·‹ţb¨©kK—’»›KC²‘òGKmĨS`ƒ˜UQ™nk}AGē”sqaJ¥ĐGR‰ĎpCuÌy ã iMc”plk|tRk†ðœev~^‘´†¦ÜŽSí¿_iyjI|ȑ|¿_»d}qŸ^{“Ƈdă}Ÿtqµ`Ƴĕg}V¡om½fa™Ço³TTj¥„tĠ—Ry”K{ùÓjuµ{t}uËR‘iŸvGŠçJFjµŠÍyqΘàQÂFewixGw½Yŷpµú³XU›½ġy™łå‰kÚwZXˆ·l„¢Á¢K”zO„Λ΀jc¼htoDHr…|­J“½}JZ_¯iPq{tę½ĕ¦Zpĵø«kQ…Ťƒ]MÛfaQpě±ǽ¾]u­Fu‹÷nƒ™čįADp}AjmcEǒaª³o³ÆÍSƇĈÙDIzˑ赟^ˆKLœ—i—Þñ€[œƒaA²zz‰Ì÷Dœ|[šíijgf‚ÕÞd®|`ƒĆ~„oĠƑô³Ŋ‘D×°¯CsŠøÀ«ì‰UMhTº¨¸ǡîS–Ô„DruÂÇZ•ÖEŽ’vPZ„žW”~؋ÐtĄE¢¦Ðy¸bŠô´oŬ¬Ž²Ês~€€]®tªašpŎJ¨Öº„_ŠŔ–`’Ŗ^Ѝ\\Ĝu–”~m²Ƹ›¸fW‰ĦrƔ}Î^gjdfÔ¡J}\\n C˜¦þWxªJRÔŠu¬ĨĨmF†dM{\\d\\ŠYÊ¢ú@@¦ª²SŠÜsC–}fNècbpRmlØ^g„d¢aÒ¢CZˆZxvÆ¶N¿’¢T@€uCœ¬^ĊðÄn|žlGl’™Rjsp¢ED}€Fio~ÔNŽ‹„~zkĘHVsDzßjƒŬŒŠŢ`Pûàl¢˜\\ÀœEhŽİgÞē X¼Pk–„|m" + ], + "encodeOffsets": [[118256, 37017]] + } + }, + { + "type": "Feature", + "id": "420000", + "properties": { + "id": "420000", + "cp": [113.298572, 30.684355], + "name": "湖北", + "childNum": 3 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@AB‚"], + ["@@lskt"], + [ + "@@¾«}{ra®pîÃ\\™›{øCŠËyyB±„b\\›ò˜Ý˜jK›‡L ]ĎĽÌ’JyÚCƈćÎT´Å´pb©È‘dFin~BCo°BĎĚømvŒ®E^vǾ½Ĝ²Ro‚bÜeNŽ„^ĺ£R†¬lĶ÷YoĖ¥Ě¾|sOr°jY`~I”¾®I†{GqpCgyl{‡£œÍƒÍyPL“¡ƒ¡¸kW‡xYlÙ抚ŁĢzœ¾žV´W¶ùŸo¾ZHxjwfx„GNÁ•³Xéæl¶‰EièIH‰ u’jÌQ~v|sv¶Ôi|ú¢Fh˜Qsğ¦ƒSiŠBg™ÐE^ÁÐ{–čnOÂȞUÎóĔ†ÊēIJ}Z³½Mŧïeyp·uk³DsѨŸL“¶_œÅuèw»—€¡WqÜ]\\‘Ò§tƗcÕ¸ÕFÏǝĉăxŻČƟO‡ƒKÉġÿ×wg”÷IÅzCg†]m«ªGeçÃTC’«[‰t§{loWeC@ps_Bp‘­r‘„f_``Z|ei¡—oċMqow€¹DƝӛDYpûs•–‹Ykıǃ}s¥ç³[§ŸcYŠ§HK„«Qy‰]¢“wwö€¸ïx¼ņ¾Xv®ÇÀµRĠЋžHMž±cÏd„ƒǍũȅȷ±DSyúĝ£ŤĀàtÖÿï[îb\\}pĭÉI±Ñy…¿³x¯N‰o‰|¹H™ÏÛm‹júË~Tš•u˜ęjCöAwě¬R’đl¯ Ñb­‰ŇT†Ŀ_[Œ‘IčĄʿnM¦ğ\\É[T·™k¹œ©oĕ@A¾w•ya¥Y\\¥Âaz¯ãÁ¡k¥ne£Ûw†E©Êō¶˓uoj_Uƒ¡cF¹­[Wv“P©w—huÕyBF“ƒ`R‹qJUw\\i¡{jŸŸEPïÿ½fć…QÑÀQ{ž‚°‡fLԁ~wXg—ītêݾ–ĺ‘Hdˆ³fJd]‹HJ²…E€ƒoU¥†HhwQsƐ»Xmg±çve›]Dm͂PˆoCc¾‹_h”–høYrŊU¶eD°Č_N~øĹĚ·`z’]Äþp¼…äÌQŒv\\rCŒé¾TnkžŐڀÜa‡“¼ÝƆĢ¶Ûo…d…ĔňТJq’Pb ¾|JŒ¾fXŠƐîĨ_Z¯À}úƲ‹N_ĒĊ^„‘ĈaŐyp»CÇĕKŠšñL³ŠġMŒ²wrIÒŭxjb[œžn«øœ˜—æˆàƒ ^²­h¯Ú€ŐªÞ¸€Y²ĒVø}Ā^İ™´‚LŠÚm„¥ÀJÞ{JVŒųÞŃx×sxxƈē ģMř–ÚðòIf–Ċ“Œ\\Ʈ±ŒdʧĘD†vČ_Àæ~DŒċ´A®µ†¨ØLV¦êHÒ¤" + ] + ], + "encodeOffsets": [[[113712, 34000]], [[115612, 30507]], [[113649, 34054]]] + } + }, + { + "type": "Feature", + "id": "430000", + "properties": { "id": "430000", "cp": [111.782279, 28.09409], "name": "湖南", "childNum": 3 }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@—n„FTs"], + ["@@ßÅÆችÔXr—†CO™“…ËR‘ïÿĩ­TooQyšÓ[‹ŅBE¬–ÎÓXa„į§Ã¸G °ITxp‰úxÚij¥Ïš–Ģ¾ŠedžÄ©ĸG…œàGh‚€M¤–Â_U}Ċ}¢pczfŠþg¤€”ÇòAV‘‹M"], + [ + "@@©K—ƒA·³CQ±Á«³BUŠƑ¹AŠtćOw™D]ŒJiØSm¯b£‘ylƒ›X…HËѱH•«–‘C^õľA–Å§¤É¥„ïyuǙuA¢^{ÌC´­¦ŷJ£^[†“ª¿‡ĕ~•Ƈ…•N… skóā‡¹¿€ï]ă~÷O§­@—Vm¡‹Qđ¦¢Ĥ{ºjԏŽŒª¥nf´•~ÕoŸž×Ûą‹MąıuZœmZcÒ IJĪ²SÊDŽŶ¨ƚƒ’CÖŎªQؼrŭŽ­«}NÏürʬŒmjr€@ĘrTW ­SsdHzƓ^ÇÂyUi¯DÅYlŹu{hTœ}mĉ–¹¥ě‰Dÿë©ıÓ[Oº£ž“¥ót€ł¹MՄžƪƒ`Pš…Di–ÛUŠ¾Å‌ìˆU’ñB“È£ýhe‰dy¡oċ€`pfmjP~‚kZa…ZsÐd°wj§ƒ@€Ĵ®w~^‚kÀÅKvNmX\\¨a“”сqvíó¿F„¤¡@ũÑVw}S@j}¾«pĂr–ªg àÀ²NJ¶¶Dô…K‚|^ª†Ž°LX¾ŴäPĪ±œ£EXd›”^¶›IJÞܓ~‘u¸ǔ˜Ž›MRhsR…e†`ÄofIÔ\\Ø  i”ćymnú¨cj ¢»–GČìƊÿШXeĈĀ¾Oð Fi ¢|[jVxrIQŒ„_E”zAN¦zLU`œcªx”OTu RLÄ¢dV„i`p˔vŎµªÉžF~ƒØ€d¢ºgİàw¸Áb[¦Zb¦–z½xBĖ@ªpº›šlS¸Ö\\Ĕ[N¥ˀmĎă’J\\‹ŀ`€…ňSڊĖÁĐiO“Ĝ«BxDõĚiv—ž–S™Ì}iùŒžÜnšÐºGŠ{Šp°M´w†ÀÒzJ²ò¨ oTçüöoÛÿñŽőФ‚ùTz²CȆȸǎŪƒƑÐc°dPÎŸğ˶[Ƚu¯½WM¡­Éž“’B·rížnZŸÒ `‡¨GA¾\\pē˜XhÆRC­üWGġu…T靧Ŏѝ©ò³I±³}_‘‹EÃħg®ęisÁPDmÅ{‰b[Rşs·€kPŸŽƥƒóRo”O‹ŸVŸ~]{g\\“êYƪ¦kÝbiċƵŠGZ»Ěõ…ó·³vŝž£ø@pyö_‹ëŽIkѵ‡bcѧy…×dY؎ªiþž¨ƒ[]f]Ņ©C}ÁN‡»hĻħƏ’ĩ" + ] + ], + "encodeOffsets": [[[115640, 30489]], [[112543, 27312]], [[116690, 26230]]] + } + }, + { + "type": "Feature", + "id": "440000", + "properties": { + "id": "440000", + "cp": [113.280637, 23.125178], + "name": "广东", + "childNum": 24 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@QdˆAua"], + ["@@ƒlxDLo"], + ["@@sbhNLo"], + ["@@Ă āŸ"], + ["@@WltO[["], + ["@@Krœ]S"], + ["@@e„„I]y"], + ["@@I|„Mym"], + ["@@ƒÛ³LSŒž¼Y"], + ["@@nvºB–ëui©`¾"], + ["@@zdšÛ›Jw®"], + ["@@†°…¯"], + ["@@a yAª¸ËJIx،@€ĀHAmßV¡o•fu•o"], + ["@@šs‰ŗÃÔėAƁ›ZšÄ ~°ČP‚‹äh"], + ["@@‹¶Ý’Ì‚vmĞh­ı‡Q"], + ["@@HœŠdSjĒ¢D}war…“u«ZqadYM"], + ["@@elŒ\\LqqU"], + ["@@~rMo\\"], + ["@@f„^ƒC"], + ["@@øPªoj÷ÍÝħXČx”°Q¨ıXNv"], + ["@@gÇƳˆŽˆ”oˆŠˆ[~tly"], + ["@@E–ÆC¿‘"], + ["@@OŽP"], + [ + "@@w‹†đóg‰™ĝ—[³‹¡VÙæÅöM̳¹pÁaËýý©D©Ü“JŹƕģGą¤{Ùū…ǘO²«BƱéA—Ò‰ĥ‡¡«BhlmtÃPµyU¯uc“d·w_bŝcīímGOŽ|KP’ȏ‡ŹãŝIŕŭŕ@Óoo¿ē‹±ß}Ž…ŭ‚ŸIJWÈCőâUâǙI›ğʼn©I›ijEׅÁ”³Aó›wXJþ±ÌŒÜӔĨ£L]ĈÙƺZǾĆĖMĸĤfŒÎĵl•ŨnȈ‘ĐtF”Š–FĤ–‚êk¶œ^k°f¶gŠŽœ}®Fa˜f`vXŲxl˜„¦–ÔÁ²¬ÐŸ¦pqÊ̲ˆi€XŸØRDÎ}†Ä@ZĠ’s„x®AR~®ETtĄZ†–ƈfŠŠHâÒÐA†µ\\S¸„^wĖkRzŠalŽŜ|E¨ÈNĀňZTŒ’pBh£\\ŒĎƀuXĖtKL–¶G|Ž»ĺEļĞ~ÜĢÛĊrˆO˜Ùîvd]nˆ¬VœÊĜ°R֟pM††–‚ƂªFbwžEÀˆ˜©Œž\\…¤]ŸI®¥D³|ˎ]CöAŤ¦…æ’´¥¸Lv¼€•¢ĽBaô–F~—š®²GÌҐEY„„œzk¤’°ahlV՞I^‹šCxĈPŽsB‰ƒºV‰¸@¾ªR²ĨN]´_eavSi‡vc•}p}Đ¼ƌkJœÚe thœ†_¸ ºx±ò_xN›Ë‹²‘@ƒă¡ßH©Ùñ}wkNÕ¹ÇO½¿£ĕ]ly_WìIžÇª`ŠuTÅxYĒÖ¼k֞’µ‚MžjJÚwn\\h‘œĒv]îh|’È›Ƅøègž¸Ķß ĉĈWb¹ƀdéʌNTtP[ŠöSvrCZžžaGuœbo´ŖÒÇА~¡zCI…özx¢„Pn‹•‰Èñ @ŒĥÒ¦†]ƞŠV}³ăĔñiiÄÓVépKG½Ä‘ÓávYo–C·sit‹iaÀy„ŧΡÈYDÑům}‰ý|m[węõĉZÅxUO}÷N¹³ĉo_qtă“qwµŁYلǝŕ¹tïÛUïmRCº…ˆĭ|µ›ÕÊK™½R‘ē ó]‘–GªęAx–»HO£|ām‡¡diď×YïYWªʼnOeÚtĐ«zđ¹T…ā‡úE™á²\\‹ķÍ}jYàÙÆſ¿Çdğ·ùTßÇţʄ¡XgWÀLJğ·¿ÃˆOj YÇ÷Qě‹i" + ] + ], + "encodeOffsets": [ + [[117381, 22988]], + [[116552, 22934]], + [[116790, 22617]], + [[116973, 22545]], + [[116444, 22536]], + [[116931, 22515]], + [[116496, 22490]], + [[116453, 22449]], + [[113301, 21439]], + [[118726, 21604]], + [[118709, 21486]], + [[113210, 20816]], + [[115482, 22082]], + [[113171, 21585]], + [[113199, 21590]], + [[115232, 22102]], + [[115739, 22373]], + [[115134, 22184]], + [[113056, 21175]], + [[119573, 21271]], + [[119957, 24020]], + [[115859, 22356]], + [[116561, 22649]], + [[116285, 22746]] + ] + } + }, + { + "type": "Feature", + "id": "450000", + "properties": { "id": "450000", "cp": [108.320004, 22.82402], "name": "广西", "childNum": 2 }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@H– TQ§•A"], + [ + "@@ĨʪƒLƒƊDÎĹĐCǦė¸zÚGn£¾›rªŀÜt¬@֛ڈSx~øOŒ˜ŶÐÂæȠ\\„ÈÜObĖw^oބLf¬°bI lTØB̈F£Ć¹gñĤaY“t¿¤VSñœK¸¤nM†¼‚JE±„½¸šŠño‹ÜCƆæĪ^ŠĚQÖ¦^‡ˆˆf´Q†üÜʝz¯šlzUĺš@쇀p¶n]sxtx¶@„~ÒĂJb©gk‚{°‚~c°`ԙ¬rV\\“la¼¤ôá`¯¹LC†ÆbŒxEræO‚v[H­˜„[~|aB£ÖsºdAĐzNÂðsŽÞƔ…Ĥªbƒ–ab`ho¡³F«èVloŽ¤™ÔRzpp®SŽĪº¨ÖƒºN…ij„d`’a”¦¤F³ºDÎńĀìŠCžĜº¦Ċ•~nS›|gźvZkCÆj°zVÈÁƔ]LÊFZg…čP­kini«‹qǀcz͔Y®¬Ů»qR×ō©DՄ‘§ƙǃŵTÉĩ±ŸıdÑnYY›IJvNĆƌØÜ Öp–}e³¦m‹©iÓ|¹Ÿħņ›|ª¦QF¢Â¬ʖovg¿em‡^ucà÷gՎuŒíÙćĝ}FĻ¼Ĺ{µHK•sLSđƃr‹č¤[Ag‘oS‹ŇYMÿ§Ç{Fśbky‰lQxĕƒ]T·¶[B…ÑÏGáşşƇe€…•ăYSs­FQ}­Bƒw‘tYğÃ@~…C̀Q ×W‡j˱rÉ¥oÏ ±«ÓÂ¥•ƒ€k—ŽwWűŒmcih³K›~‰µh¯e]lµ›él•Eģ‰•E“ďs‡’mǖŧē`ãògK_ÛsUʝ“ćğ¶hŒöŒO¤Ǜn³Žc‘`¡y‹¦C‘ez€YŠwa™–‘[ďĵűMę§]X˜Î_‚훘Û]é’ÛUćİÕBƣ±…dƒy¹T^džûÅÑŦ·‡PĻþÙ`K€¦˜…¢ÍeœĥR¿Œ³£[~Œäu¼dl‰t‚†W¸oRM¢ď\\zœ}Æzdvň–{ÎXF¶°Â_„ÒÂÏL©Ö•TmuŸ¼ãl‰›īkiqéfA„·Êµ\\őDc¥ÝF“y›Ôć˜c€űH_hL܋êĺШc}rn`½„Ì@¸¶ªVLŒŠhŒ‹\\•Ţĺk~ŽĠið°|gŒtTĭĸ^x‘vK˜VGréAé‘bUu›MJ‰VÃO¡…qĂXËS‰ģãlýàŸ_ju‡YÛÒB†œG^˜é֊¶§ŽƒEG”ÅzěƒƯ¤Ek‡N[kdåucé¬dnYpAyČ{`]þ¯T’bÜÈk‚¡Ġ•vŒàh„ÂƄ¢J" + ] + ], + "encodeOffsets": [[[111707, 21520]], [[107619, 25527]]] + } + }, + { + "type": "Feature", + "id": "460000", + "properties": { "id": "460000", "cp": [109.83119, 19.031971], "name": "海南", "childNum": 1 }, + "geometry": { + "type": "Polygon", + "coordinates": ["@@š¦Ŝil¢”XƦ‘ƞò–ïè§ŞCêɕrŧůÇąĻõ™·ĉ³œ̅kÇm@ċȧƒŧĥ‰Ľʉ­ƅſ“ȓÒ˦ŝE}ºƑ[ÍĜȋ gÎfǐÏĤ¨êƺ\\Ɔ¸ĠĎvʄȀœÐ¾jNðĀÒRŒšZdž™zÐŘΰH¨Ƣb²_Ġ "], + "encodeOffsets": [[112750, 20508]] + } + }, + { + "type": "Feature", + "id": "510000", + "properties": { + "id": "510000", + "cp": [104.065735, 30.659462], + "name": "四川", + "childNum": 2 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@LqKr"], + [ + "@@Š[ĻéV£ž_ţġñpG •réÏ·~ąSfy×͂·ºſƽiÍıƣıĻmHH}siaX@iÇ°ÁÃ×t«ƒ­Tƒ¤J–JJŒyJ•ÈŠ`Ohߦ¡uËhIyCjmÿw…ZG……Ti‹SˆsO‰žB²ŸfNmsPaˆ{M{ŠõE‘^Hj}gYpaeuž¯‘oáwHjÁ½M¡pM“–uå‡mni{fk”\\oƒÎqCw†EZ¼K›ĝŠƒAy{m÷L‡wO×SimRI¯rK™õBS«sFe‡]fµ¢óY_ÆPRcue°Cbo׌bd£ŌIHgtrnyPt¦foaXďx›lBowz‹_{ÊéWiêE„GhܸºuFĈIxf®Ž•Y½ĀǙ]¤EyŸF²ċ’w¸¿@g¢§RGv»–áŸW`ÃĵJwi]t¥wO­½a[׈]`Ãi­üL€¦LabbTÀå’c}Íh™Æhˆ‹®BH€î|Ék­¤S†y£„ia©taį·Ɖ`ō¥Uh“O…ƒĝLk}©Fos‰´›Jm„µlŁu—…ø–nÑJWΪ–YÀïAetTžŅ‚ӍG™Ë«bo‰{ıwodƟ½ƒžOġܑµxàNÖ¾P²§HKv¾–]|•B‡ÆåoZ`¡Ø`ÀmºĠ~ÌЧnDž¿¤]wğ@sƒ‰rğu‰~‘Io”[é±¹ ¿žſđӉ@q‹gˆ¹zƱřaí°KtǤV»Ã[ĩǭƑ^ÇÓ@ỗs›Zϕ‹œÅĭ€Ƌ•ěpwDóÖሯneQˌq·•GCœýS]xŸ·ý‹q³•O՜Œ¶Qzßti{ř‰áÍÇWŝŭñzÇW‹pç¿JŒ™‚Xœĩè½cŒF–ÂLiVjx}\\N†ŇĖ¥Ge–“JA¼ÄHfÈu~¸Æ«dE³ÉMA|b˜Ò…˜ćhG¬CM‚õŠ„ƤąAvƒüV€éŀ‰_V̳ĐwQj´·ZeÈÁ¨X´Æ¡Qu·»Ÿ“˜ÕZ³ġqDo‰y`L¬gdp°şŠp¦ėìÅĮZŽ°Iä”h‚‘ˆzŠĵœf²å ›ĚрKp‹IN|‹„Ñz]ń……·FU×é»R³™MƒÉ»GM«€ki€™ér™}Ã`¹ăÞmȝnÁîRǀ³ĜoİzŔwǶVÚ£À]ɜ»ĆlƂ²Ġ…þTº·àUȞÏʦ¶†I’«dĽĢdĬ¿–»Ĕ׊h\\c¬†ä²GêëĤł¥ÀǿżÃÆMº}BÕĢyFVvw–ˆxBèĻĒ©Ĉ“tCĢɽŠȣ¦āæ·HĽî“ôNԓ~^¤Ɗœu„œ^s¼{TA¼ø°¢İªDè¾Ň¶ÝJ‘®Z´ğ~Sn|ªWÚ©òzPOȸ‚bð¢|‹øĞŠŒœŒQìÛÐ@Ğ™ǎRS¤Á§d…i“´ezÝúØã]Hq„kIŸþËQǦÃsǤ[E¬ÉŪÍxXƒ·ÖƁİlƞ¹ª¹|XÊwn‘ÆƄmÀêErĒtD®ċæcQƒ”E®³^ĭ¥©l}äQto˜ŖÜqƎkµ–„ªÔĻĴ¡@Ċ°B²Èw^^RsºTĀ£ŚæœQP‘JvÄz„^Đ¹Æ¯fLà´GC²‘dt˜­ĀRt¼¤ĦOðğfÔðDŨŁĞƘïžPȆ®âbMüÀXZ ¸£@Ś›»»QÉ­™]d“sÖ×_͖_ÌêŮPrĔĐÕGĂeZÜîĘqBhtO ¤tE[h|Y‹Ô‚ZśÎs´xº±UŒ’ñˆt|O’ĩĠºNbgþŠJy^dÂY Į„]Řz¦gC‚³€R`ĀŠz’¢AjŒ¸CL„¤RÆ»@­Ŏk\\Ç´£YW}z@Z}‰Ã¶“oû¶]´^N‡Ò}èN‚ª–P˜Íy¹`S°´†ATe€VamdUĐwʄvĮÕ\\ƒu‹Æŗ¨Yp¹àZÂm™Wh{á„}WØǍ•Éüw™ga§áCNęÎ[ĀÕĪgÖɪX˜øx¬½Ů¦¦[€—„NΆL€ÜUÖ´òrÙŠxR^–†J˜k„ijnDX{Uƒ~ET{ļº¦PZc”jF²Ė@Žp˜g€ˆ¨“B{ƒu¨ŦyhoÚD®¯¢˜ WòàFΤ¨GDäz¦kŮPœġq˚¥À]€Ÿ˜eŽâÚ´ªKxī„Pˆ—Ö|æ[xäJÞĥ‚s’NÖ½ž€I†¬nĨY´®Ð—ƐŠ€mD™ŝuäđđEb…e’e_™v¡}ìęNJē}q”É埁T¯µRs¡M@}ůa†a­¯wvƉåZwž\\Z{åû^›" + ] + ], + "encodeOffsets": [[[108815, 30935]], [[110617, 31811]]] + } + }, + { + "type": "Feature", + "id": "520000", + "properties": { + "id": "520000", + "cp": [106.713478, 26.578343], + "name": "贵州", + "childNum": 3 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@†G\\†lY£‘in"], + ["@@q‚|ˆ‚mc¯tχVSÎ"], + [ + "@@hÑ£Is‡NgßH†›HªķÃh_¹ƒ¡ĝħń¦uيùŽgS¯JHŸ|sÝÅtÁïyMDč»eÕtA¤{b\\}—ƒG®u\\åPFq‹wÅaD…žK°ºâ_£ùbµ”mÁ‹ÛœĹM[q|hlaªāI}тƒµ@swtwm^oµˆD鼊yV™ky°ÉžûÛR…³‚‡eˆ‡¥]RՋěħ[ƅåÛDpŒ”J„iV™™‰ÂF²I…»mN·£›LbÒYb—WsÀbŽ™pki™TZĄă¶HŒq`……ĥ_JŸ¯ae«ƒKpÝx]aĕÛPƒÇȟ[ÁåŵÏő—÷Pw}‡TœÙ@Õs«ĿÛq©½œm¤ÙH·yǥĘĉBµĨÕnđ]K„©„œá‹ŸG纍§Õßg‡ǗĦTèƤƺ{¶ÉHÎd¾ŚÊ·OÐjXWrãLyzÉAL¾ę¢bĶėy_qMĔąro¼hĊžw¶øV¤w”²Ĉ]ʚKx|`ź¦ÂÈdr„cȁbe¸›`I¼čTF´¼Óýȃr¹ÍJ©k_șl³´_pН`oÒhŽ¶pa‚^ÓĔ}D»^Xyœ`d˜[Kv…JPhèhCrĂĚÂ^Êƌ wˆZL­Ġ£šÁbrzOIl’MM”ĪŐžËr×ÎeŦŽtw|Œ¢mKjSǘňĂStÎŦEtqFT†¾†E쬬ôxÌO¢Ÿ KŠ³ŀºäY†„”PVgŎ¦Ŋm޼VZwVlŒ„z¤…ž£Tl®ctĽÚó{G­A‡ŒÇgeš~Αd¿æaSba¥KKûj®_ć^\\ؾbP®¦x^sxjĶI_Ä X‚⼕Hu¨Qh¡À@Ëô}Ž±žGNìĎlT¸ˆ…`V~R°tbÕĊ`¸úÛtπFDu€[ƒMfqGH·¥yA‰ztMFe|R‚_Gk†ChZeÚ°to˜v`x‹b„ŒDnÐ{E}šZ˜è€x—†NEފREn˜[Pv@{~rĆAB§‚EO¿|UZ~ì„Uf¨J²ĂÝƀ‚sª–B`„s¶œfvö¦ŠÕ~dÔq¨¸º»uù[[§´sb¤¢zþFœ¢Æ…Àhˆ™ÂˆW\\ıŽËI݊o±ĭŠ£þˆÊs}¡R]ŒěƒD‚g´VG¢‚j±®è†ºÃmpU[Á›‘Œëº°r›ÜbNu¸}Žº¼‡`ni”ºÔXĄ¤¼Ôdaµ€Á_À…†ftQQgœR—‘·Ǔ’v”}Ýלĵ]µœ“Wc¤F²›OĩųãW½¯K‚©…]€{†LóµCIµ±Mß¿hŸ•©āq¬o‚½ž~@i~TUxŪÒ¢@ƒ£ÀEîôruń‚”“‚b[§nWuMÆLl¿]x}ij­€½" + ] + ], + "encodeOffsets": [[[112158, 27383]], [[112105, 27474]], [[112095, 27476]]] + } + }, + { + "type": "Feature", + "id": "530000", + "properties": { + "id": "530000", + "cp": [101.512251, 24.740609], + "name": "云南", + "childNum": 1 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@[„ùx½}ÑRH‘YīĺûsÍn‘iEoã½Ya²ė{c¬ĝg•ĂsA•ØÅwď‚õzFjw}—«Dx¿}UũlŸê™@•HÅ­F‰¨ÇoJ´Ónũuą¡Ã¢pÒŌ“Ø TF²‚xa²ËX€‚cʋlHîAßËŁkŻƑŷÉ©h™W­æßU‡“Ës¡¦}•teèƶStǀÇ}Fd£j‹ĈZĆÆ‹¤T‚č\\Dƒ}O÷š£Uˆ§~ŃG™‚åŃDĝ¸œTsd¶¶Bªš¤u¢ŌĎo~t¾ÍŶÒtD¦Ú„iôö‰€z›ØX²ghįh½Û±¯€ÿm·zR¦Ɵ`ªŊÃh¢rOԍ´£Ym¼èêf¯ŪĽn„†cÚbŒw\\zlvWžªâˆ ¦g–mĿBş£¢ƹřbĥkǫßeeZkÙIKueT»sVesb‘aĕ  ¶®dNœĄÄpªyŽ¼—„³BE˜®l‡ŽGœŭCœǶwêżĔÂe„pÍÀQƞpC„–¼ŲÈ­AÎô¶R„ä’Q^Øu¬°š_Èôc´¹ò¨P΢hlϦ´Ħ“Æ´sâDŽŲPnÊD^¯°’Upv†}®BP̪–jǬx–Söwlfòªv€qĸ|`H€­viļ€ndĜ­Ćhň•‚em·FyށqóžSį¯‘³X_ĞçêtryvL¤§z„¦c¦¥jnŞk˜ˆlD¤øz½ĜàžĂŧMÅ|áƆàÊcðÂF܎‚áŢ¥\\\\º™İøÒÐJĴ‡„îD¦zK²ǏÎEh~’CD­hMn^ÌöÄ©ČZÀžaü„fɭyœpį´ěFűk]Ôě¢qlÅĆÙa¶~Äqššê€ljN¬¼H„ÊšNQ´ê¼VظE††^ŃÒyŒƒM{ŒJLoÒœęæŸe±Ķ›y‰’‡gã“¯JYÆĭĘëo¥Š‰o¯hcK«z_pŠrC´ĢÖY”—¼ v¸¢RŽÅW³Â§fǸYi³xR´ďUˊ`êĿU„û€uĆBƒƣö‰N€DH«Ĉg†——Ñ‚aB{ÊNF´¬c·Åv}eÇÃGB»”If•¦HňĕM…~[iwjUÁKE•Ž‹¾dĪçW›šI‹èÀŒoÈXòyŞŮÈXâÎŚŠj|àsRy‹µÖ›–Pr´þŒ ¸^wþTDŔ–Hr¸‹žRÌmf‡żÕâCôox–ĜƌÆĮŒ›Ð–œY˜tâŦÔ@]ÈǮƒ\\Ī¼Ä£UsȯLbîƲŚºyh‡rŒŠ@ĒԝƀŸÀ²º\\êp“’JŠ}ĠvŠqt„Ġ@^xÀ£È†¨mËÏğ}n¹_¿¢×Y_æpˆÅ–A^{½•Lu¨GO±Õ½ßM¶w’ÁĢۂP‚›Ƣ¼pcIJxŠ|ap̬HšÐŒŊSfsðBZ¿©“XÏÒK•k†÷Eû¿‰S…rEFsÕūk”óVǥʼniTL‚¡n{‹uxţÏh™ôŝ¬ğōN“‘NJkyPaq™Âğ¤K®‡YŸxÉƋÁ]āęDqçgOg†ILu—\\_gz—]W¼ž~CÔē]bµogpў_oď`´³Țkl`IªºÎȄqÔþž»E³ĎSJ»œ_f·‚adÇqƒÇc¥Á_Źw{™L^ɱćx“U£µ÷xgĉp»ĆqNē`rĘzaĵĚ¡K½ÊBzyäKXqiWPÏɸ½řÍcÊG|µƕƣG˛÷Ÿk°_^ý|_zċBZocmø¯hhcæ\\lˆMFlư£Ĝ„ÆyH“„F¨‰µêÕ]—›HA…àӄ^it `þßäkŠĤÎT~Wlÿ¨„ÔPzUC–NVv [jâôDôď[}ž‰z¿–msSh‹¯{jïğl}šĹ[–őŒ‰gK‹©U·µË@¾ƒm_~q¡f¹…ÅË^»‘f³ø}Q•„¡Ö˳gͱ^ǁ…\\ëÃA_—¿bW›Ï[¶ƛ鏝£F{īZgm@|kHǭƁć¦UĔťƒ×ë}ǝƒeďºȡȘÏíBə£āĘPªij¶“ʼnÿ‡y©n‰ď£G¹¡I›Š±LÉĺÑdĉ܇W¥˜‰}g˜Á†{aqÃ¥aŠıęÏZ—ï`" + ], + "encodeOffsets": [[104636, 22969]] + } + }, + { + "type": "Feature", + "id": "540000", + "properties": { "id": "540000", "cp": [89.132212, 30.860361], "name": "西藏", "childNum": 1 }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@hžľxŽŖ‰xƒÒVŽ†ºÅâAĪÝȆµę¯Ňa±r_w~uSÕň‘qOj]ɄQ…£Z……UDûoY’»©M[‹L¼qãË{V͕çWViŽ]ë©Ä÷àyƛh›ÚU°ŒŒa”d„cQƒ~Mx¥™cc¡ÙaSyF—ցk­ŒuRýq¿Ôµ•QĽ³aG{¿FµëªéĜÿª@¬·–K‰·àariĕĀ«V»Ŷ™Ĵū˜gèLǴŇƶaf‹tŒèBŚ£^Šâ†ǐÝ®–šM¦ÁǞÿ¬LhŸŽJ¾óƾƺcxw‹f]Y…´ƒ¦|œQLn°aœdĊ…œ\\¨o’œǀÍŎœ´ĩĀd`tÊQŞŕ|‚¨C^©œĈ¦„¦ÎJĊ{ŽëĎjª²rЉšl`¼Ą[t|¦St辉PŒÜK¸€d˜Ƅı]s¤—î_v¹ÎVòŦj˜£Əsc—¬_Ğ´|Ł˜¦AvŽ¦w`ăaÝaa­¢e¤ı²©ªSªšÈMĄwžÉØŔì@T‘¤—Ę™\\õª@”þo´­xA s”ÂtŎKzó´ÇĊµ¢rž^nĊ­Æ¬×üGž¢‚³ {âĊ]š™G‚~bÀgVjzlhǶf€žOšfdŠ‰ªB]pj„•TO–tĊ‚n¤}®¦ƒČ¥d¢¼»ddš”Y¼Žt—¢eȤJ¤}Ǿ¡°§¤AГlc@ĝ”sªćļđAç‡wx•UuzEÖġ~AN¹ÄÅȀŻ¦¿ģŁéì±H…ãd«g[؉¼ēÀ•cīľġ¬cJ‘µ…ÐʥVȝ¸ßS¹†ý±ğkƁ¼ą^ɛ¤Ûÿ‰b[}¬ōõÃ]ËNm®g@•Bg}ÍF±ǐyL¥íCˆƒIij€Ï÷њį[¹¦[⚍EÛïÁÉdƅß{âNÆāŨߝ¾ě÷yC£‡k­´ÓH@¹†TZ¥¢įƒ·ÌAЧ®—Zc…v½ŸZ­¹|ŕWZqgW“|ieZÅYVӁqdq•bc²R@†c‡¥Rã»Ge†ŸeƃīQ•}J[ғK…¬Ə|o’ėjġĠÑN¡ð¯EBčnwôɍėªƒ²•CλŹġǝʅįĭạ̃ūȹ]ΓͧgšsgȽóϧµǛ†ęgſ¶ҍć`ĘąŌJޚä¤rÅň¥ÖÁUětęuůÞiĊÄÀ\\Æs¦ÓRb|Â^řÌkÄŷ¶½÷‡f±iMݑ›‰@ĥ°G¬ÃM¥n£Øą‚ğ¯ß”§aëbéüÑOčœk£{\\‘eµª×M‘šÉfm«Ƒ{Å׃Gŏǩãy³©WÑăû‚··‘Q—òı}¯ã‰I•éÕÂZ¨īès¶ZÈsŽæĔTŘvŽgÌsN@îá¾ó@‰˜ÙwU±ÉT廣TđŸWxq¹Zo‘b‹s[׌¯cĩv‡Œėŧ³BM|¹k‰ªħ—¥TzNYnݍßpęrñĠĉRS~½ŠěVVŠµ‚õ‡«ŒM££µB•ĉ¥áºae~³AuĐh`Ü³ç@BۘïĿa©|z²Ý¼D”£àč²‹ŸƒIƒû›I ā€óK¥}rÝ_Á´éMaň¨€~ªSĈ½Ž½KÙóĿeƃÆBŽ·¬ën×W|Uº}LJrƳ˜lŒµ`bÔ`QˆˆÐÓ@s¬ñIŒÍ@ûws¡åQÑßÁ`ŋĴ{Ī“T•ÚÅTSij‚‹Yo|Ç[ǾµMW¢ĭiÕØ¿@˜šMh…pÕ]j†éò¿OƇĆƇp€êĉâlØw–ěsˆǩ‚ĵ¸c…bU¹ř¨WavquSMzeo_^gsÏ·¥Ó@~¯¿RiīB™Š\\”qTGªÇĜçPoŠÿfñòą¦óQīÈáP•œābß{ƒZŗĸIæńhnszÁCËìñšÏ·ąĚÝUm®ó­L·ăU›Èíoù´Êj°ŁŤ_uµ^‘°Œìǖ@tĶĒ¡Æ‡M³Ģ«˜İĨÅ®ğ†RŽāð“ggheÆ¢z‚Ê©Ô\\°ÝĎz~ź¤Pn–MĪÖB£Ÿk™n鄧żćŠ˜ĆK„Ē°¼L¶è‰âz¨u¦¥LDĘz¬ýÎmĘd¾ß”Fz“hg²™Fy¦ĝ¤ċņbΛ@y‚Ąæm°NĮZRÖíŽJ²öLĸÒ¨Y®ƌÐV‰à˜tt_ڀÂyĠzž]Ţh€zĎ{†ĢX”ˆc|šÐqŽšfO¢¤ög‚ÌHNŽ„PKŖœŽ˜Uú´xx[xˆvĐCûĀŠìÖT¬¸^}Ìsòd´_Ž‡KgžLĴ…ÀBon|H@–Êx˜—¦BpŰˆŌ¿fµƌA¾zLjRxŠ¶F”œkĄźRzŀˆ~¶[”´Hnª–VƞuĒ­È¨ƎcƽÌm¸ÁÈM¦x͊ëÀxdžB’šú^´W†£–d„kɾĬpœw‚˂ØɦļĬIŚœÊ•n›Ŕa¸™~J°î”lɌxĤÊÈðhÌ®‚g˜T´øŽàCˆŽÀ^ªerrƘdž¢İP|Ė ŸWœªĦ^¶´ÂL„aT±üWƜ˜ǀRšŶUńšĖ[QhlLüA†‹Ü\\†qR›Ą©" + ], + "encodeOffsets": [[90849, 37210]] + } + }, + { + "type": "Feature", + "id": "610000", + "properties": { + "id": "610000", + "cp": [108.948024, 34.263161], + "name": "陕西", + "childNum": 1 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@˜p¢—ȮµšûG™Ħ}Ħšðǚ¶òƄ€jɂz°{ºØkÈęâ¦jª‚Bg‚\\œċ°s¬Ž’]jžú ‚E”Ȍdž¬s„t‡”RˆÆdĠݎwܔ¸ôW¾ƮłÒ_{’Ìšû¼„jº¹¢GǪÒ¯ĘƒZ`ºŊƒecņąš~BÂgzpâēòYǠȰÌTΨÂWœ|fcŸă§uF—Œ@NŸ¢XLƒŠRMº[ğȣſï|¥J™kc`sʼnǷ’Y¹‹W@µ÷K…ãï³ÛIcñ·VȋڍÒķø©—þ¥ƒy‚ÓŸğęmWµÎumZyOŅƟĥÓ~sÑL¤µaŅY¦ocyZ{‰y c]{ŒTa©ƒ`U_Ěē£ωÊƍKù’K¶ȱÝƷ§{û»ÅÁȹÍéuij|¹cÑd‘ŠìUYƒŽO‘uF–ÕÈYvÁCqӃT•Ǣí§·S¹NgŠV¬ë÷Át‡°Dد’C´ʼnƒópģ}„ċcE˅FŸŸéGU¥×K…§­¶³B‹Č}C¿åċ`wġB·¤őcƭ²ő[Å^axwQO…ÿEËߌ•ĤNĔŸwƇˆÄŠńwĪ­Šo[„_KÓª³“ÙnK‰Çƒěœÿ]ď€ă_d©·©Ýŏ°Ù®g]±„Ÿ‡ß˜å›—¬÷m\\›iaǑkěX{¢|ZKlçhLt€Ňîŵ€œè[€É@ƉĄEœ‡tƇÏ˜³­ħZ«mJ…›×¾‘MtÝĦ£IwÄå\\Õ{‡˜ƒOwĬ©LÙ³ÙgBƕŀr̛ĢŭO¥lãyC§HÍ£ßEñŸX¡—­°ÙCgpťz‘ˆb`wI„vA|§”‡—hoĕ@E±“iYd¥OĻ¹S|}F@¾oAO²{tfžÜ—¢Fǂ҈W²°BĤh^Wx{@„¬‚­F¸¡„ķn£P|ŸªĴ@^ĠĈæb–Ôc¶l˜Yi…–^Mi˜cĎ°Â[ä€vï¶gv@À“Ĭ·lJ¸sn|¼u~a]’ÆÈtŌºJp’ƒþ£KKf~Š¦UbyäIšĺãn‡Ô¿^­žŵMT–hĠܤko¼Ŏìąǜh`[tŒRd²IJ_œXPrɲ‰l‘‚XžiL§àƒ–¹ŽH˜°Ȧqº®QC—bA†„ŌJ¸ĕÚ³ĺ§ `d¨YjžiZvRĺ±öVKkjGȊĐePОZmļKÀ€‚[ŠŽ`ösìh†ïÎoĬdtKÞ{¬èÒÒBŒÔpIJÇĬJŊ¦±J«ˆY§‹@·pH€µàåVKe›pW†ftsAÅqC·¬ko«pHÆuK@oŸHĆۄķhx“e‘n›S³àǍrqƶRbzy€¸ËАl›¼EºpĤ¼Œx¼½~Ğ’”à@†ÚüdK^ˆmÌSj" + ], + "encodeOffsets": [[110234, 38774]] + } + }, + { + "type": "Feature", + "id": "620000", + "properties": { + "id": "620000", + "cp": [103.823557, 36.058039], + "name": "甘肃", + "childNum": 2 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@VuUv"], + [ + "@@ũ‹EĠtt~nkh`Q‰¦ÅÄÜdw˜Ab×ĠąJˆ¤DüègĺqBqœj°lI¡ĨÒ¤úSHbš‡ŠjΑBŠ°aZˆ¢KJŽ’O[|A£žDx}Nì•HUnrk„ kp€¼Y kMJn[aG‚áÚÏ[½rc†}aQxOgsPMnUs‡nc‹Z…ž–sKúvA›t„Þġ’£®ĀYKdnFwš¢JE°”Latf`¼h¬we|€Æ‡šbj}GA€·~WŽ”—`†¢MC¤tL©IJ°qdf”O‚“bÞĬ¹ttu`^ZúE`Œ[@„Æsîz®¡’C„ƳƜG²“R‘¢R’m”fŽwĸg܃‚ą G@pzJM½mŠhVy¸uÈÔO±¨{LfæU¶ßGĂq\\ª¬‡²I‚¥IʼnÈīoı‹ÓÑAçÑ|«LÝcspīðÍg…të_õ‰\\ĉñLYnĝg’ŸRǡÁiHLlõUĹ²uQjYi§Z_c¨Ÿ´ĹĖÙ·ŋI…ƒaBD˜­R¹ȥr—¯G•ºß„K¨jWk’ɱŠOq›Wij\\a­‹Q\\sg_ĆǛōëp»£lğۀgS•ŶN®À]ˆÓäm™ĹãJaz¥V}‰Le¤L„ýo‘¹IsŋÅÇ^‘Žbz…³tmEÁ´aŠ¹cčecÇN•ĊãÁ\\č¯—dNj•]j†—ZµkÓda•ćå]ğij@ ©O{¤ĸm¢ƒE·®ƒ«|@Xwg]Aģ±¯‡XǁÑdzªc›wQÚŝñsÕ³ÛV_ýƒ˜¥\\ů¥©¾÷w—Ž©WÕÊĩhÿÖÁRo¸V¬âDb¨šhûx–Ê×nj~Zâƒg|šXÁnßYoº§ZÅŘvŒ[„ĭÖʃuďxcVbnUSf…B¯³_Tzº—ΕO©çMÑ~Mˆ³]µ^püµ”ŠÄY~y@X~¤Z³€[Èōl@®Å¼£QKƒ·Di‹¡By‘ÿ‰Q_´D¥hŗyƒ^ŸĭÁZ]cIzý‰ah¹MĪğP‘s{ò‡‹‘²Vw¹t³Ŝˁ[ŽÑ}X\\gsFŸ£sPAgěp×ëfYHāďÖqēŭOÏë“dLü•\\iŒ”t^c®šRʺ¶—¢H°mˆ‘rYŸ£BŸ¹čIoľu¶uI]vģSQ{ƒUŻ”Å}QÂ|̋°ƅ¤ĩŪU ęĄžÌZҞ\\v˜²PĔ»ƢNHƒĂyAmƂwVmž`”]ȏb•”H`‰Ì¢²ILvĜ—H®¤Dlt_„¢JJÄämèÔDëþgºƫ™”aʎÌrêYi~ ÎݤNpÀA¾Ĕ¼b…ð÷’Žˆ‡®‚”üs”zMzÖĖQdȨý†v§Tè|ªH’þa¸|šÐ ƒwKĢx¦ivr^ÿ ¸l öæfƟĴ·PJv}n\\h¹¶v†·À|\\ƁĚN´Ĝ€çèÁz]ġ¤²¨QÒŨTIl‡ªťØ}¼˗ƦvÄùØE‹’«Fï˛Iq”ōŒTvāÜŏ‚íÛߜÛV—j³âwGăÂíNOŠˆŠPìyV³ʼnĖýZso§HіiYw[߆\\X¦¥c]ÔƩÜ·«j‡ÐqvÁ¦m^ċ±R™¦΋ƈťĚgÀ»IïĨʗƮŽ°Ɲ˜ĻþÍAƉſ±tÍEÕÞāNU͗¡\\ſčåÒʻĘm ƭÌŹöʥ’ëQ¤µ­ÇcƕªoIýˆ‰Iɐ_mkl³ă‰Ɠ¦j—¡Yz•Ňi–}Msßõ–īʋ —}ƒÁVmŸ_[n}eı­Uĥ¼‘ª•I{ΧDӜƻėoj‘qYhĹT©oūĶ£]ďxĩ‹ǑMĝ‰q`B´ƃ˺Ч—ç~™²ņj@”¥@đ´ί}ĥtPńǾV¬ufӃÉC‹tÓ̻‰…¹£G³€]ƖƾŎĪŪĘ̖¨ʈĢƂlɘ۪üºňUðǜȢƢż̌ȦǼ‚ĤŊɲĖ­Kq´ï¦—ºĒDzņɾªǀÞĈĂD†½ĄĎÌŗĞrôñnŽœN¼â¾ʄľԆ|DŽŽ֦ज़ȗlj̘̭ɺƅêgV̍ʆĠ·ÌĊv|ýĖÕWĊǎÞ´õ¼cÒÒBĢ͢UĜð͒s¨ňƃLĉÕÝ@ɛƯ÷¿Ľ­ĹeȏijëCȚDŲyê×Ŗyò¯ļcÂßY…tÁƤyAã˾J@ǝrý‹‰@¤…rz¸oP¹ɐÚyᐇHŸĀ[Jw…cVeȴϜ»ÈŽĖ}ƒŰŐèȭǢόĀƪÈŶë;Ñ̆ȤМľĮEŔ—ĹŊũ~ËUă{ŸĻƹɁύȩþĽvĽƓÉ@ē„ĽɲßǐƫʾǗĒpäWÐxnsÀ^ƆwW©¦cÅ¡Ji§vúF¶Ž¨c~c¼īŒeXǚ‹\\đ¾JŽwÀďksãA‹fÕ¦L}wa‚o”Z’‹D½†Ml«]eÒÅaɲáo½FõÛ]ĻÒ¡wYR£¢rvÓ®y®LF‹LzĈ„ôe]gx}•|KK}xklL]c¦£fRtív¦†PĤoH{tK" + ] + ], + "encodeOffsets": [[[108619, 36299]], [[108589, 36341]]] + } + }, + { + "type": "Feature", + "id": "630000", + "properties": { "id": "630000", "cp": [96.778916, 35.623178], "name": "青海", "childNum": 2 }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@InJm"], + [ + "@@CƒÆ½OŃĦsΰ~Ē³¦@@“Ņiš±è}ؘƄ˹A³r_ĞŠǒNĪŒĐw¤^ŬĵªpĺSZg’rpiƼĘԛ¨C|͖J’©Ħ»®VIJ~f\\m `Un„˜~ʌŸ•ĬàöNt•~ňjy–¢Zi˜Ɣ¥ĄŠk´nl`JʇŠJþ©pdƖ®È£¶ìRʦ‘źõƮËnŸʼėæÑƀĎ[‚˜¢VÎĂMÖÝÎF²sƊƀÎBļýƞ—¯ʘƭðħ¼Jh¿ŦęΌƇš¥²Q]Č¥nuÂÏriˆ¸¬ƪÛ^Ó¦d€¥[Wà…x\\ZŽjҕ¨GtpþYŊĕ´€zUO뇉P‰îMĄÁxH´á˜iÜUà›îÜՁĂÛSuŎ‹r“œJð̬EŒ‘FÁú×uÃÎkr“Ē{V}İ«O_ÌËĬ©ŽÓŧSRѱ§Ģ£^ÂyèçěM³Ƃę{[¸¿u…ºµ[gt£¸OƤĿéYŸõ·kĀŸq]juw¥Dĩƍ€õÇPéĽG‘ž©ã‡¤G…uȧþRcÕĕNy“yût“ˆ­‡ø‘†ï»a½ē¿BMoį£ŸÍj}éZËqbʍš“Ƭh¹ìÿÓAçãnIáI`ƒks£CG­ě˜Uy×Cy•…’Ÿ@¶ʡÊBnāzG„ơMē¼±O÷õJËĚăVŸĪũƆ£Œ¯{ËL½Ìzż“„VR|ĠTbuvJvµhĻĖH”Aëáa…­OÇðñęNw‡…œľ·L›mI±íĠĩPÉ×®ÿs—’cB³±JKßĊ«`…ađ»·QAmO’‘Vţéÿ¤¹SQt]]Çx€±¯A@ĉij¢Óļ©•ƒl¶ÅÛr—ŕspãRk~¦ª]Į­´“FR„åd­ČsCqđéFn¿Åƃm’Éx{W©ºƝºįkÕƂƑ¸wWūЩÈFž£\\tÈ¥ÄRÈýÌJ ƒlGr^×äùyÞ³fj”c†€¨£ÂZ|ǓMĝšÏ@ëÜőR‹›ĝ‰Œ÷¡{aïȷPu°ËXÙ{©TmĠ}Y³’­ÞIňµç½©C¡į÷¯B»|St»›]vƒųƒs»”}MÓ ÿʪƟǭA¡fs˜»PY¼c¡»¦c„ċ­¥£~msĉP•–Siƒ^o©A‰Šec‚™PeǵŽkg‚yUi¿h}aH™šĉ^|ᴟ¡HØûÅ«ĉ®]m€¡qĉ¶³ÈyôōLÁst“BŸ®wn±ă¥HSòėš£˜S’ë@לÊăxÇN©™©T±ª£IJ¡fb®ÞbŽb_Ą¥xu¥B—ž{łĝ³«`d˜Ɛt—¤ťiñžÍUuºí`£˜^tƃIJc—·ÛLO‹½Šsç¥Ts{ă\\_»™kϊ±q©čiìĉ|ÍIƒ¥ć¥›€]ª§D{ŝŖÉR_sÿc³Īō›ƿΑ›§p›[ĉ†›c¯bKm›R¥{³„Z†e^ŽŒwx¹dƽŽôIg §Mĕ ƹĴ¿—ǣÜ̓]‹Ý–]snåA{‹eŒƭ`ǻŊĿ\\ijŬű”YÂÿ¬jĖqŽßbŠ¸•L«¸©@ěĀ©ê¶ìÀEH|´bRľž–Ó¶rÀQþ‹vl®Õ‚E˜TzÜdb ˜hw¤{LR„ƒd“c‹b¯‹ÙVgœ‚ƜßzÃô쮍^jUèXΖ|UäÌ»rKŽ\\ŒªN‘¼pZCü†VY††¤ɃRi^rPҒTÖ}|br°qňbĚ°ªiƶGQ¾²„x¦PœmlŜ‘[Ĥ¡ΞsĦŸÔÏâ\\ªÚŒU\\f…¢N²§x|¤§„xĔsZPòʛ²SÐqF`ª„VƒÞŜĶƨVZŒÌL`ˆ¢dŐIqr\\oäõ–F礻Ŷ×h¹]Clـ\\¦ďÌį¬řtTӺƙgQÇÓHţĒ”´ÃbEÄlbʔC”|CˆŮˆk„Ʈ[ʼ¬ňœ´KŮÈΰÌĪ¶ƶlð”ļA†TUvdTŠG†º̼ŠÔ€ŒsÊDԄveOg" + ] + ], + "encodeOffsets": [[[105308, 37219]], [[95370, 40081]]] + } + }, + { + "type": "Feature", + "id": "640000", + "properties": { "id": "640000", "cp": [106.278179, 37.26637], "name": "宁夏", "childNum": 2 }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + "@@KëÀęĞ«OęȿȕŸı]ʼn¡åįÕÔ«Ǵõƪ™ĚQÐZhv K°›öqÀѐS[ÃÖHƖčË‡nL]ûc…Ùß@‚“ĝ‘¾}w»»‹oģF¹œ»kÌÏ·{zPƒ§B­¢íyÅt@ƒ@áš]Yv_ssģ¼i߁”ĻL¾ġsKD£¡N_…“˜X¸}B~Haiˆ™Åf{«x»ge_bs“KF¯¡Ix™mELcÿZ¤­Ģ‘ƒÝœsuBLù•t†ŒYdˆmVtNmtOPhRw~bd…¾qÐ\\âÙH\\bImlNZŸ»loƒŸqlVm–Gā§~QCw¤™{A\\‘PKŸNY‡¯bF‡kC¥’sk‹Šs_Ã\\ă«¢ħkJi¯r›rAhĹûç£CU‡ĕĊ_ԗBixÅُĄnªÑaM~ħpOu¥sîeQ¥¤^dkKwlL~{L~–hw^‚ófćƒKyEŒ­K­zuÔ¡qQ¤xZÑ¢^ļöܾEpž±âbÊÑÆ^fk¬…NC¾‘Œ“YpxbK~¥Že֎ŒäBlt¿Đx½I[ĒǙŒWž‹f»Ĭ}d§dµùEuj¨‚IÆ¢¥dXªƅx¿]mtÏwßRĶŒX¢͎vÆzƂZò®ǢÌʆCrâºMÞzžÆMҔÊÓŊZľ–r°Î®Ȉmª²ĈUªĚøºˆĮ¦ÌĘk„^FłĬhĚiĀĖ¾iİbjÕ" + ], + ["@@mfwěwMrŢªv@G‰"] + ], + "encodeOffsets": [[[109366, 40242]], [[108600, 36303]]] + } + }, + { + "type": "Feature", + "id": "650000", + "properties": { "id": "650000", "cp": [85.617733, 40.792818], "name": "新疆", "childNum": 1 }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@QØĔ²X¨”~ǘBºjʐßØvK”ƔX¨vĊOžÃƒ·¢i@~c—‡ĝe_«”Eš“}QxgɪëÏÃ@sÅyXoŖ{ô«ŸuX…ê•Îf`œC‚¹ÂÿÐGĮÕĞXŪōŸMźÈƺQèĽôe|¿ƸJR¤ĘEjcUóº¯Ĩ_ŘÁMª÷Ð¥Oéȇ¿ÖğǤǷÂF҇zÉx[]­Ĥĝ‰œ¦EP}ûƥé¿İƷTėƫœŕƅ™ƱB»Đ±’ēO…¦E–•}‘`cȺrĦáŖuҞª«IJ‡πdƺÏØZƴwʄ¤ĖGЙǂZĶƒèH¶}ÚZצʥĪï|ÇĦMŔ»İĝLj‹ì¥Βœba­¯¥ǕǚkĆŵĦɑĺƯxūД̵nơʃĽá½M»›òmqóŘĝč˾ăC…ćāƿÝɽ©DZŅ¹đ¥˜³ðLrÁ®ɱĕģʼnǻ̋ȥơŻǛȡVï¹Ň۩ûkɗġƁ§ʇė̕ĩũƽō^ƕŠUv£ƁQï“Ƶkŏ½ΉÃŭdzLқʻ«ƭ\\lƒ‡ŭD‡“{ʓDkaFÃÄa“³ŤđÔGRÈƚhSӹŚsİ«ĐË[¥ÚDkº^Øg¼ŵ¸£EÍö•€ůʼnT¡c_‡ËKY‹ƧUśĵ„݃U_©rETÏʜ±OñtYwē¨ƒ{£¨uM³x½şL©Ùá[ÓÐĥ Νtģ¢\\‚ś’nkO›w¥±ƒT»ƷFɯàĩÞáB¹Æ…ÑUw„੍žĽw[“mG½Èå~‡Æ÷QyŠěCFmĭZī—ŵVÁ™ƿQƛ—ûXS²‰b½KϽĉS›©ŷXĕŸ{ŽĕK·¥Ɨcqq©f¿]‡ßDõU³h—­gËÇïģÉɋw“k¯í}I·šœbmœÉ–ř›īJɥĻˁ×xo›ɹī‡l•c…¤³Xù]‘™DžA¿w͉ì¥wÇN·ÂËnƾƍdǧđ®Ɲv•Um©³G\\“}µĿ‡QyŹl㓛µEw‰LJQ½yƋBe¶ŋÀů‡ož¥A—˜Éw@•{Gpm¿Aij†ŽKLhˆ³`ñcËtW‚±»ÕS‰ëüÿďD‡u\\wwwù³—V›LŕƒOMËGh£õP¡™er™Ïd{“‡ġWÁ…č|yšg^ğyÁzÙs`—s|ÉåªÇ}m¢Ń¨`x¥’ù^•}ƒÌ¥H«‰Yªƅ”Aйn~ź¯šf¤áÀz„gŠÇDIԝ´AňĀ҄¶ûEYospõD[{ù°]u›Jq•U•|Soċxţ[õÔĥkŋÞŭZ˺óYËüċrw €ÞkrťË¿XGÉbřaDü·Ē÷Aê[Ää€I®BÕИÞ_¢āĠpŠÛÄȉĖġDKwbm‡ÄNô‡ŠfœƫVÉvi†dz—H‘‹QµâFšù­Âœ³¦{YGžƒd¢ĚÜO „€{Ö¦ÞÍÀPŒ^b–ƾŠlŽ[„vt×ĈÍE˨¡Đ~´î¸ùÎh€uè`¸ŸHÕŔVºwĠââWò‡@{œÙNÝ´ə²ȕn{¿¥{l—÷eé^e’ďˆXj©î\\ªÑò˜Üìc\\üqˆÕ[Č¡xoÂċªbØ­Œø|€¶ȴZdÆšońéŒGš\\”¼C°ÌƁn´nxšÊOĨ’Ūƴĸ¢¸òTxÊǪMīИÖŲÃɎOvˆʦƢ~FŽ‡Rěò—¿ġ~åŊœú‰Nšžš¸qŽ’Ę[Ĕ¶ÂćnÒPĒÜvúĀÊbÖ{Äî¸~Ŕünp¤ÂH¾œĄYÒ©ÊfºmԈĘcDoĬMŬ’˜S¤„s²‚”ʘچžȂVŦ –ŽèW°ªB|IJXŔþÈJĦÆæFĚêŠYĂªĂ]øªŖNÞüA€’fɨJ€˜¯ÎrDDšĤ€`€mz\\„§~D¬{vJÂ˜«lµĂb–¤p€ŌŰNĄ¨ĊXW|ų ¿¾ɄĦƐMT”‡òP˜÷fØĶK¢ȝ˔Sô¹òEð­”`Ɩ½ǒÂň×äı–§ĤƝ§C~¡‚hlå‚ǺŦŞkâ’~}ŽFøàIJaĞ‚fƠ¥Ž„Ŕdž˜®U¸ˆźXœv¢aƆúŪtŠųƠjd•ƺŠƺÅìnrh\\ĺ¯äɝĦ]èpĄ¦´LƞĬŠ´ƤǬ˼Ēɸ¤rºǼ²¨zÌPðŀbþ¹ļD¢¹œ\\ĜÑŚŸ¶ZƄ³àjĨoâŠȴLʉȮŒĐ­ĚăŽÀêZǚŐ¤qȂ\\L¢ŌİfÆs|zºeªÙæ§΢{Ā´ƐÚ¬¨Ĵà²łhʺKÞºÖTŠiƢ¾ªì°`öøu®Ê¾ãØ" + ], + "encodeOffsets": [[88824, 50096]] + } + }, + { + "type": "Feature", + "id": "110000", + "properties": { + "id": "110000", + "cp": [116.405285, 39.904989], + "name": "北京", + "childNum": 1 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@ĽOÁ›ûtŷmiÍt_H»Ĩ±d`Š¹­{bw…Yr“³S]§§o¹€qGtm_Sŧ€“oa›‹FLg‘QN_•dV€@Zom_ć\\ߚc±x¯oœRcfe…£’o§ËgToÛJíĔóu…|wP¤™XnO¢ÉˆŦ¯rNÄā¤zâŖÈRpŢZŠœÚ{GŠrFt¦Òx§ø¹RóäV¤XdˆżâºWbwŚ¨Ud®bêņ¾‘jnŎGŃŶŠnzÚSeîĜZczî¾i]͜™QaúÍÔiþĩȨWĢ‹ü|Ėu[qb[swP@ÅğP¿{\\‡¥A¨Ï‘Ѩj¯ŠX\\¯œMK‘pA³[H…īu}}" + ], + "encodeOffsets": [[120023, 41045]] + } + }, + { + "type": "Feature", + "id": "120000", + "properties": { + "id": "120000", + "cp": [117.190182, 39.125596], + "name": "天津", + "childNum": 1 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + "@@ŬgX§Ü«E…¶Ḟ“¬O_™ïlÁg“z±AXe™µÄĵ{¶]gitgšIj·›¥îakS€‰¨ÐƎk}ĕ{gB—qGf{¿a†U^fI“ư‹³õ{YƒıëNĿžk©ïËZŏ‘R§òoY×Ógc…ĥs¡bġ«@dekąI[nlPqCnp{ˆō³°`{PNdƗqSÄĻNNâyj]äžÒD ĬH°Æ]~¡HO¾ŒX}ÐxŒgp“gWˆrDGˆŒpù‚Š^L‚ˆrzWxˆZ^¨´T\\|~@I‰zƒ–bĤ‹œjeĊªz£®Ĕvě€L†mV¾Ô_ȔNW~zbĬvG†²ZmDM~”~" + ], + "encodeOffsets": [[120237, 41215]] + } + }, + { + "type": "Feature", + "id": "310000", + "properties": { + "id": "310000", + "cp": [121.472644, 31.231706], + "name": "上海", + "childNum": 6 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@ɧư¬EpƸÁxc‡"], + ["@@©„ªƒ"], + ["@@”MA‹‘š"], + ["@@Qp݁E§ÉC¾"], + ["@@bŝՕÕEȣÚƥêImɇǦèÜĠŒÚžÃƌÃ͎ó"], + ["@@ǜûȬɋŠŭ™×^‰sYŒɍDŋ‘ŽąñCG²«ªč@h–_p¯A{‡oloY€¬j@IJ`•gQڛhr|ǀ^MIJvtbe´R¯Ô¬¨YŽô¤r]ì†Ƭį"] + ], + "encodeOffsets": [[[124702, 32062]], [[124547, 32200]], [[124808, 31991]], [[124726, 32110]], [[124903, 32376]], [[124438, 32149]]] + } + }, + { + "type": "Feature", + "id": "500000", + "properties": { + "id": "500000", + "cp": [107.304962, 29.533155], + "name": "重庆", + "childNum": 2 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + "@@vjG~nGŘŬĶȂƀƾ¹¸ØÎezĆT¸}êЖqHŸðqĖ䒊¥^CƒIj–²p…\\_ æüY|[YxƊæuž°xb®…Űb@~¢NQt°¶‚S栓Ê~rljĔëĚ¢~šuf`‘‚†fa‚ĔJåĊ„nÖ]„jƎćÊ@Š£¾a®£Ű{ŶĕF‹ègLk{Y|¡ĜWƔtƬJÑxq‹±ĢN´‰òK‰™–LÈüD|s`ŋ’ć]ƒÃ‰`đŒMûƱ½~Y°ħ`ƏíW‰½eI‹½{aŸ‘OIrÏ¡ĕŇa†p†µÜƅġ‘œ^ÖÛbÙŽŏml½S‹êqDu[R‹ãË»†ÿw`»y‘¸_ĺę}÷`M¯ċfCVµqʼn÷Z•gg“Œ`d½pDO‡ÎCnœ^uf²ènh¼WtƏxRGg¦…pV„†FI±ŽG^ŒIc´ec‡’G•ĹÞ½sëĬ„h˜xW‚}Kӈe­Xsbk”F¦›L‘ØgTkïƵNï¶}Gy“w\\oñ¡nmĈzjŸ•@™Óc£»Wă¹Ój“_m»ˆ¹·~MvÛaqœ»­‰êœ’\\ÂoVnŽÓØ͙²«‹bq¿efE „€‹Ĝ^Qž~ Évý‡ş¤²Į‰pEİ}zcĺƒL‹½‡š¿gņ›¡ýE¡ya£³t\\¨\\vú»¼§·Ñr_oÒý¥u‚•_n»_ƒ•At©Þűā§IVeëƒY}{VPÀFA¨ąB}q@|Ou—\\Fm‰QF݅Mw˜å}]•€|FmϋCaƒwŒu_p—¯sfÙgY…DHl`{QEfNysBŠ¦zG¸rHe‚„N\\CvEsÐùÜ_·ÖĉsaQ¯€}_U‡†xÃđŠq›NH¬•Äd^ÝŰR¬ã°wećJEž·vÝ·Hgƒ‚éFXjÉê`|yŒpxkAwœWĐpb¥eOsmzwqChóUQl¥F^laf‹anòsr›EvfQdÁUVf—ÎvÜ^efˆtET¬ôA\\œ¢sJŽnQTjP؈xøK|nBz‰„œĞ»LY‚…FDxӄvr“[ehľš•vN”¢o¾NiÂxGp⬐z›bfZo~hGi’]öF|‰|Nb‡tOMn eA±ŠtPT‡LjpYQ|†SH††YĀxinzDJ€Ìg¢và¥Pg‰_–ÇzII‹€II•„£®S¬„Øs쐣ŒN" + ], + ["@@ifjN@s"] + ], + "encodeOffsets": [[[109628, 30765]], [[111725, 31320]]] + } + }, + { + "type": "Feature", + "id": "810000", + "properties": { + "id": "810000", + "cp": [114.173355, 22.320048], + "name": "香港", + "childNum": 5 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + ["@@AlBk"], + ["@@mŽn"], + ["@@EpFo"], + ["@@ea¢pl¸Eõ¹‡hj[ƒ]ÔCΖ@lj˜¡uBXŸ…•´‹AI¹…[‹yDUˆ]W`çwZkmc–…M›žp€Åv›}I‹oJlcaƒfёKŽ°ä¬XJmРđhI®æÔtSHn€Eˆ„ÒrÈc"], + ["@@rMUw‡AS®€e"] + ], + "encodeOffsets": [[[117111, 23002]], [[117072, 22876]], [[117045, 22887]], [[116975, 23082]], [[116882, 22747]]] + } + }, + { + "type": "Feature", + "id": "820000", + "properties": { "id": "820000", "cp": [113.54909, 22.198951], "name": "澳门", "childNum": 1 }, + "geometry": { + "type": "Polygon", + "coordinates": ["@@kÊd°å§s"], + "encodeOffsets": [[116279, 22639]] + } + } + ], + "UTF8Encoding": true +} diff --git a/src/views/demo/charts/data.ts b/src/views/demo/charts/data.ts new file mode 100644 index 00000000..d7d2411c --- /dev/null +++ b/src/views/demo/charts/data.ts @@ -0,0 +1,189 @@ +export const mapData: any = [ + { + name: '北京', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '天津', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '上海', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '重庆', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '河北', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '河南', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '云南', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '辽宁', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '黑龙江', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '湖南', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '安徽', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '山东', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '新疆', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '江苏', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '浙江', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '江西', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '湖北', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '广西', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '甘肃', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '山西', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '内蒙古', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '陕西', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '吉林', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '福建', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '贵州', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '广东', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '青海', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '西藏', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '四川', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '宁夏', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '海南', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '台湾', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '香港', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + }, + { + name: '澳门', + value: Math.round(Math.random() * 1000), + tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)] + } +] + +export const getLineData = (() => { + const category: any[] = [] + let dottedBase = +new Date() + const lineData: any[] = [] + const barData: any[] = [] + + for (let i = 0; i < 20; i++) { + const date = new Date((dottedBase += 1000 * 3600 * 24)) + category.push([date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-')) + const b = Math.random() * 200 + const d = Math.random() * 200 + barData.push(b) + lineData.push(d + b) + } + return { barData, category, lineData } +})() diff --git a/src/views/demo/charts/map/Baidu.vue b/src/views/demo/charts/map/Baidu.vue new file mode 100644 index 00000000..14505806 --- /dev/null +++ b/src/views/demo/charts/map/Baidu.vue @@ -0,0 +1,33 @@ + + diff --git a/src/views/demo/charts/map/Gaode.vue b/src/views/demo/charts/map/Gaode.vue new file mode 100644 index 00000000..20753bab --- /dev/null +++ b/src/views/demo/charts/map/Gaode.vue @@ -0,0 +1,35 @@ + + diff --git a/src/views/demo/charts/map/Google.vue b/src/views/demo/charts/map/Google.vue new file mode 100644 index 00000000..7f3136fd --- /dev/null +++ b/src/views/demo/charts/map/Google.vue @@ -0,0 +1,40 @@ + + diff --git a/src/views/demo/comp/button/index.vue b/src/views/demo/comp/button/index.vue new file mode 100644 index 00000000..6faa5cb1 --- /dev/null +++ b/src/views/demo/comp/button/index.vue @@ -0,0 +1,106 @@ + + diff --git a/src/views/demo/comp/card-list/index.vue b/src/views/demo/comp/card-list/index.vue new file mode 100644 index 00000000..82b52b33 --- /dev/null +++ b/src/views/demo/comp/card-list/index.vue @@ -0,0 +1,32 @@ + + diff --git a/src/views/demo/comp/count-to/index.vue b/src/views/demo/comp/count-to/index.vue new file mode 100644 index 00000000..7668c087 --- /dev/null +++ b/src/views/demo/comp/count-to/index.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/views/demo/comp/cropper/index.vue b/src/views/demo/comp/cropper/index.vue new file mode 100644 index 00000000..07a40a87 --- /dev/null +++ b/src/views/demo/comp/cropper/index.vue @@ -0,0 +1,72 @@ + + + + diff --git a/src/views/demo/comp/desc/index.vue b/src/views/demo/comp/desc/index.vue new file mode 100644 index 00000000..5134c049 --- /dev/null +++ b/src/views/demo/comp/desc/index.vue @@ -0,0 +1,77 @@ + + diff --git a/src/views/demo/comp/drawer/Drawer1.vue b/src/views/demo/comp/drawer/Drawer1.vue new file mode 100644 index 00000000..02070601 --- /dev/null +++ b/src/views/demo/comp/drawer/Drawer1.vue @@ -0,0 +1,6 @@ + + diff --git a/src/views/demo/comp/drawer/Drawer2.vue b/src/views/demo/comp/drawer/Drawer2.vue new file mode 100644 index 00000000..f104e20b --- /dev/null +++ b/src/views/demo/comp/drawer/Drawer2.vue @@ -0,0 +1,10 @@ + + diff --git a/src/views/demo/comp/drawer/Drawer3.vue b/src/views/demo/comp/drawer/Drawer3.vue new file mode 100644 index 00000000..a4d5420a --- /dev/null +++ b/src/views/demo/comp/drawer/Drawer3.vue @@ -0,0 +1,27 @@ + + diff --git a/src/views/demo/comp/drawer/Drawer4.vue b/src/views/demo/comp/drawer/Drawer4.vue new file mode 100644 index 00000000..0dda5630 --- /dev/null +++ b/src/views/demo/comp/drawer/Drawer4.vue @@ -0,0 +1,46 @@ + + diff --git a/src/views/demo/comp/drawer/Drawer5.vue b/src/views/demo/comp/drawer/Drawer5.vue new file mode 100644 index 00000000..506eea2f --- /dev/null +++ b/src/views/demo/comp/drawer/Drawer5.vue @@ -0,0 +1,9 @@ + + diff --git a/src/views/demo/comp/drawer/index.vue b/src/views/demo/comp/drawer/index.vue new file mode 100644 index 00000000..83192479 --- /dev/null +++ b/src/views/demo/comp/drawer/index.vue @@ -0,0 +1,50 @@ + + diff --git a/src/views/demo/comp/flow-chart/dataTurbo.json b/src/views/demo/comp/flow-chart/dataTurbo.json new file mode 100644 index 00000000..f6432c4e --- /dev/null +++ b/src/views/demo/comp/flow-chart/dataTurbo.json @@ -0,0 +1,240 @@ +{ + "flowElementList": [ + { + "incoming": [], + "outgoing": ["Flow_33inf2k"], + "dockers": [], + "type": 2, + "properties": { + "a": "efrwe", + "b": "wewe", + "name": "开始", + "x": 280, + "y": 200, + "text": { + "x": 280, + "y": 200, + "value": "开始" + }, + "logicFlowType": "bpmn:startEvent" + }, + "key": "Event_1d42u4p" + }, + { + "incoming": ["Flow_379e0o9"], + "outgoing": [], + "dockers": [], + "type": 3, + "properties": { + "a": "efrwe", + "b": "wewe", + "name": "结束", + "x": 920, + "y": 200, + "text": { + "x": 920, + "y": 200, + "value": "结束" + }, + "logicFlowType": "bpmn:endEvent" + }, + "key": "Event_08p8i6q" + }, + { + "incoming": ["Flow_0pfouf0"], + "outgoing": ["Flow_3918lhh"], + "dockers": [], + "type": 6, + "properties": { + "a": "efrwe", + "b": "wewe", + "name": "网关", + "x": 580, + "y": 200, + "text": { + "x": 580, + "y": 200, + "value": "网关" + }, + "logicFlowType": "bpmn:exclusiveGateway" + }, + "key": "Gateway_1fngqgj" + }, + { + "incoming": ["Flow_33inf2k"], + "outgoing": ["Flow_0pfouf0"], + "dockers": [], + "type": 4, + "properties": { + "a": "efrwe", + "b": "wewe", + "name": "用户", + "x": 420, + "y": 200, + "text": { + "x": 420, + "y": 200, + "value": "用户" + }, + "logicFlowType": "bpmn:userTask" + }, + "key": "Activity_2mgtaia" + }, + { + "incoming": ["Flow_3918lhh"], + "outgoing": ["Flow_379e0o9"], + "dockers": [], + "type": 5, + "properties": { + "a": "efrwe", + "b": "wewe", + "name": "服务", + "x": 760, + "y": 200, + "text": { + "x": 760, + "y": 200, + "value": "服务" + }, + "logicFlowType": "bpmn:serviceTask" + }, + "key": "Activity_1sp8qc8" + }, + { + "incoming": ["Event_1d42u4p"], + "outgoing": ["Activity_2mgtaia"], + "type": 1, + "dockers": [], + "properties": { + "name": "边", + "text": { + "x": 331, + "y": 200, + "value": "边" + }, + "startPoint": { + "x": 298, + "y": 200 + }, + "endPoint": { + "x": 370, + "y": 200 + }, + "pointsList": [ + { + "x": 298, + "y": 200 + }, + { + "x": 370, + "y": 200 + } + ], + "logicFlowType": "bpmn:sequenceFlow" + }, + "key": "Flow_33inf2k" + }, + { + "incoming": ["Activity_2mgtaia"], + "outgoing": ["Gateway_1fngqgj"], + "type": 1, + "dockers": [], + "properties": { + "name": "边2", + "text": { + "x": 507, + "y": 200, + "value": "边2" + }, + "startPoint": { + "x": 470, + "y": 200 + }, + "endPoint": { + "x": 555, + "y": 200 + }, + "pointsList": [ + { + "x": 470, + "y": 200 + }, + { + "x": 555, + "y": 200 + } + ], + "logicFlowType": "bpmn:sequenceFlow" + }, + "key": "Flow_0pfouf0" + }, + { + "incoming": ["Gateway_1fngqgj"], + "outgoing": ["Activity_1sp8qc8"], + "type": 1, + "dockers": [], + "properties": { + "name": "边3", + "text": { + "x": 664, + "y": 200, + "value": "边3" + }, + "startPoint": { + "x": 605, + "y": 200 + }, + "endPoint": { + "x": 710, + "y": 200 + }, + "pointsList": [ + { + "x": 605, + "y": 200 + }, + { + "x": 710, + "y": 200 + } + ], + "logicFlowType": "bpmn:sequenceFlow" + }, + "key": "Flow_3918lhh" + }, + { + "incoming": ["Activity_1sp8qc8"], + "outgoing": ["Event_08p8i6q"], + "type": 1, + "dockers": [], + "properties": { + "name": "边4", + "text": { + "x": 871, + "y": 200, + "value": "边4" + }, + "startPoint": { + "x": 810, + "y": 200 + }, + "endPoint": { + "x": 902, + "y": 200 + }, + "pointsList": [ + { + "x": 810, + "y": 200 + }, + { + "x": 902, + "y": 200 + } + ], + "logicFlowType": "bpmn:sequenceFlow" + }, + "key": "Flow_379e0o9" + } + ] +} diff --git a/src/views/demo/comp/flow-chart/index.vue b/src/views/demo/comp/flow-chart/index.vue new file mode 100644 index 00000000..88313567 --- /dev/null +++ b/src/views/demo/comp/flow-chart/index.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/views/demo/comp/lazy/TargetContent.vue b/src/views/demo/comp/lazy/TargetContent.vue new file mode 100644 index 00000000..a71605b5 --- /dev/null +++ b/src/views/demo/comp/lazy/TargetContent.vue @@ -0,0 +1,12 @@ + + diff --git a/src/views/demo/comp/lazy/Transition.vue b/src/views/demo/comp/lazy/Transition.vue new file mode 100644 index 00000000..46d4161b --- /dev/null +++ b/src/views/demo/comp/lazy/Transition.vue @@ -0,0 +1,72 @@ + + + diff --git a/src/views/demo/comp/lazy/index.vue b/src/views/demo/comp/lazy/index.vue new file mode 100644 index 00000000..4933b5c4 --- /dev/null +++ b/src/views/demo/comp/lazy/index.vue @@ -0,0 +1,47 @@ + + + diff --git a/src/views/demo/comp/loading/index.vue b/src/views/demo/comp/loading/index.vue new file mode 100644 index 00000000..aaa464db --- /dev/null +++ b/src/views/demo/comp/loading/index.vue @@ -0,0 +1,91 @@ + + diff --git a/src/views/demo/comp/modal/Modal1.vue b/src/views/demo/comp/modal/Modal1.vue new file mode 100644 index 00000000..0a5bc826 --- /dev/null +++ b/src/views/demo/comp/modal/Modal1.vue @@ -0,0 +1,59 @@ + + + diff --git a/src/views/demo/comp/modal/Modal2.vue b/src/views/demo/comp/modal/Modal2.vue new file mode 100644 index 00000000..37253772 --- /dev/null +++ b/src/views/demo/comp/modal/Modal2.vue @@ -0,0 +1,10 @@ + + diff --git a/src/views/demo/comp/modal/Modal3.vue b/src/views/demo/comp/modal/Modal3.vue new file mode 100644 index 00000000..bbe975c8 --- /dev/null +++ b/src/views/demo/comp/modal/Modal3.vue @@ -0,0 +1,8 @@ + + diff --git a/src/views/demo/comp/modal/Modal4.vue b/src/views/demo/comp/modal/Modal4.vue new file mode 100644 index 00000000..5d8f27f8 --- /dev/null +++ b/src/views/demo/comp/modal/Modal4.vue @@ -0,0 +1,73 @@ + + diff --git a/src/views/demo/comp/modal/index.vue b/src/views/demo/comp/modal/index.vue new file mode 100644 index 00000000..66835dd0 --- /dev/null +++ b/src/views/demo/comp/modal/index.vue @@ -0,0 +1,89 @@ + + diff --git a/src/views/demo/comp/qrcode/index.vue b/src/views/demo/comp/qrcode/index.vue new file mode 100644 index 00000000..f30a1bba --- /dev/null +++ b/src/views/demo/comp/qrcode/index.vue @@ -0,0 +1,103 @@ + + + diff --git a/src/views/demo/comp/scroll/Action.vue b/src/views/demo/comp/scroll/Action.vue new file mode 100644 index 00000000..bf7894ea --- /dev/null +++ b/src/views/demo/comp/scroll/Action.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/views/demo/comp/scroll/VirtualScroll.vue b/src/views/demo/comp/scroll/VirtualScroll.vue new file mode 100644 index 00000000..37427d1e --- /dev/null +++ b/src/views/demo/comp/scroll/VirtualScroll.vue @@ -0,0 +1,57 @@ + + + diff --git a/src/views/demo/comp/scroll/index.vue b/src/views/demo/comp/scroll/index.vue new file mode 100644 index 00000000..3dae1ddc --- /dev/null +++ b/src/views/demo/comp/scroll/index.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/views/demo/comp/strength-meter/index.vue b/src/views/demo/comp/strength-meter/index.vue new file mode 100644 index 00000000..28f2946e --- /dev/null +++ b/src/views/demo/comp/strength-meter/index.vue @@ -0,0 +1,24 @@ + + + + diff --git a/src/views/demo/comp/transition/index.vue b/src/views/demo/comp/transition/index.vue new file mode 100644 index 00000000..0614ea4c --- /dev/null +++ b/src/views/demo/comp/transition/index.vue @@ -0,0 +1,91 @@ + + + diff --git a/src/views/demo/comp/upload/index.vue b/src/views/demo/comp/upload/index.vue new file mode 100644 index 00000000..1f3dfe17 --- /dev/null +++ b/src/views/demo/comp/upload/index.vue @@ -0,0 +1,44 @@ + + diff --git a/src/views/demo/comp/verify/Rotate.vue b/src/views/demo/comp/verify/Rotate.vue new file mode 100644 index 00000000..5fbe6baa --- /dev/null +++ b/src/views/demo/comp/verify/Rotate.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/views/demo/comp/verify/index.vue b/src/views/demo/comp/verify/index.vue new file mode 100644 index 00000000..548fb80e --- /dev/null +++ b/src/views/demo/comp/verify/index.vue @@ -0,0 +1,83 @@ + + + diff --git a/src/views/demo/editor/json/index.vue b/src/views/demo/editor/json/index.vue new file mode 100644 index 00000000..6f0165c7 --- /dev/null +++ b/src/views/demo/editor/json/index.vue @@ -0,0 +1,88 @@ + + diff --git a/src/views/demo/editor/markdown/Editor.vue b/src/views/demo/editor/markdown/Editor.vue new file mode 100644 index 00000000..ca8e4102 --- /dev/null +++ b/src/views/demo/editor/markdown/Editor.vue @@ -0,0 +1,50 @@ + + diff --git a/src/views/demo/editor/markdown/index.vue b/src/views/demo/editor/markdown/index.vue new file mode 100644 index 00000000..2f0c2e2f --- /dev/null +++ b/src/views/demo/editor/markdown/index.vue @@ -0,0 +1,79 @@ + + diff --git a/src/views/demo/editor/tinymce/Editor.vue b/src/views/demo/editor/tinymce/Editor.vue new file mode 100644 index 00000000..530583ed --- /dev/null +++ b/src/views/demo/editor/tinymce/Editor.vue @@ -0,0 +1,51 @@ + + diff --git a/src/views/demo/editor/tinymce/index.vue b/src/views/demo/editor/tinymce/index.vue new file mode 100644 index 00000000..07cdbd3c --- /dev/null +++ b/src/views/demo/editor/tinymce/index.vue @@ -0,0 +1,15 @@ + + diff --git a/src/views/demo/excel/ArrayExport.vue b/src/views/demo/excel/ArrayExport.vue new file mode 100644 index 00000000..8bbbcc8c --- /dev/null +++ b/src/views/demo/excel/ArrayExport.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/views/demo/excel/CustomExport.vue b/src/views/demo/excel/CustomExport.vue new file mode 100644 index 00000000..5d6aeea1 --- /dev/null +++ b/src/views/demo/excel/CustomExport.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/views/demo/excel/ImportExcel.vue b/src/views/demo/excel/ImportExcel.vue new file mode 100644 index 00000000..a004fcce --- /dev/null +++ b/src/views/demo/excel/ImportExcel.vue @@ -0,0 +1,46 @@ + + diff --git a/src/views/demo/excel/JsonExport.vue b/src/views/demo/excel/JsonExport.vue new file mode 100644 index 00000000..e9199fae --- /dev/null +++ b/src/views/demo/excel/JsonExport.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/views/demo/excel/data.ts b/src/views/demo/excel/data.ts new file mode 100644 index 00000000..9d4c3e94 --- /dev/null +++ b/src/views/demo/excel/data.ts @@ -0,0 +1,59 @@ +import { BasicColumn } from '@/components/Table' + +export const columns: BasicColumn[] = [ + { + title: 'ID', + dataIndex: 'id', + width: 80 + }, + { + title: '姓名', + dataIndex: 'name', + width: 120 + }, + { + title: '年龄', + dataIndex: 'age', + width: 80 + }, + { + title: '编号', + dataIndex: 'no', + width: 80 + }, + { + title: '地址', + dataIndex: 'address' + }, + { + title: '开始时间', + dataIndex: 'beginTime' + }, + { + title: '结束时间', + dataIndex: 'endTime' + } +] + +export const data: any[] = (() => { + const arr: any[] = [] + for (let index = 0; index < 40; index++) { + arr.push({ + id: `${index}`, + name: `${index} John Brown`, + age: `${index + 10}`, + no: `${index}98678`, + address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park', + beginTime: new Date().toLocaleString(), + endTime: new Date().toLocaleString() + }) + } + return arr +})() + +// ["ID", "姓名", "年龄", "编号", "地址", "开始时间", "结束时间"] +export const arrHeader = columns.map((column) => column.title) +// [["ID", "姓名", "年龄", "编号", "地址", "开始时间", "结束时间"],["0", "0 John Brown", "10", "098678"]] +export const arrData = data.map((item) => { + return Object.keys(item).map((key) => item[key]) +}) diff --git a/src/views/demo/feat/breadcrumb/ChildrenList.vue b/src/views/demo/feat/breadcrumb/ChildrenList.vue new file mode 100644 index 00000000..aeb497a1 --- /dev/null +++ b/src/views/demo/feat/breadcrumb/ChildrenList.vue @@ -0,0 +1,8 @@ + + diff --git a/src/views/demo/feat/breadcrumb/ChildrenListDetail.vue b/src/views/demo/feat/breadcrumb/ChildrenListDetail.vue new file mode 100644 index 00000000..351cf1ed --- /dev/null +++ b/src/views/demo/feat/breadcrumb/ChildrenListDetail.vue @@ -0,0 +1,8 @@ + + diff --git a/src/views/demo/feat/breadcrumb/FlatList.vue b/src/views/demo/feat/breadcrumb/FlatList.vue new file mode 100644 index 00000000..d5c226e9 --- /dev/null +++ b/src/views/demo/feat/breadcrumb/FlatList.vue @@ -0,0 +1,8 @@ + + diff --git a/src/views/demo/feat/breadcrumb/FlatListDetail.vue b/src/views/demo/feat/breadcrumb/FlatListDetail.vue new file mode 100644 index 00000000..0be7d273 --- /dev/null +++ b/src/views/demo/feat/breadcrumb/FlatListDetail.vue @@ -0,0 +1,4 @@ + + diff --git a/src/views/demo/feat/context-menu/index.vue b/src/views/demo/feat/context-menu/index.vue new file mode 100644 index 00000000..71b85286 --- /dev/null +++ b/src/views/demo/feat/context-menu/index.vue @@ -0,0 +1,77 @@ + + diff --git a/src/views/demo/feat/copy/index.vue b/src/views/demo/feat/copy/index.vue new file mode 100644 index 00000000..8f2b99e2 --- /dev/null +++ b/src/views/demo/feat/copy/index.vue @@ -0,0 +1,33 @@ + + diff --git a/src/views/demo/feat/download/imgBase64.ts b/src/views/demo/feat/download/imgBase64.ts new file mode 100644 index 00000000..2af85464 --- /dev/null +++ b/src/views/demo/feat/download/imgBase64.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line max-len +export default `data:image/png;base64,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` diff --git a/src/views/demo/feat/download/index.vue b/src/views/demo/feat/download/index.vue new file mode 100644 index 00000000..58880ee3 --- /dev/null +++ b/src/views/demo/feat/download/index.vue @@ -0,0 +1,47 @@ + + diff --git a/src/views/demo/feat/full-screen/index.vue b/src/views/demo/feat/full-screen/index.vue new file mode 100644 index 00000000..d14b1333 --- /dev/null +++ b/src/views/demo/feat/full-screen/index.vue @@ -0,0 +1,32 @@ + + diff --git a/src/views/demo/feat/icon/index.vue b/src/views/demo/feat/icon/index.vue new file mode 100644 index 00000000..9c42c535 --- /dev/null +++ b/src/views/demo/feat/icon/index.vue @@ -0,0 +1,74 @@ + + diff --git a/src/views/demo/feat/img-preview/index.vue b/src/views/demo/feat/img-preview/index.vue new file mode 100644 index 00000000..aacebb02 --- /dev/null +++ b/src/views/demo/feat/img-preview/index.vue @@ -0,0 +1,24 @@ + + diff --git a/src/views/demo/feat/menu-params/index.vue b/src/views/demo/feat/menu-params/index.vue new file mode 100644 index 00000000..ebd70e78 --- /dev/null +++ b/src/views/demo/feat/menu-params/index.vue @@ -0,0 +1,32 @@ + + diff --git a/src/views/demo/feat/msg/index.vue b/src/views/demo/feat/msg/index.vue new file mode 100644 index 00000000..52c672cf --- /dev/null +++ b/src/views/demo/feat/msg/index.vue @@ -0,0 +1,67 @@ + + diff --git a/src/views/demo/feat/print/index.vue b/src/views/demo/feat/print/index.vue new file mode 100644 index 00000000..a991dc39 --- /dev/null +++ b/src/views/demo/feat/print/index.vue @@ -0,0 +1,35 @@ + + diff --git a/src/views/demo/feat/request-demo/index.vue b/src/views/demo/feat/request-demo/index.vue new file mode 100644 index 00000000..9f7bfda2 --- /dev/null +++ b/src/views/demo/feat/request-demo/index.vue @@ -0,0 +1,22 @@ + + + + diff --git a/src/views/demo/feat/ripple/index.vue b/src/views/demo/feat/ripple/index.vue new file mode 100644 index 00000000..153acdfd --- /dev/null +++ b/src/views/demo/feat/ripple/index.vue @@ -0,0 +1,31 @@ + + + + diff --git a/src/views/demo/feat/session-timeout/index.vue b/src/views/demo/feat/session-timeout/index.vue new file mode 100644 index 00000000..96a0d6c2 --- /dev/null +++ b/src/views/demo/feat/session-timeout/index.vue @@ -0,0 +1,43 @@ + + diff --git a/src/views/demo/feat/tab-params/index.vue b/src/views/demo/feat/tab-params/index.vue new file mode 100644 index 00000000..d5f92eac --- /dev/null +++ b/src/views/demo/feat/tab-params/index.vue @@ -0,0 +1,19 @@ + + diff --git a/src/views/demo/feat/tabs/TabDetail.vue b/src/views/demo/feat/tabs/TabDetail.vue new file mode 100644 index 00000000..2218babc --- /dev/null +++ b/src/views/demo/feat/tabs/TabDetail.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/views/demo/feat/tabs/index.vue b/src/views/demo/feat/tabs/index.vue new file mode 100644 index 00000000..5175dcb6 --- /dev/null +++ b/src/views/demo/feat/tabs/index.vue @@ -0,0 +1,49 @@ + + diff --git a/src/views/demo/feat/watermark/index.vue b/src/views/demo/feat/watermark/index.vue new file mode 100644 index 00000000..be2d3297 --- /dev/null +++ b/src/views/demo/feat/watermark/index.vue @@ -0,0 +1,16 @@ + + diff --git a/src/views/demo/feat/ws/index.vue b/src/views/demo/feat/ws/index.vue new file mode 100644 index 00000000..02ab811a --- /dev/null +++ b/src/views/demo/feat/ws/index.vue @@ -0,0 +1,101 @@ + + diff --git a/src/views/demo/form/AdvancedForm.vue b/src/views/demo/form/AdvancedForm.vue new file mode 100644 index 00000000..c218204c --- /dev/null +++ b/src/views/demo/form/AdvancedForm.vue @@ -0,0 +1,180 @@ + + diff --git a/src/views/demo/form/AppendForm.vue b/src/views/demo/form/AppendForm.vue new file mode 100644 index 00000000..0367b9ed --- /dev/null +++ b/src/views/demo/form/AppendForm.vue @@ -0,0 +1,122 @@ + + diff --git a/src/views/demo/form/CustomerForm.vue b/src/views/demo/form/CustomerForm.vue new file mode 100644 index 00000000..a97b452f --- /dev/null +++ b/src/views/demo/form/CustomerForm.vue @@ -0,0 +1,75 @@ + + diff --git a/src/views/demo/form/DynamicForm.vue b/src/views/demo/form/DynamicForm.vue new file mode 100644 index 00000000..e4eab082 --- /dev/null +++ b/src/views/demo/form/DynamicForm.vue @@ -0,0 +1,228 @@ + + diff --git a/src/views/demo/form/RefForm.vue b/src/views/demo/form/RefForm.vue new file mode 100644 index 00000000..915e05a7 --- /dev/null +++ b/src/views/demo/form/RefForm.vue @@ -0,0 +1,165 @@ + + diff --git a/src/views/demo/form/RuleForm.vue b/src/views/demo/form/RuleForm.vue new file mode 100644 index 00000000..b66c0e71 --- /dev/null +++ b/src/views/demo/form/RuleForm.vue @@ -0,0 +1,241 @@ + + diff --git a/src/views/demo/form/TabsForm.vue b/src/views/demo/form/TabsForm.vue new file mode 100644 index 00000000..3cdae5cb --- /dev/null +++ b/src/views/demo/form/TabsForm.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/src/views/demo/form/UseForm.vue b/src/views/demo/form/UseForm.vue new file mode 100644 index 00000000..8c37c834 --- /dev/null +++ b/src/views/demo/form/UseForm.vue @@ -0,0 +1,466 @@ + + + diff --git a/src/views/demo/form/index.vue b/src/views/demo/form/index.vue new file mode 100644 index 00000000..5f04bce1 --- /dev/null +++ b/src/views/demo/form/index.vue @@ -0,0 +1,684 @@ + + diff --git a/src/views/demo/level/Menu111.vue b/src/views/demo/level/Menu111.vue new file mode 100644 index 00000000..9fcd5c0c --- /dev/null +++ b/src/views/demo/level/Menu111.vue @@ -0,0 +1,10 @@ + + diff --git a/src/views/demo/level/Menu12.vue b/src/views/demo/level/Menu12.vue new file mode 100644 index 00000000..4d53543f --- /dev/null +++ b/src/views/demo/level/Menu12.vue @@ -0,0 +1,10 @@ + + diff --git a/src/views/demo/level/Menu2.vue b/src/views/demo/level/Menu2.vue new file mode 100644 index 00000000..34f72c87 --- /dev/null +++ b/src/views/demo/level/Menu2.vue @@ -0,0 +1,10 @@ + + diff --git a/src/views/demo/main-out/index.vue b/src/views/demo/main-out/index.vue new file mode 100644 index 00000000..2406632f --- /dev/null +++ b/src/views/demo/main-out/index.vue @@ -0,0 +1,6 @@ + diff --git a/src/views/demo/page/account/center/Application.vue b/src/views/demo/page/account/center/Application.vue new file mode 100644 index 00000000..49134c89 --- /dev/null +++ b/src/views/demo/page/account/center/Application.vue @@ -0,0 +1,74 @@ + + + diff --git a/src/views/demo/page/account/center/Article.vue b/src/views/demo/page/account/center/Article.vue new file mode 100644 index 00000000..d2bbdde9 --- /dev/null +++ b/src/views/demo/page/account/center/Article.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/views/demo/page/account/center/Project.vue b/src/views/demo/page/account/center/Project.vue new file mode 100644 index 00000000..a55916aa --- /dev/null +++ b/src/views/demo/page/account/center/Project.vue @@ -0,0 +1,56 @@ + + + diff --git a/src/views/demo/page/account/center/data.tsx b/src/views/demo/page/account/center/data.tsx new file mode 100644 index 00000000..9e4b34e8 --- /dev/null +++ b/src/views/demo/page/account/center/data.tsx @@ -0,0 +1,124 @@ +export interface ListItem { + title: string + icon: string + color?: string +} + +export interface TabItem { + key: string + name: string + component: string +} + +export const tags: string[] = ['很有想法的', '专注设计', '川妹子', '大长腿', '海纳百川', '前端开发', 'vue3'] + +export const teams: ListItem[] = [ + { + icon: 'ri:alipay-fill', + title: '科学搬砖组', + color: '#ff4000' + }, + { + icon: 'emojione-monotone:letter-a', + title: '中二少年团', + color: '#7c51b8' + }, + { + icon: 'ri:alipay-fill', + title: '高逼格设计', + color: '#00adf7' + }, + { + icon: 'jam:codepen-circle', + title: '程序员日常', + color: '#00adf7' + }, + { + icon: 'fa:behance-square', + title: '科学搬砖组', + color: '#7c51b8' + }, + { + icon: 'jam:codepen-circle', + title: '程序员日常', + color: '#ff4000' + } +] + +export const details: ListItem[] = [ + { + icon: 'ic:outline-contacts', + title: '交互专家' + }, + { + icon: 'grommet-icons:cluster', + title: '某某某事业群' + }, + { + icon: 'bx:bx-home-circle', + title: '福建省厦门市' + } +] + +export const achieveList: TabItem[] = [ + { + key: '1', + name: '文章', + component: 'Article' + }, + { + key: '2', + name: '应用', + component: 'Application' + }, + { + key: '3', + name: '项目', + component: 'Project' + } +] + +export const actions: any[] = [ + { icon: 'clarity:star-line', text: '156', color: '#018ffb' }, + { icon: 'bx:bxs-like', text: '156', color: '#459ae8' }, + { icon: 'bx:bxs-message-dots', text: '2', color: '#42d27d' } +] + +export const articleList = (() => { + const result: any[] = [] + for (let i = 0; i < 4; i++) { + result.push({ + title: 'Vben Admin', + description: ['Vben', '设计语言', 'Typescript'], + content: '基于Vue Next, TypeScript, Ant Design实现的一套完整的企业级后台管理系统。', + time: '2020-11-14 11:20' + }) + } + return result +})() + +export const applicationList = (() => { + const result: any[] = [] + for (let i = 0; i < 8; i++) { + result.push({ + title: 'Vben Admin', + icon: 'emojione-monotone:letter-a', + color: '#1890ff', + active: '100', + new: '1,799', + download: 'bx:bx-download' + }) + } + return result +})() + +export const projectList = (() => { + const result: any[] = [] + for (let i = 0; i < 8; i++) { + result.push({ + title: 'Vben Admin', + content: '基于Vue Next, TypeScript, Ant Design实现的一套完整的企业级后台管理系统。' + }) + } + return result +})() diff --git a/src/views/demo/page/account/center/index.vue b/src/views/demo/page/account/center/index.vue new file mode 100644 index 00000000..ecef77d3 --- /dev/null +++ b/src/views/demo/page/account/center/index.vue @@ -0,0 +1,155 @@ + + + + diff --git a/src/views/demo/page/account/setting/AccountBind.vue b/src/views/demo/page/account/setting/AccountBind.vue new file mode 100644 index 00000000..7e6917e9 --- /dev/null +++ b/src/views/demo/page/account/setting/AccountBind.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/views/demo/page/account/setting/BaseSetting.vue b/src/views/demo/page/account/setting/BaseSetting.vue new file mode 100644 index 00000000..83fa66cb --- /dev/null +++ b/src/views/demo/page/account/setting/BaseSetting.vue @@ -0,0 +1,78 @@ + + + + diff --git a/src/views/demo/page/account/setting/MsgNotify.vue b/src/views/demo/page/account/setting/MsgNotify.vue new file mode 100644 index 00000000..91576233 --- /dev/null +++ b/src/views/demo/page/account/setting/MsgNotify.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/views/demo/page/account/setting/SecureSetting.vue b/src/views/demo/page/account/setting/SecureSetting.vue new file mode 100644 index 00000000..1b5246e9 --- /dev/null +++ b/src/views/demo/page/account/setting/SecureSetting.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/views/demo/page/account/setting/data.ts b/src/views/demo/page/account/setting/data.ts new file mode 100644 index 00000000..effcf51a --- /dev/null +++ b/src/views/demo/page/account/setting/data.ts @@ -0,0 +1,149 @@ +import { FormSchema } from '@/components/Form' + +export interface ListItem { + key: string + title: string + description: string + extra?: string + avatar?: string + color?: string +} + +// tab的list +export const settingList = [ + { + key: '1', + name: '基本设置', + component: 'BaseSetting' + }, + { + key: '2', + name: '安全设置', + component: 'SecureSetting' + }, + { + key: '3', + name: '账号绑定', + component: 'AccountBind' + }, + { + key: '4', + name: '新消息通知', + component: 'MsgNotify' + } +] + +// 基础设置 form +export const baseSetschemas: FormSchema[] = [ + { + field: 'email', + component: 'Input', + label: '邮箱', + colProps: { span: 18 } + }, + { + field: 'name', + component: 'Input', + label: '昵称', + colProps: { span: 18 } + }, + { + field: 'introduction', + component: 'InputTextArea', + label: '个人简介', + colProps: { span: 18 } + }, + { + field: 'phone', + component: 'Input', + label: '联系电话', + colProps: { span: 18 } + }, + { + field: 'address', + component: 'Input', + label: '所在地区', + colProps: { span: 18 } + } +] + +// 安全设置 list +export const secureSettingList: ListItem[] = [ + { + key: '1', + title: '账户密码', + description: '当前密码强度::强', + extra: '修改' + }, + { + key: '2', + title: '密保手机', + description: '已绑定手机::138****8293', + extra: '修改' + }, + { + key: '3', + title: '密保问题', + description: '未设置密保问题,密保问题可有效保护账户安全', + extra: '修改' + }, + { + key: '4', + title: '备用邮箱', + description: '已绑定邮箱::ant***sign.com', + extra: '修改' + }, + { + key: '5', + title: 'MFA 设备', + description: '未绑定 MFA 设备,绑定后,可以进行二次确认', + extra: '修改' + } +] + +// 账号绑定 list +export const accountBindList: ListItem[] = [ + { + key: '1', + title: '绑定淘宝', + description: '当前未绑定淘宝账号', + extra: '绑定', + avatar: 'ri:taobao-fill', + color: '#ff4000' + }, + { + key: '2', + title: '绑定支付宝', + description: '当前未绑定支付宝账号', + extra: '绑定', + avatar: 'fa-brands:alipay', + color: '#2eabff' + }, + { + key: '3', + title: '绑定钉钉', + description: '当前未绑定钉钉账号', + extra: '绑定', + avatar: 'ri:dingding-fill', + color: '#2eabff' + } +] + +// 新消息通知 list +export const msgNotifyList: ListItem[] = [ + { + key: '1', + title: '账户密码', + description: '其他用户的消息将以站内信的形式通知' + }, + { + key: '2', + title: '系统消息', + description: '系统消息将以站内信的形式通知' + }, + { + key: '3', + title: '待办任务', + description: '待办任务将以站内信的形式通知' + } +] diff --git a/src/views/demo/page/account/setting/index.vue b/src/views/demo/page/account/setting/index.vue new file mode 100644 index 00000000..29bb0f8f --- /dev/null +++ b/src/views/demo/page/account/setting/index.vue @@ -0,0 +1,61 @@ + + + + diff --git a/src/views/demo/page/desc/basic/data.tsx b/src/views/demo/page/desc/basic/data.tsx new file mode 100644 index 00000000..5d997a24 --- /dev/null +++ b/src/views/demo/page/desc/basic/data.tsx @@ -0,0 +1,196 @@ +import { DescItem } from '@/components/Description' +import { BasicColumn } from '@/components/Table/src/types/table' +import { Button } from '@/components/Button' + +import { Badge } from 'ant-design-vue' + +export const refundData = { + a1: '1000000000', + a2: '已取货', + a3: '1234123421', + a4: '3214321432' +} + +export const personData = { + b1: '付小小', + b2: '18100000000', + b3: '菜鸟仓储', + b4: '浙江省杭州市西湖区万塘路18号', + b5: '无' +} +export const refundSchema: DescItem[] = [ + { + field: 'a1', + label: '取货单号' + }, + { + field: 'a2', + label: '状态' + }, + { + field: 'a3', + label: '销售单号' + }, + { + field: 'a4', + label: '子订单' + } +] +export const personSchema: DescItem[] = [ + { + field: 'b1', + label: '用户姓名' + }, + { + field: 'b2', + label: '联系电话' + }, + { + field: 'b3', + label: '常用快递' + }, + { + field: 'b4', + label: '取货地址' + }, + { + field: 'b5', + label: '备注' + } +] + +export const refundTableSchema: BasicColumn[] = [ + { + title: '商品编号', + width: 150, + dataIndex: 't1', + customRender: ({ record }) => { + return ( + + ) + } + }, + { + title: '商品名称', + width: 150, + dataIndex: 't2' + }, + { + title: '商品条码', + width: 150, + dataIndex: 't3' + }, + { + title: '单价 ', + width: 150, + dataIndex: 't4' + }, + { + title: '数量(件) ', + width: 150, + dataIndex: 't5' + }, + { + title: '金额', + width: 150, + dataIndex: 't6' + } +] +export const refundTimeTableSchema: BasicColumn[] = [ + { + title: '时间', + width: 150, + dataIndex: 't1' + }, + { + title: '当前进度', + width: 150, + dataIndex: 't2' + }, + { + title: '状态', + width: 150, + dataIndex: 't3', + customRender: ({ record }) => { + return + } + }, + { + title: '操作员ID ', + width: 150, + dataIndex: 't4' + }, + { + title: '耗时', + width: 150, + dataIndex: 't5' + } +] + +export const refundTableData: any[] = [ + { + t1: 1234561, + t2: '矿泉水 550ml', + t3: '12421432143214321', + t4: '2.00', + t5: 1, + t6: 2.0 + }, + { + t1: 1234562, + t2: '矿泉水 550ml', + t3: '12421432143214321', + t4: '2.00', + t5: 2, + t6: 2.0 + }, + { + t1: 1234562, + t2: '矿泉水 550ml', + t3: '12421432143214321', + t4: '2.00', + t5: 2, + t6: 2.0 + }, + { + t1: 1234562, + t2: '矿泉水 550ml', + t3: '12421432143214321', + t4: '2.00', + t5: 2, + t6: 2.0 + } +] + +export const refundTimeTableData: any[] = [ + { + t1: '2017-10-01 14:10', + t2: '联系客户', + t3: '进行中', + t4: '取货员 ID1234', + t5: '5mins' + }, + { + t1: '2017-10-01 14:10', + t2: '取货员出发', + t3: '成功', + t4: '取货员 ID1234', + t5: '5mins' + }, + { + t1: '2017-10-01 14:10', + t2: '取货员接单', + t3: '成功', + t4: '系统', + t5: '5mins' + }, + { + t1: '2017-10-01 14:10', + t2: '申请审批通过', + t3: '成功', + t4: '用户', + t5: '1h' + } +] diff --git a/src/views/demo/page/desc/basic/index.vue b/src/views/demo/page/desc/basic/index.vue new file mode 100644 index 00000000..b40f2408 --- /dev/null +++ b/src/views/demo/page/desc/basic/index.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/views/demo/page/desc/high/data.tsx b/src/views/demo/page/desc/high/data.tsx new file mode 100644 index 00000000..458726db --- /dev/null +++ b/src/views/demo/page/desc/high/data.tsx @@ -0,0 +1,65 @@ +import { BasicColumn } from '@/components/Table/src/types/table' + +import { Badge } from 'ant-design-vue' + +export const refundTimeTableSchema: BasicColumn[] = [ + { + title: '时间', + width: 150, + dataIndex: 't1' + }, + { + title: '当前进度', + width: 150, + dataIndex: 't2' + }, + { + title: '状态', + width: 150, + dataIndex: 't3', + customRender: ({ record }) => { + return + } + }, + { + title: '操作员ID ', + width: 150, + dataIndex: 't4' + }, + { + title: '耗时', + width: 150, + dataIndex: 't5' + } +] + +export const refundTimeTableData: any[] = [ + { + t1: '2017-10-01 14:10', + t2: '联系客户', + t3: '进行中', + t4: '取货员 ID1234', + t5: '5mins' + }, + { + t1: '2017-10-01 14:10', + t2: '取货员出发', + t3: '成功', + t4: '取货员 ID1234', + t5: '5mins' + }, + { + t1: '2017-10-01 14:10', + t2: '取货员接单', + t3: '成功', + t4: '系统', + t5: '5mins' + }, + { + t1: '2017-10-01 14:10', + t2: '申请审批通过', + t3: '成功', + t4: '用户', + t5: '1h' + } +] diff --git a/src/views/demo/page/desc/high/index.vue b/src/views/demo/page/desc/high/index.vue new file mode 100644 index 00000000..50f767e8 --- /dev/null +++ b/src/views/demo/page/desc/high/index.vue @@ -0,0 +1,109 @@ + + diff --git a/src/views/demo/page/form/basic/data.ts b/src/views/demo/page/form/basic/data.ts new file mode 100644 index 00000000..022dafd5 --- /dev/null +++ b/src/views/demo/page/form/basic/data.ts @@ -0,0 +1,138 @@ +import { FormSchema } from '@/components/Form' +const colProps = { + span: 8 +} + +export const schemas: FormSchema[] = [ + { + field: 'title', + component: 'Input', + label: '标题', + colProps, + componentProps: { + placeholder: '给目标起个名字' + }, + required: true + }, + { + field: 'time', + component: 'RangePicker', + label: '起止日期', + colProps, + required: true + }, + { + field: 'client', + component: 'Input', + colProps, + label: '客户', + helpMessage: '目标的服务对象', + subLabel: '( 选填 )', + componentProps: { + placeholder: '请描述你服务的客户,内部客户直接 @姓名/工号' + } + }, + { + field: 'weights', + component: 'InputNumber', + label: '权重', + colProps, + subLabel: '( 选填 )', + componentProps: { + formatter: (value: string) => (value ? `${value}%` : ''), + parser: (value: string) => value.replace('%', ''), + placeholder: '请输入' + } + }, + { + field: 'target', + component: 'InputTextArea', + label: '目标描述', + colProps, + componentProps: { + placeholder: '请输入你的阶段性工作目标', + rows: 4 + }, + required: true + }, + { + field: 'metrics', + component: 'InputTextArea', + label: '衡量标准', + colProps, + componentProps: { + placeholder: '请输入衡量标准', + rows: 4 + }, + required: true + }, + + { + field: 'inviteer', + component: 'Input', + label: '邀评人', + colProps: { + span: 8 + }, + subLabel: '( 选填 )', + componentProps: { + placeholder: '请直接 @姓名/工号,最多可邀请 5 人' + } + }, + { + field: 'disclosure', + component: 'RadioGroup', + label: '目标公开', + colProps: { + span: 16 + }, + itemProps: { + extra: '客户、邀评人默认被分享' + }, + componentProps: { + options: [ + { + label: '公开', + value: '1' + }, + { + label: '部分公开', + value: '2' + }, + { + label: '不公开', + value: '3' + } + ] + } + }, + { + field: 'disclosure', + component: 'Select', + label: ' ', + colProps: { + span: 8 + }, + show: ({ model }) => { + return model.disclosure === '2' + }, + componentProps: { + placeholder: '公开给', + mode: 'multiple', + options: [ + { + label: '同事1', + value: '1' + }, + { + label: '同事2', + value: '2' + }, + { + label: '同事3', + value: '3' + } + ] + } + } +] diff --git a/src/views/demo/page/form/basic/index.vue b/src/views/demo/page/form/basic/index.vue new file mode 100644 index 00000000..2089a92c --- /dev/null +++ b/src/views/demo/page/form/basic/index.vue @@ -0,0 +1,60 @@ + + + diff --git a/src/views/demo/page/form/high/PersonTable.vue b/src/views/demo/page/form/high/PersonTable.vue new file mode 100644 index 00000000..f16f3929 --- /dev/null +++ b/src/views/demo/page/form/high/PersonTable.vue @@ -0,0 +1,125 @@ + + diff --git a/src/views/demo/page/form/high/data.ts b/src/views/demo/page/form/high/data.ts new file mode 100644 index 00000000..9153874d --- /dev/null +++ b/src/views/demo/page/form/high/data.ts @@ -0,0 +1,149 @@ +import { FormSchema } from '@/components/Form' + +const basicOptions: LabelValueOptions = [ + { + label: '付晓晓', + value: '1' + }, + { + label: '周毛毛', + value: '2' + } +] + +const storeTypeOptions: LabelValueOptions = [ + { + label: '私密', + value: '1' + }, + { + label: '公开', + value: '2' + } +] + +export const schemas: FormSchema[] = [ + { + field: 'f1', + component: 'Input', + label: '仓库名', + required: true + }, + { + field: 'f2', + component: 'Input', + label: '仓库域名', + required: true, + componentProps: { + addonBefore: 'http://', + addonAfter: 'com' + }, + colProps: { + offset: 2 + } + }, + { + field: 'f3', + component: 'Select', + label: '仓库管理员', + componentProps: { + options: basicOptions + }, + required: true, + colProps: { + offset: 2 + } + }, + { + field: 'f4', + component: 'Select', + label: '审批人', + componentProps: { + options: basicOptions + }, + required: true + }, + { + field: 'f5', + component: 'RangePicker', + label: '生效日期', + required: true, + colProps: { + offset: 2 + } + }, + { + field: 'f6', + component: 'Select', + label: '仓库类型', + componentProps: { + options: storeTypeOptions + }, + required: true, + colProps: { + offset: 2 + } + } +] +export const taskSchemas: FormSchema[] = [ + { + field: 't1', + component: 'Input', + label: '任务名', + required: true + }, + { + field: 't2', + component: 'Input', + label: '任务描述', + required: true, + colProps: { + offset: 2 + } + }, + { + field: 't3', + component: 'Select', + label: '执行人', + componentProps: { + options: basicOptions + }, + required: true, + colProps: { + offset: 2 + } + }, + { + field: 't4', + component: 'Select', + label: '责任人', + componentProps: { + options: basicOptions + }, + required: true + }, + { + field: 't5', + component: 'TimePicker', + label: '生效日期', + required: true, + componentProps: { + style: { width: '100%' } + }, + colProps: { + offset: 2 + } + }, + { + field: 't6', + component: 'Select', + label: '任务类型', + componentProps: { + options: storeTypeOptions + }, + required: true, + colProps: { + offset: 2 + } + } +] diff --git a/src/views/demo/page/form/high/index.vue b/src/views/demo/page/form/high/index.vue new file mode 100644 index 00000000..06c758b1 --- /dev/null +++ b/src/views/demo/page/form/high/index.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/views/demo/page/form/step/Step1.vue b/src/views/demo/page/form/step/Step1.vue new file mode 100644 index 00000000..3546bde0 --- /dev/null +++ b/src/views/demo/page/form/step/Step1.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/views/demo/page/form/step/Step2.vue b/src/views/demo/page/form/step/Step2.vue new file mode 100644 index 00000000..e2574f0b --- /dev/null +++ b/src/views/demo/page/form/step/Step2.vue @@ -0,0 +1,65 @@ + + + diff --git a/src/views/demo/page/form/step/Step3.vue b/src/views/demo/page/form/step/Step3.vue new file mode 100644 index 00000000..a64307fc --- /dev/null +++ b/src/views/demo/page/form/step/Step3.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/views/demo/page/form/step/data.tsx b/src/views/demo/page/form/step/data.tsx new file mode 100644 index 00000000..99ce8c48 --- /dev/null +++ b/src/views/demo/page/form/step/data.tsx @@ -0,0 +1,78 @@ +import { FormSchema } from '@/components/Form' + +export const step1Schemas: FormSchema[] = [ + { + field: 'account', + component: 'Select', + label: '付款账户', + required: true, + defaultValue: '1', + componentProps: { + options: [ + { + label: 'anncwb@126.com', + value: '1' + } + ] + }, + colProps: { + span: 24 + } + }, + { + field: 'fac', + component: 'InputGroup', + label: '收款账户', + required: true, + defaultValue: 'test@example.com', + slot: 'fac', + colProps: { + span: 24 + } + }, + { + field: 'pay', + component: 'Input', + label: '', + defaultValue: 'zfb', + show: false + }, + { + field: 'payeeName', + component: 'Input', + label: '收款人姓名', + defaultValue: 'Vben', + required: true, + colProps: { + span: 24 + } + }, + { + field: 'money', + component: 'Input', + label: '转账金额', + defaultValue: '500', + required: true, + renderComponentContent: () => { + return { + prefix: () => '¥' + } + }, + colProps: { + span: 24 + } + } +] + +export const step2Schemas: FormSchema[] = [ + { + field: 'pwd', + component: 'InputPassword', + label: '支付密码', + required: true, + defaultValue: '123456', + colProps: { + span: 24 + } + } +] diff --git a/src/views/demo/page/form/step/index.vue b/src/views/demo/page/form/step/index.vue new file mode 100644 index 00000000..424f2c94 --- /dev/null +++ b/src/views/demo/page/form/step/index.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/views/demo/page/list/basic/data.tsx b/src/views/demo/page/list/basic/data.tsx new file mode 100644 index 00000000..989bb978 --- /dev/null +++ b/src/views/demo/page/list/basic/data.tsx @@ -0,0 +1,17 @@ +export const cardList = (() => { + const result: any[] = [] + for (let i = 0; i < 6; i++) { + result.push({ + id: i, + title: 'Vben Admin', + description: '基于Vue Next, TypeScript, Ant Design Vue实现的一套完整的企业级后台管理系统', + datetime: '2020-11-26 17:39', + extra: '编辑', + icon: 'logos:vue', + color: '#1890ff', + author: 'Vben', + percent: 20 * (i + 1) + }) + } + return result +})() diff --git a/src/views/demo/page/list/basic/index.vue b/src/views/demo/page/list/basic/index.vue new file mode 100644 index 00000000..c460eab5 --- /dev/null +++ b/src/views/demo/page/list/basic/index.vue @@ -0,0 +1,146 @@ + + + diff --git a/src/views/demo/page/list/card/data.tsx b/src/views/demo/page/list/card/data.tsx new file mode 100644 index 00000000..13df288d --- /dev/null +++ b/src/views/demo/page/list/card/data.tsx @@ -0,0 +1,14 @@ +export const cardList = (() => { + const result: any[] = [] + for (let i = 0; i < 12; i++) { + result.push({ + title: 'Vben Admin', + icon: 'logos:vue', + color: '#1890ff', + active: '100', + new: '1,799', + download: 'bx:bx-download' + }) + } + return result +})() diff --git a/src/views/demo/page/list/card/index.vue b/src/views/demo/page/list/card/index.vue new file mode 100644 index 00000000..86b6772a --- /dev/null +++ b/src/views/demo/page/list/card/index.vue @@ -0,0 +1,89 @@ + + + diff --git a/src/views/demo/page/list/search/data.tsx b/src/views/demo/page/list/search/data.tsx new file mode 100644 index 00000000..d2f8e416 --- /dev/null +++ b/src/views/demo/page/list/search/data.tsx @@ -0,0 +1,37 @@ +import { FormSchema } from '@/components/Form' + +export const searchList = (() => { + const result: any[] = [] + for (let i = 0; i < 6; i++) { + result.push({ + id: i, + title: 'Vben Admin', + description: ['Vben', '设计语言', 'Typescript'], + content: '基于Vue Next, TypeScript, Ant Design实现的一套完整的企业级后台管理系统。', + time: '2020-11-14 11:20' + }) + } + return result +})() + +export const actions: any[] = [ + { icon: 'clarity:star-line', text: '156', color: '#018ffb' }, + { icon: 'bx:bxs-like', text: '156', color: '#459ae8' }, + { icon: 'bx:bxs-message-dots', text: '2', color: '#42d27d' } +] + +export const schemas: FormSchema[] = [ + { + field: 'field1', + component: 'InputSearch', + label: '项目名', + colProps: { + span: 8 + }, + componentProps: { + onChange: (e: any) => { + console.log(e) + } + } + } +] diff --git a/src/views/demo/page/list/search/index.vue b/src/views/demo/page/list/search/index.vue new file mode 100644 index 00000000..b354de81 --- /dev/null +++ b/src/views/demo/page/list/search/index.vue @@ -0,0 +1,108 @@ + + + diff --git a/src/views/demo/page/result/fail/index.vue b/src/views/demo/page/result/fail/index.vue new file mode 100644 index 00000000..41f338f1 --- /dev/null +++ b/src/views/demo/page/result/fail/index.vue @@ -0,0 +1,47 @@ + + + diff --git a/src/views/demo/page/result/success/index.vue b/src/views/demo/page/result/success/index.vue new file mode 100644 index 00000000..2eadc301 --- /dev/null +++ b/src/views/demo/page/result/success/index.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/views/demo/permission/CurrentPermissionMode.vue b/src/views/demo/permission/CurrentPermissionMode.vue new file mode 100644 index 00000000..4fd5d5f1 --- /dev/null +++ b/src/views/demo/permission/CurrentPermissionMode.vue @@ -0,0 +1,20 @@ + + diff --git a/src/views/demo/permission/back/Btn.vue b/src/views/demo/permission/back/Btn.vue new file mode 100644 index 00000000..aa4035af --- /dev/null +++ b/src/views/demo/permission/back/Btn.vue @@ -0,0 +1,76 @@ + + + diff --git a/src/views/demo/permission/back/index.vue b/src/views/demo/permission/back/index.vue new file mode 100644 index 00000000..e732f137 --- /dev/null +++ b/src/views/demo/permission/back/index.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/views/demo/permission/front/AuthPageA.vue b/src/views/demo/permission/front/AuthPageA.vue new file mode 100644 index 00000000..8f4da517 --- /dev/null +++ b/src/views/demo/permission/front/AuthPageA.vue @@ -0,0 +1,16 @@ + + + diff --git a/src/views/demo/permission/front/AuthPageB.vue b/src/views/demo/permission/front/AuthPageB.vue new file mode 100644 index 00000000..817bc71c --- /dev/null +++ b/src/views/demo/permission/front/AuthPageB.vue @@ -0,0 +1,16 @@ + + + diff --git a/src/views/demo/permission/front/Btn.vue b/src/views/demo/permission/front/Btn.vue new file mode 100644 index 00000000..3965eb0c --- /dev/null +++ b/src/views/demo/permission/front/Btn.vue @@ -0,0 +1,73 @@ + + + diff --git a/src/views/demo/permission/front/index.vue b/src/views/demo/permission/front/index.vue new file mode 100644 index 00000000..0a2ba400 --- /dev/null +++ b/src/views/demo/permission/front/index.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/views/demo/setup/index.vue b/src/views/demo/setup/index.vue new file mode 100644 index 00000000..376078b1 --- /dev/null +++ b/src/views/demo/setup/index.vue @@ -0,0 +1,36 @@ + + diff --git a/src/views/demo/system/account/AccountDetail.vue b/src/views/demo/system/account/AccountDetail.vue new file mode 100644 index 00000000..1e0c6955 --- /dev/null +++ b/src/views/demo/system/account/AccountDetail.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/src/views/demo/system/account/AccountModal.vue b/src/views/demo/system/account/AccountModal.vue new file mode 100644 index 00000000..dcb2b7ac --- /dev/null +++ b/src/views/demo/system/account/AccountModal.vue @@ -0,0 +1,66 @@ + + diff --git a/src/views/demo/system/account/DeptTree.vue b/src/views/demo/system/account/DeptTree.vue new file mode 100644 index 00000000..be59cb23 --- /dev/null +++ b/src/views/demo/system/account/DeptTree.vue @@ -0,0 +1,35 @@ + + diff --git a/src/views/demo/system/account/account.data.ts b/src/views/demo/system/account/account.data.ts new file mode 100644 index 00000000..40019624 --- /dev/null +++ b/src/views/demo/system/account/account.data.ts @@ -0,0 +1,127 @@ +import { getAllRoleList, isAccountExist } from '@/api/demo/system' +import { BasicColumn } from '@/components/Table' +import { FormSchema } from '@/components/Table' + +export const columns: BasicColumn[] = [ + { + title: '用户名', + dataIndex: 'account', + width: 120 + }, + { + title: '昵称', + dataIndex: 'nickname', + width: 120 + }, + { + title: '邮箱', + dataIndex: 'email', + width: 120 + }, + { + title: '创建时间', + dataIndex: 'createTime', + width: 180 + }, + { + title: '角色', + dataIndex: 'role', + width: 200 + }, + { + title: '备注', + dataIndex: 'remark' + } +] + +export const searchFormSchema: FormSchema[] = [ + { + field: 'account', + label: '用户名', + component: 'Input', + colProps: { span: 8 } + }, + { + field: 'nickname', + label: '昵称', + component: 'Input', + colProps: { span: 8 } + } +] + +export const accountFormSchema: FormSchema[] = [ + { + field: 'account', + label: '用户名', + component: 'Input', + helpMessage: ['本字段演示异步验证', '不能输入带有admin的用户名'], + rules: [ + { + required: true, + message: '请输入用户名' + }, + { + validator(_, value) { + return new Promise((resolve, reject) => { + isAccountExist(value) + .then(() => resolve()) + .catch((err) => { + reject(err.message || '验证失败') + }) + }) + } + } + ] + }, + { + field: 'pwd', + label: '密码', + component: 'InputPassword', + required: true, + ifShow: false + }, + { + label: '角色', + field: 'role', + component: 'ApiSelect', + componentProps: { + api: getAllRoleList, + labelField: 'roleName', + valueField: 'roleValue' + }, + required: true + }, + { + field: 'dept', + label: '所属部门', + component: 'TreeSelect', + componentProps: { + fieldNames: { + label: 'deptName', + key: 'id', + value: 'id' + }, + getPopupContainer: () => document.body + }, + required: true + }, + { + field: 'nickname', + label: '昵称', + component: 'Input', + required: true + }, + + { + label: '邮箱', + field: 'email', + component: 'Input', + required: true + }, + + { + label: '备注', + field: 'remark', + component: 'InputTextArea' + } +] diff --git a/src/views/demo/system/account/index.vue b/src/views/demo/system/account/index.vue new file mode 100644 index 00000000..0e1b4296 --- /dev/null +++ b/src/views/demo/system/account/index.vue @@ -0,0 +1,119 @@ + + diff --git a/src/views/demo/system/dept/DeptModal.vue b/src/views/demo/system/dept/DeptModal.vue new file mode 100644 index 00000000..8ecb394a --- /dev/null +++ b/src/views/demo/system/dept/DeptModal.vue @@ -0,0 +1,54 @@ + + diff --git a/src/views/demo/system/dept/dept.data.ts b/src/views/demo/system/dept/dept.data.ts new file mode 100644 index 00000000..53f4e9d6 --- /dev/null +++ b/src/views/demo/system/dept/dept.data.ts @@ -0,0 +1,108 @@ +import { BasicColumn } from '@/components/Table' +import { FormSchema } from '@/components/Table' +import { h } from 'vue' +import { Tag } from 'ant-design-vue' + +export const columns: BasicColumn[] = [ + { + title: '部门名称', + dataIndex: 'deptName', + width: 160, + align: 'left' + }, + { + title: '排序', + dataIndex: 'orderNo', + width: 50 + }, + { + title: '状态', + dataIndex: 'status', + width: 80, + customRender: ({ record }) => { + const status = record.status + const enable = ~~status === 0 + const color = enable ? 'green' : 'red' + const text = enable ? '启用' : '停用' + return h(Tag, { color: color }, () => text) + } + }, + { + title: '创建时间', + dataIndex: 'createTime', + width: 180 + }, + { + title: '备注', + dataIndex: 'remark' + } +] + +export const searchFormSchema: FormSchema[] = [ + { + field: 'deptName', + label: '部门名称', + component: 'Input', + colProps: { span: 8 } + }, + { + field: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: [ + { label: '启用', value: '0' }, + { label: '停用', value: '1' } + ] + }, + colProps: { span: 8 } + } +] + +export const formSchema: FormSchema[] = [ + { + field: 'deptName', + label: '部门名称', + component: 'Input', + required: true + }, + { + field: 'parentDept', + label: '上级部门', + component: 'TreeSelect', + + componentProps: { + fieldNames: { + label: 'deptName', + key: 'id', + value: 'id' + }, + getPopupContainer: () => document.body + }, + required: true + }, + { + field: 'orderNo', + label: '排序', + component: 'InputNumber', + required: true + }, + { + field: 'status', + label: '状态', + component: 'RadioButtonGroup', + defaultValue: '0', + componentProps: { + options: [ + { label: '启用', value: '0' }, + { label: '停用', value: '1' } + ] + }, + required: true + }, + { + label: '备注', + field: 'remark', + component: 'InputTextArea' + } +] diff --git a/src/views/demo/system/dept/index.vue b/src/views/demo/system/dept/index.vue new file mode 100644 index 00000000..f193ecf4 --- /dev/null +++ b/src/views/demo/system/dept/index.vue @@ -0,0 +1,86 @@ + + diff --git a/src/views/demo/system/menu/MenuDrawer.vue b/src/views/demo/system/menu/MenuDrawer.vue new file mode 100644 index 00000000..f53d6e02 --- /dev/null +++ b/src/views/demo/system/menu/MenuDrawer.vue @@ -0,0 +1,55 @@ + + diff --git a/src/views/demo/system/menu/index.vue b/src/views/demo/system/menu/index.vue new file mode 100644 index 00000000..49a09e56 --- /dev/null +++ b/src/views/demo/system/menu/index.vue @@ -0,0 +1,94 @@ + + diff --git a/src/views/demo/system/menu/menu.data.ts b/src/views/demo/system/menu/menu.data.ts new file mode 100644 index 00000000..562a5088 --- /dev/null +++ b/src/views/demo/system/menu/menu.data.ts @@ -0,0 +1,202 @@ +import { BasicColumn } from '@/components/Table' +import { FormSchema } from '@/components/Table' +import { h } from 'vue' +import { Tag } from 'ant-design-vue' +import { Icon } from '@/components/Icon' + +export const columns: BasicColumn[] = [ + { + title: '菜单名称', + dataIndex: 'menuName', + width: 200, + align: 'left' + }, + { + title: '图标', + dataIndex: 'icon', + width: 50, + customRender: ({ record }) => { + return h(Icon, { icon: record.icon }) + } + }, + { + title: '权限标识', + dataIndex: 'permission', + width: 180 + }, + { + title: '组件', + dataIndex: 'component' + }, + { + title: '排序', + dataIndex: 'orderNo', + width: 50 + }, + { + title: '状态', + dataIndex: 'status', + width: 80, + customRender: ({ record }) => { + const status = record.status + const enable = ~~status === 0 + const color = enable ? 'green' : 'red' + const text = enable ? '启用' : '停用' + return h(Tag, { color: color }, () => text) + } + }, + { + title: '创建时间', + dataIndex: 'createTime', + width: 180 + } +] + +const isDir = (type: string) => type === '0' +const isMenu = (type: string) => type === '1' +const isButton = (type: string) => type === '2' + +export const searchFormSchema: FormSchema[] = [ + { + field: 'menuName', + label: '菜单名称', + component: 'Input', + colProps: { span: 8 } + }, + { + field: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: [ + { label: '启用', value: '0' }, + { label: '停用', value: '1' } + ] + }, + colProps: { span: 8 } + } +] + +export const formSchema: FormSchema[] = [ + { + field: 'type', + label: '菜单类型', + component: 'RadioButtonGroup', + defaultValue: '0', + componentProps: { + options: [ + { label: '目录', value: '0' }, + { label: '菜单', value: '1' }, + { label: '按钮', value: '2' } + ] + }, + colProps: { lg: 24, md: 24 } + }, + { + field: 'menuName', + label: '菜单名称', + component: 'Input', + required: true + }, + + { + field: 'parentMenu', + label: '上级菜单', + component: 'TreeSelect', + componentProps: { + fieldNames: { + label: 'menuName', + key: 'id', + value: 'id' + }, + getPopupContainer: () => document.body + } + }, + + { + field: 'orderNo', + label: '排序', + component: 'InputNumber', + required: true + }, + { + field: 'icon', + label: '图标', + component: 'IconPicker', + required: true, + ifShow: ({ values }) => !isButton(values.type) + }, + + { + field: 'routePath', + label: '路由地址', + component: 'Input', + required: true, + ifShow: ({ values }) => !isButton(values.type) + }, + { + field: 'component', + label: '组件路径', + component: 'Input', + ifShow: ({ values }) => isMenu(values.type) + }, + { + field: 'permission', + label: '权限标识', + component: 'Input', + ifShow: ({ values }) => !isDir(values.type) + }, + { + field: 'status', + label: '状态', + component: 'RadioButtonGroup', + defaultValue: '0', + componentProps: { + options: [ + { label: '启用', value: '0' }, + { label: '禁用', value: '1' } + ] + } + }, + { + field: 'isExt', + label: '是否外链', + component: 'RadioButtonGroup', + defaultValue: '0', + componentProps: { + options: [ + { label: '否', value: '0' }, + { label: '是', value: '1' } + ] + }, + ifShow: ({ values }) => !isButton(values.type) + }, + + { + field: 'keepalive', + label: '是否缓存', + component: 'RadioButtonGroup', + defaultValue: '0', + componentProps: { + options: [ + { label: '否', value: '0' }, + { label: '是', value: '1' } + ] + }, + ifShow: ({ values }) => isMenu(values.type) + }, + + { + field: 'show', + label: '是否显示', + component: 'RadioButtonGroup', + defaultValue: '0', + componentProps: { + options: [ + { label: '是', value: '0' }, + { label: '否', value: '1' } + ] + }, + ifShow: ({ values }) => !isButton(values.type) + } +] diff --git a/src/views/demo/system/password/index.vue b/src/views/demo/system/password/index.vue new file mode 100644 index 00000000..6f97bbb0 --- /dev/null +++ b/src/views/demo/system/password/index.vue @@ -0,0 +1,36 @@ + + diff --git a/src/views/demo/system/password/pwd.data.ts b/src/views/demo/system/password/pwd.data.ts new file mode 100644 index 00000000..16f7e27b --- /dev/null +++ b/src/views/demo/system/password/pwd.data.ts @@ -0,0 +1,46 @@ +import { FormSchema } from '@/components/Form' + +export const formSchema: FormSchema[] = [ + { + field: 'passwordOld', + label: '当前密码', + component: 'InputPassword', + required: true + }, + { + field: 'passwordNew', + label: '新密码', + component: 'StrengthMeter', + componentProps: { + placeholder: '新密码' + }, + rules: [ + { + required: true, + message: '请输入新密码' + } + ] + }, + { + field: 'confirmPassword', + label: '确认密码', + component: 'InputPassword', + + dynamicRules: ({ values }) => { + return [ + { + required: true, + validator: (_, value) => { + if (!value) { + return Promise.reject('密码不能为空') + } + if (value !== values.passwordNew) { + return Promise.reject('两次输入的密码不一致!') + } + return Promise.resolve() + } + } + ] + } + } +] diff --git a/src/views/demo/system/role/RoleDrawer.vue b/src/views/demo/system/role/RoleDrawer.vue new file mode 100644 index 00000000..aade53e0 --- /dev/null +++ b/src/views/demo/system/role/RoleDrawer.vue @@ -0,0 +1,67 @@ + + diff --git a/src/views/demo/system/role/index.vue b/src/views/demo/system/role/index.vue new file mode 100644 index 00000000..9c5a4c43 --- /dev/null +++ b/src/views/demo/system/role/index.vue @@ -0,0 +1,83 @@ + + diff --git a/src/views/demo/system/role/role.data.ts b/src/views/demo/system/role/role.data.ts new file mode 100644 index 00000000..ee014d9b --- /dev/null +++ b/src/views/demo/system/role/role.data.ts @@ -0,0 +1,124 @@ +import { BasicColumn } from '@/components/Table' +import { FormSchema } from '@/components/Table' +import { h } from 'vue' +import { Switch } from 'ant-design-vue' +import { setRoleStatus } from '@/api/demo/system' +import { useMessage } from '@/hooks/web/useMessage' + +export const columns: BasicColumn[] = [ + { + title: '角色名称', + dataIndex: 'roleName', + width: 200 + }, + { + title: '角色值', + dataIndex: 'roleValue', + width: 180 + }, + { + title: '排序', + dataIndex: 'orderNo', + width: 50 + }, + { + title: '状态', + dataIndex: 'status', + width: 120, + customRender: ({ record }) => { + if (!Reflect.has(record, 'pendingStatus')) { + record.pendingStatus = false + } + return h(Switch, { + checked: record.status === '1', + checkedChildren: '已启用', + unCheckedChildren: '已禁用', + loading: record.pendingStatus, + onChange(checked: boolean) { + record.pendingStatus = true + const newStatus = checked ? '1' : '0' + const { createMessage } = useMessage() + setRoleStatus(record.id, newStatus) + .then(() => { + record.status = newStatus + createMessage.success(`已成功修改角色状态`) + }) + .catch(() => { + createMessage.error('修改角色状态失败') + }) + .finally(() => { + record.pendingStatus = false + }) + } + }) + } + }, + { + title: '创建时间', + dataIndex: 'createTime', + width: 180 + }, + { + title: '备注', + dataIndex: 'remark' + } +] + +export const searchFormSchema: FormSchema[] = [ + { + field: 'roleNme', + label: '角色名称', + component: 'Input', + colProps: { span: 8 } + }, + { + field: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: [ + { label: '启用', value: '0' }, + { label: '停用', value: '1' } + ] + }, + colProps: { span: 8 } + } +] + +export const formSchema: FormSchema[] = [ + { + field: 'roleName', + label: '角色名称', + required: true, + component: 'Input' + }, + { + field: 'roleValue', + label: '角色值', + required: true, + component: 'Input' + }, + { + field: 'status', + label: '状态', + component: 'RadioButtonGroup', + defaultValue: '0', + componentProps: { + options: [ + { label: '启用', value: '0' }, + { label: '停用', value: '1' } + ] + } + }, + { + label: '备注', + field: 'remark', + component: 'InputTextArea' + }, + { + label: ' ', + field: 'menu', + slot: 'menu', + component: 'Input' + } +] diff --git a/src/views/demo/table/AuthColumn.vue b/src/views/demo/table/AuthColumn.vue new file mode 100644 index 00000000..3cf72ebf --- /dev/null +++ b/src/views/demo/table/AuthColumn.vue @@ -0,0 +1,142 @@ + + diff --git a/src/views/demo/table/Basic.vue b/src/views/demo/table/Basic.vue new file mode 100644 index 00000000..eae6cae7 --- /dev/null +++ b/src/views/demo/table/Basic.vue @@ -0,0 +1,63 @@ + + diff --git a/src/views/demo/table/CustomerCell.vue b/src/views/demo/table/CustomerCell.vue new file mode 100644 index 00000000..ba3c62eb --- /dev/null +++ b/src/views/demo/table/CustomerCell.vue @@ -0,0 +1,98 @@ + + diff --git a/src/views/demo/table/EditCellTable.vue b/src/views/demo/table/EditCellTable.vue new file mode 100644 index 00000000..39a7851b --- /dev/null +++ b/src/views/demo/table/EditCellTable.vue @@ -0,0 +1,258 @@ + + diff --git a/src/views/demo/table/EditRowTable.vue b/src/views/demo/table/EditRowTable.vue new file mode 100644 index 00000000..b1b80266 --- /dev/null +++ b/src/views/demo/table/EditRowTable.vue @@ -0,0 +1,301 @@ + + diff --git a/src/views/demo/table/ExpandTable.vue b/src/views/demo/table/ExpandTable.vue new file mode 100644 index 00000000..cd888002 --- /dev/null +++ b/src/views/demo/table/ExpandTable.vue @@ -0,0 +1,65 @@ + + diff --git a/src/views/demo/table/FetchTable.vue b/src/views/demo/table/FetchTable.vue new file mode 100644 index 00000000..b35f3870 --- /dev/null +++ b/src/views/demo/table/FetchTable.vue @@ -0,0 +1,32 @@ + + diff --git a/src/views/demo/table/FixedColumn.vue b/src/views/demo/table/FixedColumn.vue new file mode 100644 index 00000000..cef5cb2a --- /dev/null +++ b/src/views/demo/table/FixedColumn.vue @@ -0,0 +1,84 @@ + + diff --git a/src/views/demo/table/FixedHeight.vue b/src/views/demo/table/FixedHeight.vue new file mode 100644 index 00000000..7b9bddaf --- /dev/null +++ b/src/views/demo/table/FixedHeight.vue @@ -0,0 +1,37 @@ + + diff --git a/src/views/demo/table/FooterTable.vue b/src/views/demo/table/FooterTable.vue new file mode 100644 index 00000000..59829f55 --- /dev/null +++ b/src/views/demo/table/FooterTable.vue @@ -0,0 +1,40 @@ + + diff --git a/src/views/demo/table/FormTable.vue b/src/views/demo/table/FormTable.vue new file mode 100644 index 00000000..53fc42f4 --- /dev/null +++ b/src/views/demo/table/FormTable.vue @@ -0,0 +1,71 @@ + + diff --git a/src/views/demo/table/MergeHeader.vue b/src/views/demo/table/MergeHeader.vue new file mode 100644 index 00000000..92177141 --- /dev/null +++ b/src/views/demo/table/MergeHeader.vue @@ -0,0 +1,18 @@ + + diff --git a/src/views/demo/table/MultipleHeader.vue b/src/views/demo/table/MultipleHeader.vue new file mode 100644 index 00000000..3ea3e907 --- /dev/null +++ b/src/views/demo/table/MultipleHeader.vue @@ -0,0 +1,16 @@ + + diff --git a/src/views/demo/table/RefTable.vue b/src/views/demo/table/RefTable.vue new file mode 100644 index 00000000..5ed84d9f --- /dev/null +++ b/src/views/demo/table/RefTable.vue @@ -0,0 +1,104 @@ + + diff --git a/src/views/demo/table/ResizeParentHeightTable.vue b/src/views/demo/table/ResizeParentHeightTable.vue new file mode 100644 index 00000000..e249edea --- /dev/null +++ b/src/views/demo/table/ResizeParentHeightTable.vue @@ -0,0 +1,67 @@ + + diff --git a/src/views/demo/table/TreeTable.vue b/src/views/demo/table/TreeTable.vue new file mode 100644 index 00000000..a7587b0f --- /dev/null +++ b/src/views/demo/table/TreeTable.vue @@ -0,0 +1,34 @@ + + diff --git a/src/views/demo/table/UseTable.vue b/src/views/demo/table/UseTable.vue new file mode 100644 index 00000000..539eada3 --- /dev/null +++ b/src/views/demo/table/UseTable.vue @@ -0,0 +1,124 @@ + + diff --git a/src/views/demo/table/tableData.tsx b/src/views/demo/table/tableData.tsx new file mode 100644 index 00000000..04d02db6 --- /dev/null +++ b/src/views/demo/table/tableData.tsx @@ -0,0 +1,292 @@ +import { FormProps, FormSchema } from '@/components/Table' +import { BasicColumn } from '@/components/Table/src/types/table' + +export function getBasicColumns(): BasicColumn[] { + return [ + { + title: 'ID', + dataIndex: 'id', + fixed: 'left', + width: 200 + }, + { + title: '姓名', + dataIndex: 'name', + width: 150, + filters: [ + { text: 'Male', value: 'male' }, + { text: 'Female', value: 'female' } + ] + }, + { + title: '地址', + dataIndex: 'address' + }, + { + title: '编号', + dataIndex: 'no', + width: 150, + sorter: true, + defaultHidden: true + }, + { + title: '开始时间', + width: 150, + sorter: true, + dataIndex: 'beginTime' + }, + { + title: '结束时间', + width: 150, + sorter: true, + dataIndex: 'endTime' + } + ] +} + +export function getBasicShortColumns(): BasicColumn[] { + return [ + { + title: 'ID', + width: 150, + dataIndex: 'id', + sorter: true, + sortOrder: 'ascend' + }, + { + title: '姓名', + dataIndex: 'name', + width: 120 + }, + { + title: '地址', + dataIndex: 'address' + }, + { + title: '编号', + dataIndex: 'no', + width: 80 + } + ] +} + +export function getMultipleHeaderColumns(): BasicColumn[] { + return [ + { + title: 'ID', + dataIndex: 'id', + width: 200 + }, + { + title: '姓名', + dataIndex: 'name', + width: 120 + }, + { + title: '地址', + dataIndex: 'address', + sorter: true, + children: [ + { + title: '编号', + dataIndex: 'no', + width: 120, + filters: [ + { text: 'Male', value: 'male', children: [] }, + { text: 'Female', value: 'female', children: [] } + ] + }, + + { + title: '开始时间', + dataIndex: 'beginTime', + width: 120 + }, + { + title: '结束时间', + dataIndex: 'endTime', + width: 120 + } + ] + } + ] +} + +export function getCustomHeaderColumns(): BasicColumn[] { + return [ + { + title: 'ID', + dataIndex: 'id', + helpMessage: 'headerHelpMessage方式1', + width: 200 + }, + { + // title: '姓名', + dataIndex: 'name', + width: 120 + // slots: { title: 'customTitle' }, + }, + { + // title: '地址', + dataIndex: 'address', + width: 120, + // slots: { title: 'customAddress' }, + sorter: true + }, + + { + title: '编号', + dataIndex: 'no', + width: 120, + filters: [ + { text: 'Male', value: 'male', children: [] }, + { text: 'Female', value: 'female', children: [] } + ] + }, + { + title: '开始时间', + dataIndex: 'beginTime', + width: 120 + }, + { + title: '结束时间', + dataIndex: 'endTime', + width: 120 + } + ] +} + +const cellContent = (_, index) => ({ + colSpan: index === 9 ? 0 : 1 +}) + +export function getMergeHeaderColumns(): BasicColumn[] { + return [ + { + title: 'ID', + dataIndex: 'id', + width: 300, + customCell: (_, index) => ({ + colSpan: index === 9 ? 6 : 1 + }) + }, + { + title: '姓名', + dataIndex: 'name', + width: 300, + customCell: cellContent + }, + { + title: '地址', + dataIndex: 'address', + colSpan: 2, + width: 120, + sorter: true, + customCell: (_, index) => ({ + rowSpan: index === 2 ? 2 : 1, + colSpan: index === 3 || index === 9 ? 0 : 1 + }) + }, + { + title: '编号', + dataIndex: 'no', + colSpan: 0, + filters: [ + { text: 'Male', value: 'male', children: [] }, + { text: 'Female', value: 'female', children: [] } + ], + customCell: cellContent + }, + { + title: '开始时间', + dataIndex: 'beginTime', + width: 200, + customCell: cellContent + }, + { + title: '结束时间', + dataIndex: 'endTime', + width: 200, + customCell: cellContent + } + ] +} +export const getAdvanceSchema = (itemNumber = 6): FormSchema[] => { + const arr: any = [] + for (let index = 0; index < itemNumber; index++) { + arr.push({ + field: `field${index}`, + label: `字段${index}`, + component: 'Input', + colProps: { + xl: 12, + xxl: 8 + } + }) + } + return arr +} +export function getFormConfig(): Partial { + return { + labelWidth: 100, + schemas: [ + ...getAdvanceSchema(5), + { + field: `field11`, + label: `Slot示例`, + component: 'Select', + slot: 'custom', + colProps: { + xl: 12, + xxl: 8 + } + } + ] + } +} +export function getBasicData() { + return (() => { + const arr: any = [] + for (let index = 0; index < 40; index++) { + arr.push({ + id: `${index}`, + name: 'John Brown', + age: `1${index}`, + no: `${index + 10}`, + address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park', + beginTime: new Date().toLocaleString(), + endTime: new Date().toLocaleString() + }) + } + return arr + })() +} + +export function getTreeTableData() { + return (() => { + const arr: any = [] + for (let index = 0; index < 40; index++) { + arr.push({ + id: `${index}`, + name: 'John Brown', + age: `1${index}`, + no: `${index + 10}`, + address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park', + beginTime: new Date().toLocaleString(), + endTime: new Date().toLocaleString(), + children: [ + { + id: `l2-${index}`, + name: 'John Brown', + age: `1${index}`, + no: `${index + 10}`, + address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park', + beginTime: new Date().toLocaleString(), + endTime: new Date().toLocaleString() + } + ] + }) + } + return arr + })() +} diff --git a/src/views/demo/tree/ActionTree.vue b/src/views/demo/tree/ActionTree.vue new file mode 100644 index 00000000..73bf3e2c --- /dev/null +++ b/src/views/demo/tree/ActionTree.vue @@ -0,0 +1,116 @@ + + diff --git a/src/views/demo/tree/EditTree.vue b/src/views/demo/tree/EditTree.vue new file mode 100644 index 00000000..34ba5c27 --- /dev/null +++ b/src/views/demo/tree/EditTree.vue @@ -0,0 +1,107 @@ + + diff --git a/src/views/demo/tree/data.ts b/src/views/demo/tree/data.ts new file mode 100644 index 00000000..03f0f403 --- /dev/null +++ b/src/views/demo/tree/data.ts @@ -0,0 +1,118 @@ +import { TreeItem } from '@/components/Tree' + +export const treeData: TreeItem[] = [ + { + title: 'parent ', + key: '0-0', + children: [ + { title: 'leaf', key: '0-0-0' }, + { + title: 'leaf', + key: '0-0-1', + children: [ + { title: 'leaf', key: '0-0-0-0', children: [{ title: 'leaf', key: '0-0-0-0-1' }] }, + { title: 'leaf', key: '0-0-0-1' } + ] + } + ] + }, + { + title: 'parent 2', + key: '1-1', + children: [ + { title: 'leaf', key: '1-1-0' }, + { title: 'leaf', key: '1-1-1' } + ] + }, + { + title: 'parent 3', + key: '2-2', + children: [ + { title: 'leaf', key: '2-2-0' }, + { title: 'leaf', key: '2-2-1' } + ] + } +] + +export const treeData2: any[] = [ + { + name: 'parent ', + id: '0-0', + + children: [ + { name: 'leaf', id: '0-0-0' }, + { + name: 'leaf', + id: '0-0-1', + + children: [ + { + name: 'leaf', + + id: '0-0-0-0', + children: [{ name: 'leaf', id: '0-0-0-0-1' }] + }, + { name: 'leaf', id: '0-0-0-1' } + ] + } + ] + }, + { + name: 'parent 2', + id: '1-1', + + children: [ + { name: 'leaf', id: '1-1-0' }, + { name: 'leaf', id: '1-1-1' } + ] + }, + { + name: 'parent 3', + id: '2-2', + + children: [ + { name: 'leaf', id: '2-2-0' }, + { name: 'leaf', id: '2-2-1' } + ] + } +] + +export const treeData3: any[] = [ + { + name: 'parent ', + key: '0-0', + children: [ + { name: 'leaf', key: '0-0-0' }, + { + name: 'leaf', + key: '0-0-1', + children: [ + { + name: 'leaf', + key: '0-0-0-0', + children: [{ name: 'leaf', key: '0-0-0-0-1' }] + }, + { name: 'leaf', key: '0-0-0-1' } + ] + } + ] + }, + { + name: 'parent 2', + key: '1-1', + + children: [ + { name: 'leaf', key: '1-1-0' }, + { name: 'leaf', key: '1-1-1' } + ] + }, + { + name: 'parent 3', + key: '2-2', + + children: [ + { name: 'leaf', key: '2-2-0' }, + { name: 'leaf', key: '2-2-1' } + ] + } +] diff --git a/src/views/demo/tree/index.vue b/src/views/demo/tree/index.vue new file mode 100644 index 00000000..c8eaf67a --- /dev/null +++ b/src/views/demo/tree/index.vue @@ -0,0 +1,111 @@ + + diff --git a/src/views/sys/about/index.vue b/src/views/sys/about/index.vue new file mode 100644 index 00000000..0ce3d309 --- /dev/null +++ b/src/views/sys/about/index.vue @@ -0,0 +1,98 @@ + + diff --git a/src/views/sys/error-log/DetailModal.vue b/src/views/sys/error-log/DetailModal.vue new file mode 100644 index 00000000..48159e01 --- /dev/null +++ b/src/views/sys/error-log/DetailModal.vue @@ -0,0 +1,26 @@ + + diff --git a/src/views/sys/error-log/data.tsx b/src/views/sys/error-log/data.tsx new file mode 100644 index 00000000..ab3a5cc5 --- /dev/null +++ b/src/views/sys/error-log/data.tsx @@ -0,0 +1,67 @@ +import { Tag } from 'ant-design-vue' +import { BasicColumn } from '@/components/Table' +import { ErrorTypeEnum } from '@/enums/exceptionEnum' +import { useI18n } from '@/hooks/web/useI18n' + +const { t } = useI18n() + +export function getColumns(): BasicColumn[] { + return [ + { + dataIndex: 'type', + title: t('sys.errorLog.tableColumnType'), + width: 80, + customRender: ({ text }) => { + const color = + text === ErrorTypeEnum.VUE + ? 'green' + : text === ErrorTypeEnum.RESOURCE + ? 'cyan' + : text === ErrorTypeEnum.PROMISE + ? 'blue' + : ErrorTypeEnum.AJAX + ? 'red' + : 'purple' + return {() => text} + } + }, + { + dataIndex: 'url', + title: 'URL', + width: 200 + }, + { + dataIndex: 'time', + title: t('sys.errorLog.tableColumnDate'), + width: 160 + }, + { + dataIndex: 'file', + title: t('sys.errorLog.tableColumnFile'), + width: 200 + }, + { + dataIndex: 'name', + title: 'Name', + width: 200 + }, + { + dataIndex: 'message', + title: t('sys.errorLog.tableColumnMsg'), + width: 300 + }, + { + dataIndex: 'stack', + title: t('sys.errorLog.tableColumnStackMsg') + } + ] +} + +export function getDescSchema(): any { + return getColumns().map((column) => { + return { + field: column.dataIndex!, + label: column.title + } + }) +} diff --git a/src/views/sys/error-log/index.vue b/src/views/sys/error-log/index.vue new file mode 100644 index 00000000..ae145174 --- /dev/null +++ b/src/views/sys/error-log/index.vue @@ -0,0 +1,97 @@ + + + diff --git a/src/views/sys/exception/Exception.vue b/src/views/sys/exception/Exception.vue new file mode 100644 index 00000000..3fcf01a7 --- /dev/null +++ b/src/views/sys/exception/Exception.vue @@ -0,0 +1,142 @@ + + diff --git a/src/views/sys/exception/index.ts b/src/views/sys/exception/index.ts new file mode 100644 index 00000000..86b6f815 --- /dev/null +++ b/src/views/sys/exception/index.ts @@ -0,0 +1 @@ +export { default as Exception } from './Exception.vue' diff --git a/src/views/sys/iframe/FrameBlank.vue b/src/views/sys/iframe/FrameBlank.vue new file mode 100644 index 00000000..0e6f54b7 --- /dev/null +++ b/src/views/sys/iframe/FrameBlank.vue @@ -0,0 +1,6 @@ + + diff --git a/src/views/sys/iframe/index.vue b/src/views/sys/iframe/index.vue new file mode 100644 index 00000000..0b61e1e8 --- /dev/null +++ b/src/views/sys/iframe/index.vue @@ -0,0 +1,85 @@ + + + diff --git a/src/views/sys/lock/LockPage.vue b/src/views/sys/lock/LockPage.vue new file mode 100644 index 00000000..9774414d --- /dev/null +++ b/src/views/sys/lock/LockPage.vue @@ -0,0 +1,216 @@ + + + diff --git a/src/views/sys/lock/index.vue b/src/views/sys/lock/index.vue new file mode 100644 index 00000000..05526a48 --- /dev/null +++ b/src/views/sys/lock/index.vue @@ -0,0 +1,13 @@ + + diff --git a/src/views/sys/lock/useNow.ts b/src/views/sys/lock/useNow.ts new file mode 100644 index 00000000..10795965 --- /dev/null +++ b/src/views/sys/lock/useNow.ts @@ -0,0 +1,60 @@ +import { dateUtil } from '@/utils/dateUtil' +import { reactive, toRefs } from 'vue' +import { tryOnMounted, tryOnUnmounted } from '@vueuse/core' + +export function useNow(immediate = true) { + let timer: IntervalHandle + + const state = reactive({ + year: 0, + month: 0, + week: '', + day: 0, + hour: '', + minute: '', + second: 0, + meridiem: '' + }) + + const update = () => { + const now = dateUtil() + + const h = now.format('HH') + const m = now.format('mm') + const s = now.get('s') + + state.year = now.get('y') + state.month = now.get('M') + 1 + state.week = '星期' + ['日', '一', '二', '三', '四', '五', '六'][now.day()] + state.day = now.get('date') + state.hour = h + state.minute = m + state.second = s + + state.meridiem = now.format('A') + } + + function start() { + update() + clearInterval(timer) + timer = setInterval(() => update(), 1000) + } + + function stop() { + clearInterval(timer) + } + + tryOnMounted(() => { + immediate && start() + }) + + tryOnUnmounted(() => { + stop() + }) + + return { + ...toRefs(state), + start, + stop + } +} diff --git a/src/views/sys/login/ForgetPasswordForm.vue b/src/views/sys/login/ForgetPasswordForm.vue new file mode 100644 index 00000000..e651d73f --- /dev/null +++ b/src/views/sys/login/ForgetPasswordForm.vue @@ -0,0 +1,56 @@ + + diff --git a/src/views/sys/login/Login.vue b/src/views/sys/login/Login.vue new file mode 100644 index 00000000..3fc2feab --- /dev/null +++ b/src/views/sys/login/Login.vue @@ -0,0 +1,210 @@ + + + diff --git a/src/views/sys/login/LoginForm.vue b/src/views/sys/login/LoginForm.vue new file mode 100644 index 00000000..ad9ebae2 --- /dev/null +++ b/src/views/sys/login/LoginForm.vue @@ -0,0 +1,136 @@ + + diff --git a/src/views/sys/login/LoginFormTitle.vue b/src/views/sys/login/LoginFormTitle.vue new file mode 100644 index 00000000..1156fc5c --- /dev/null +++ b/src/views/sys/login/LoginFormTitle.vue @@ -0,0 +1,25 @@ + + diff --git a/src/views/sys/login/MobileForm.vue b/src/views/sys/login/MobileForm.vue new file mode 100644 index 00000000..94b55a5c --- /dev/null +++ b/src/views/sys/login/MobileForm.vue @@ -0,0 +1,53 @@ + + diff --git a/src/views/sys/login/QrCodeForm.vue b/src/views/sys/login/QrCodeForm.vue new file mode 100644 index 00000000..b247c74b --- /dev/null +++ b/src/views/sys/login/QrCodeForm.vue @@ -0,0 +1,27 @@ + + diff --git a/src/views/sys/login/RegisterForm.vue b/src/views/sys/login/RegisterForm.vue new file mode 100644 index 00000000..de5d9ce3 --- /dev/null +++ b/src/views/sys/login/RegisterForm.vue @@ -0,0 +1,78 @@ + + diff --git a/src/views/sys/login/SessionTimeoutLogin.vue b/src/views/sys/login/SessionTimeoutLogin.vue new file mode 100644 index 00000000..f7d7a3c0 --- /dev/null +++ b/src/views/sys/login/SessionTimeoutLogin.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/views/sys/login/useLogin.ts b/src/views/sys/login/useLogin.ts new file mode 100644 index 00000000..780f4843 --- /dev/null +++ b/src/views/sys/login/useLogin.ts @@ -0,0 +1,124 @@ +import type { Rule, FormInstance } from 'ant-design-vue/lib/form' +import type { RuleObject, NamePath } from 'ant-design-vue/lib/form/interface' +import { ref, computed, unref, Ref } from 'vue' +import { useI18n } from '@/hooks/web/useI18n' + +export enum LoginStateEnum { + LOGIN, + REGISTER, + RESET_PASSWORD, + MOBILE, + QR_CODE +} + +const currentState = ref(LoginStateEnum.LOGIN) + +// 这里也可以优化 +// import { createGlobalState } from '@vueuse/core' + +export function useLoginState() { + function setLoginState(state: LoginStateEnum) { + currentState.value = state + } + + const getLoginState = computed(() => currentState.value) + + function handleBackLogin() { + setLoginState(LoginStateEnum.LOGIN) + } + + return { setLoginState, getLoginState, handleBackLogin } +} + +export function useFormValid(formRef: Ref) { + const validate = computed(() => { + const form = unref(formRef) + return form?.validate ?? ((_nameList?: NamePath) => Promise.resolve()) + }) + + async function validForm() { + const form = unref(formRef) + if (!form) return + const data = await form.validate() + return data as T + } + + return { validate, validForm } +} + +export function useFormRules(formData?: Recordable) { + const { t } = useI18n() + + const getAccountFormRule = computed(() => createRule(t('sys.login.accountPlaceholder'))) + const getPasswordFormRule = computed(() => createRule(t('sys.login.passwordPlaceholder'))) + const getSmsFormRule = computed(() => createRule(t('sys.login.smsPlaceholder'))) + const getMobileFormRule = computed(() => createRule(t('sys.login.mobilePlaceholder'))) + + const validatePolicy = async (_: RuleObject, value: boolean) => { + return !value ? Promise.reject(t('sys.login.policyPlaceholder')) : Promise.resolve() + } + + const validateConfirmPassword = (password: string) => { + return async (_: RuleObject, value: string) => { + if (!value) { + return Promise.reject(t('sys.login.passwordPlaceholder')) + } + if (value !== password) { + return Promise.reject(t('sys.login.diffPwd')) + } + return Promise.resolve() + } + } + + const getFormRules = computed((): { [k: string]: Rule | Rule[] } => { + const accountFormRule = unref(getAccountFormRule) + const passwordFormRule = unref(getPasswordFormRule) + const smsFormRule = unref(getSmsFormRule) + const mobileFormRule = unref(getMobileFormRule) + + const mobileRule = { + sms: smsFormRule, + mobile: mobileFormRule + } + switch (unref(currentState)) { + // register form rules + case LoginStateEnum.REGISTER: + return { + account: accountFormRule, + password: passwordFormRule, + confirmPassword: [{ validator: validateConfirmPassword(formData?.password), trigger: 'change' }], + policy: [{ validator: validatePolicy, trigger: 'change' }], + ...mobileRule + } + + // reset password form rules + case LoginStateEnum.RESET_PASSWORD: + return { + account: accountFormRule, + ...mobileRule + } + + // mobile form rules + case LoginStateEnum.MOBILE: + return mobileRule + + // login form rules + default: + return { + account: accountFormRule, + password: passwordFormRule + } + } + }) + return { getFormRules } +} + +function createRule(message: string) { + return [ + { + required: true, + message, + trigger: 'change' + } + ] as RuleObject[] +} diff --git a/src/views/sys/redirect/index.vue b/src/views/sys/redirect/index.vue new file mode 100644 index 00000000..d031daf6 --- /dev/null +++ b/src/views/sys/redirect/index.vue @@ -0,0 +1,30 @@ + + diff --git a/stylelint.config.js b/stylelint.config.js new file mode 100644 index 00000000..f3224cca --- /dev/null +++ b/stylelint.config.js @@ -0,0 +1,89 @@ +module.exports = { + root: true, + plugins: ['stylelint-order'], + extends: ['stylelint-config-standard', 'stylelint-config-prettier'], + customSyntax: 'postcss-html', + rules: { + 'function-no-unknown': null, + 'selector-class-pattern': null, + 'selector-pseudo-class-no-unknown': [ + true, + { + ignorePseudoClasses: ['global'] + } + ], + 'selector-pseudo-element-no-unknown': [ + true, + { + ignorePseudoElements: ['v-deep'] + } + ], + 'at-rule-no-unknown': [ + true, + { + ignoreAtRules: ['tailwind', 'apply', 'variants', 'responsive', 'screen', 'function', 'if', 'each', 'include', 'mixin'] + } + ], + 'no-empty-source': null, + 'string-quotes': null, + 'named-grid-areas-no-invalid': null, + 'unicode-bom': 'never', + 'no-descending-specificity': null, + 'font-family-no-missing-generic-family-keyword': null, + 'declaration-colon-space-after': 'always-single-line', + 'declaration-colon-space-before': 'never', + // 'declaration-block-trailing-semicolon': 'always', + 'rule-empty-line-before': [ + 'always', + { + ignore: ['after-comment', 'first-nested'] + } + ], + 'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }], + 'order/order': [ + [ + 'dollar-variables', + 'custom-properties', + 'at-rules', + 'declarations', + { + type: 'at-rule', + name: 'supports' + }, + { + type: 'at-rule', + name: 'media' + }, + 'rules' + ], + { severity: 'warning' } + ] + }, + ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'], + overrides: [ + { + files: ['*.vue', '**/*.vue', '*.html', '**/*.html'], + extends: ['stylelint-config-recommended'], + rules: { + 'keyframes-name-pattern': null, + 'selector-pseudo-class-no-unknown': [ + true, + { + ignorePseudoClasses: ['deep', 'global'] + } + ], + 'selector-pseudo-element-no-unknown': [ + true, + { + ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted'] + } + ] + } + }, + { + files: ['*.less', '**/*.less'], + customSyntax: 'postcss-less', + extends: ['stylelint-config-standard', 'stylelint-config-recommended-vue'] + } + ] +} diff --git a/tests/server/README.md b/tests/server/README.md new file mode 100644 index 00000000..14298dfc --- /dev/null +++ b/tests/server/README.md @@ -0,0 +1,15 @@ +# Test Server + +It is used to start the test interface service, which can test the upload, websocket, login and other interfaces. + +## Usage + +```bash + +cd ./test/server + +pnpm install + +pnpm run start + +``` diff --git a/tests/server/controller/FileController.ts b/tests/server/controller/FileController.ts new file mode 100644 index 00000000..35e19acb --- /dev/null +++ b/tests/server/controller/FileController.ts @@ -0,0 +1,18 @@ +import FileService from '../service/FileService' + +class FileController { + private service: FileService = new FileService() + + upload = async (ctx) => { + const files = ctx.request.files.file + console.log(files) + + if (files.length === undefined) { + this.service.upload(ctx, files, false) + } else { + this.service.upload(ctx, files, true) + } + } +} + +export default new FileController() diff --git a/tests/server/controller/UserController.ts b/tests/server/controller/UserController.ts new file mode 100644 index 00000000..b42af32a --- /dev/null +++ b/tests/server/controller/UserController.ts @@ -0,0 +1,15 @@ +import UserService from '../service/UserService' + +class UserController { + private service: UserService = new UserService() + + login = async (ctx) => { + ctx.body = await this.service.login() + } + + getUserInfoById = async (ctx) => { + ctx.body = await this.service.getUserInfoById() + } +} + +export default new UserController() diff --git a/tests/server/ecosystem.config.js b/tests/server/ecosystem.config.js new file mode 100644 index 00000000..98cc1384 --- /dev/null +++ b/tests/server/ecosystem.config.js @@ -0,0 +1,18 @@ +const { name } = require('./package.json') +const path = require('path') + +module.exports = { + apps: [ + { + name, + script: path.resolve(__dirname, './dist/index.js'), + instances: require('os').cpus().length, + autorestart: true, + watch: true, + env_production: { + NODE_ENV: 'production', + PORT: 8080 + } + } + ] +} diff --git a/tests/server/index.ts b/tests/server/index.ts new file mode 100644 index 00000000..321ccbe2 --- /dev/null +++ b/tests/server/index.ts @@ -0,0 +1,63 @@ +import Koa from 'koa' +import path from 'path' +import Router from 'koa-router' +import body from 'koa-body' +import cors from 'koa2-cors' +import koaStatic from 'koa-static' +import websockify from 'koa-websocket' +import route from 'koa-route' + +import AppRoutes from './routes' + +const PORT = 3300 + +const app = websockify(new Koa()) + +app.ws.use(function (ctx, next) { + ctx.websocket.send('connection succeeded!') + return next(ctx) +}) + +app.ws.use( + route.all('/test', function (ctx) { + // ctx.websocket.send('Hello World'); + ctx.websocket.on('message', function (message) { + // do something with the message from client + + if (message !== 'ping') { + const data = JSON.stringify({ + id: Math.ceil(Math.random() * 1000), + time: new Date().getTime(), + res: `${message}` + }) + ctx.websocket.send(data) + } + console.log(message) + }) + }) +) + +const router = new Router() + +// router +AppRoutes.forEach((route) => router[route.method](route.path, route.action)) + +app.use(cors()) +app.use( + body({ + encoding: 'gzip', + multipart: true, + formidable: { + // uploadDir: path.join(__dirname, '/upload/'), // 设置文件上传目录 + keepExtensions: true, + maxFieldsSize: 20 * 1024 * 1024 + } + }) +) +app.use(router.routes()) +app.use(router.allowedMethods()) +app.use(koaStatic(path.join(__dirname))) + +app.listen(PORT, () => { + console.log(`Application started successfully: http://localhost:${PORT}`) +}) diff --git a/tests/server/nodemon.json b/tests/server/nodemon.json new file mode 100644 index 00000000..59fa5afb --- /dev/null +++ b/tests/server/nodemon.json @@ -0,0 +1,8 @@ +{ + "watch": ["src"], + "ext": "ts", + "exec": "ts-node -r tsconfig-paths/register index.ts", + "events": { + "restart": "clear" + } +} diff --git a/tests/server/package.json b/tests/server/package.json new file mode 100644 index 00000000..cce2442d --- /dev/null +++ b/tests/server/package.json @@ -0,0 +1,36 @@ +{ + "name": "server", + "version": "1.0.0", + "license": "MIT", + "scripts": { + "start": "nodemon", + "build": "rimraf ./dist && tsup ./index.ts --dts --format cjs,esm ", + "prod": "npx pm2 start ecosystem.config.js --env production", + "restart": "pm2 restart ecosystem.config.js --env production", + "stop": "npx pm2 stop ecosystem.config.js" + }, + "dependencies": { + "fs-extra": "^10.0.1", + "koa": "^2.13.4", + "koa-body": "^4.2.0", + "koa-bodyparser": "^4.3.0", + "koa-route": "^3.2.0", + "koa-router": "^10.1.1", + "koa-static": "^5.0.0", + "koa-websocket": "^6.0.0", + "koa2-cors": "^2.0.6" + }, + "devDependencies": { + "@types/koa": "^2.13.4", + "@types/koa-bodyparser": "^5.0.2", + "@types/koa-router": "^7.4.4", + "@types/node": "^17.0.21", + "nodemon": "^2.0.15", + "pm2": "^5.2.0", + "rimraf": "^3.0.2", + "ts-node": "^10.7.0", + "tsconfig-paths": "^3.14.0", + "tsup": "^5.12.1", + "typescript": "^4.6.2" + } +} diff --git a/tests/server/routes.ts b/tests/server/routes.ts new file mode 100644 index 00000000..1aac884c --- /dev/null +++ b/tests/server/routes.ts @@ -0,0 +1,23 @@ +import UserController from './controller/UserController' +import FileController from './controller/FileController' + +export default [ + // user + { + path: '/login', + method: 'post', + action: UserController.login + }, + { + path: '/getUserInfoById', + method: 'get', + action: UserController.getUserInfoById + }, + + // file + { + path: '/upload', + method: 'post', + action: FileController.upload + } +] diff --git a/tests/server/service/FileService.ts b/tests/server/service/FileService.ts new file mode 100644 index 00000000..3f9f4562 --- /dev/null +++ b/tests/server/service/FileService.ts @@ -0,0 +1,54 @@ +import path from 'path' +import fs from 'fs-extra' + +const uploadUrl = 'http://localhost:3300/static/upload' +const filePath = path.join(__dirname, '../static/upload/') + +fs.ensureDir(filePath) +export default class UserService { + async upload(ctx, files, isMultiple) { + let fileReader, fileResource, writeStream + + const fileFunc = function (file) { + fileReader = fs.createReadStream(file.path) + fileResource = filePath + `/${file.name}` + console.log(fileResource) + + writeStream = fs.createWriteStream(fileResource) + fileReader.pipe(writeStream) + } + + const returnFunc = function (flag) { + if (flag) { + let url = '' + for (let i = 0; i < files.length; i++) { + url += uploadUrl + `/${files[i].name},` + } + url = url.replace(/,$/gi, '') + ctx.body = { + url: url, + code: 0, + message: 'upload Success!' + } + } else { + ctx.body = { + url: uploadUrl + `/${files.name}`, + code: 0, + message: 'upload Success!' + } + } + } + console.log(isMultiple, files.length) + + if (isMultiple) { + for (let i = 0; i < files.length; i++) { + const f1 = files[i] + fileFunc(f1) + } + } else { + fileFunc(files) + } + fs.ensureDir(filePath) + returnFunc(isMultiple) + } +} diff --git a/tests/server/service/UserService.ts b/tests/server/service/UserService.ts new file mode 100644 index 00000000..7001875d --- /dev/null +++ b/tests/server/service/UserService.ts @@ -0,0 +1,25 @@ +import { Result } from '../utils' + +const fakeUserInfo = { + userId: '1', + username: 'vben', + realName: 'Vben Admin', + desc: 'manager', + password: '123456', + token: 'fakeToken1', + roles: [ + { + roleName: 'Super Admin', + value: 'super' + } + ] +} +export default class UserService { + async login() { + return Result.success(fakeUserInfo) + } + + async getUserInfoById() { + return Result.success(fakeUserInfo) + } +} diff --git a/tests/server/tsconfig.json b/tests/server/tsconfig.json new file mode 100644 index 00000000..76203ed7 --- /dev/null +++ b/tests/server/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": false, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es6", + "sourceMap": false, + "esModuleInterop": true, + "outDir": "./dist", + "baseUrl": "./" + }, + "exclude": ["node_modules"] +} diff --git a/tests/server/utils.ts b/tests/server/utils.ts new file mode 100644 index 00000000..f5911a67 --- /dev/null +++ b/tests/server/utils.ts @@ -0,0 +1,9 @@ +export class Result { + static success(data: any) { + return { + code: 0, + success: true, + result: data + } + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..3181a8da --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,43 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "noLib": false, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": true, + "strictFunctionTypes": false, + "jsx": "preserve", + "baseUrl": ".", + "allowJs": true, + "sourceMap": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "experimentalDecorators": true, + "lib": ["dom", "esnext"], + "noImplicitAny": false, + "skipLibCheck": true, + "types": ["vite/client"], + "removeComments": true, + "paths": { + "@/*": ["src/*"] + } + }, + "include": [ + "tests/**/*.ts", + "src/**/*.ts", + "src/**/*.d.ts", + "src/**/*.tsx", + "src/**/*.vue", + "src/types/**/*.d.tsts", + "src/types/**/*.ts", + "build/**/*.ts", + "build/**/*.d.ts", + "mock/**/*.ts", + "vite.config.ts" + ], + "exclude": ["node_modules", "tests/server/**/*.ts", "dist", "**/*.js"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 00000000..e4e7e019 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,99 @@ +import type { UserConfig, ConfigEnv } from 'vite' +import pkg from './package.json' +import dayjs from 'dayjs' +import { loadEnv } from 'vite' +import { resolve } from 'path' +import { generateModifyVars } from './build/generate/generateModifyVars' +import { createProxy } from './build/vite/proxy' +import { wrapperEnv } from './build/utils' +import { createVitePlugins } from './build/vite/plugin' +import { OUTPUT_DIR } from './build/constant' +import { include, exclude } from './build/vite/optimize' + +function pathResolve(dir: string) { + return resolve(process.cwd(), '.', dir) +} + +const { dependencies, devDependencies, name, version } = pkg +const __APP_INFO__ = { + pkg: { dependencies, devDependencies, name, version }, + lastBuildTime: dayjs().format('YYYY-MM-DD HH:mm:ss') +} + +export default ({ command, mode }: ConfigEnv): UserConfig => { + const root = process.cwd() + + const env = loadEnv(mode, root) + + // The boolean type read by loadEnv is a string. This function can be converted to boolean type + const viteEnv = wrapperEnv(env) + + const { VITE_PORT, VITE_PUBLIC_PATH, VITE_PROXY, VITE_DROP_CONSOLE } = viteEnv + + const isBuild = command === 'build' + + return { + base: VITE_PUBLIC_PATH, + root, + server: { + https: true, + // Listening on all local IPs + host: true, + port: VITE_PORT, + // Load proxy configuration from .env + proxy: createProxy(VITE_PROXY) + }, + resolve: { + alias: [ + { + find: 'vue-i18n', + replacement: 'vue-i18n/dist/vue-i18n.cjs.js' + }, + // @/xxxx => src/xxxx + { + find: /\@\//, + replacement: pathResolve('src') + '/' + } + ] + }, + esbuild: { + drop: VITE_DROP_CONSOLE ? ['console', 'debugger'] : [] + }, + build: { + target: 'es2015', + cssTarget: 'chrome80', + outDir: OUTPUT_DIR, + // minify: 'terser', + /** + * 当 minify=“minify:'terser'” 解开注释 + * Uncomment when minify="minify:'terser'" + */ + // terserOptions: { + // compress: { + // keep_infinity: true, + // drop_console: VITE_DROP_CONSOLE, + // }, + // }, + // Turning off brotliSize display can slightly reduce packaging time + reportCompressedSize: false, + chunkSizeWarningLimit: 2000 + }, + define: { + __APP_INFO__: JSON.stringify(__APP_INFO__) + }, + + css: { + preprocessorOptions: { + less: { + modifyVars: generateModifyVars(), + javascriptEnabled: true + } + } + }, + + // The vite plugin used by the project. The quantity is large, so it is separately extracted and managed + plugins: createVitePlugins(mode, viteEnv, isBuild), + + optimizeDeps: { include, exclude } + } +} diff --git a/windi.config.ts b/windi.config.ts new file mode 100644 index 00000000..51190ba8 --- /dev/null +++ b/windi.config.ts @@ -0,0 +1,74 @@ +import { defineConfig } from 'vite-plugin-windicss' +import { primaryColor } from './build/config/themeConfig' + +export default defineConfig({ + darkMode: 'class', + plugins: [createEnterPlugin()], + theme: { + extend: { + zIndex: { + '-1': '-1' + }, + colors: { + primary: primaryColor + }, + screens: { + sm: '576px', + md: '768px', + lg: '992px', + xl: '1200px', + '2xl': '1600px' + } + } + } +}) + +/** + * Used for animation when the element is displayed. + * @param maxOutput The larger the maxOutput output, the larger the generated css volume. + */ +function createEnterPlugin(maxOutput = 6) { + const createCss = (index: number, d = 'x') => { + const upd = d.toUpperCase() + return { + [`*> .enter-${d}:nth-child(${index})`]: { + transform: `translate${upd}(50px)` + }, + [`*> .-enter-${d}:nth-child(${index})`]: { + transform: `translate${upd}(-50px)` + }, + [`* > .enter-${d}:nth-child(${index}),* > .-enter-${d}:nth-child(${index})`]: { + 'z-index': `${10 - index}`, + opacity: '0', + animation: `enter-${d}-animation 0.4s ease-in-out 0.3s`, + 'animation-fill-mode': 'forwards', + 'animation-delay': `${(index * 1) / 10}s` + } + } + } + const handler = ({ addBase }) => { + const addRawCss = {} + for (let index = 1; index < maxOutput; index++) { + Object.assign(addRawCss, { + ...createCss(index, 'x'), + ...createCss(index, 'y') + }) + } + addBase({ + ...addRawCss, + [`@keyframes enter-x-animation`]: { + to: { + opacity: '1', + transform: 'translateX(0)' + } + }, + [`@keyframes enter-y-animation`]: { + to: { + opacity: '1', + transform: 'translateY(0)' + } + } + }) + } + return { handler } +}