From 56f3017baaed19460df55c961659235919657f72 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 11 Feb 2023 00:44:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=EF=BC=8C=E8=87=AA=20v1.7.1=20=E7=89=88=E6=9C=AC=E5=BC=80?= =?UTF-8?q?=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 12 + .env | 15 + .env.base | 19 + .env.dev | 31 + .env.pro | 31 + .env.test | 31 + .eslintignore | 8 + .eslintrc-auto-import.json | 259 +++ .eslintrc.js | 69 + .gitignore | 8 + .prettierignore | 9 + .stylelintignore | 6 + .vscode/extensions.json | 15 + .vscode/settings.json | 44 + LICENSE | 21 + README.en.md | 36 - README.md | 244 +- build/vite/index.ts | 98 + build/vite/optimize.ts | 68 + index.html | 151 ++ package.json | 137 ++ postcss.config.js | 5 + prettier.config.js | 22 + public/favicon.ico | Bin 0 -> 4286 bytes public/logo.gif | Bin 0 -> 6334 bytes src/App.vue | 49 + src/api/bpm/activity/index.ts | 8 + src/api/bpm/definition/index.ts | 21 + src/api/bpm/form/index.ts | 56 + src/api/bpm/leave/index.ts | 27 + src/api/bpm/model/index.ts | 59 + src/api/bpm/processInstance/index.ts | 40 + src/api/bpm/task/index.ts | 34 + src/api/bpm/taskAssignRule/index.ts | 29 + src/api/bpm/userGroup/index.ts | 47 + src/api/infra/apiAccessLog/index.ts | 50 + src/api/infra/apiErrorLog/index.ts | 66 + src/api/infra/codegen/index.ts | 57 + src/api/infra/codegen/types.ts | 61 + src/api/infra/config/index.ts | 62 + src/api/infra/dataSourceConfig/index.ts | 35 + src/api/infra/dbDoc/index.ts | 16 + src/api/infra/fileConfig/index.ts | 66 + src/api/infra/fileList/index.ts | 28 + src/api/infra/job/index.ts | 75 + src/api/infra/jobLog/index.ts | 49 + src/api/infra/redis/index.ts | 41 + src/api/infra/redis/types.ts | 185 ++ src/api/login/index.ts | 73 + src/api/login/types.ts | 42 + src/api/mp/account/index.ts | 41 + src/api/mp/autoReply/index.ts | 39 + src/api/mp/draft/index.ts | 35 + src/api/mp/freePublish/index.ts | 23 + src/api/mp/material/index.ts | 16 + src/api/mp/menu/index.ts | 26 + src/api/mp/message/index.ts | 17 + src/api/mp/mpuser/index.ts | 31 + src/api/mp/statistics/index.ts | 33 + src/api/mp/tag/index.ts | 53 + src/api/pay/app/index.ts | 78 + src/api/pay/channel/index.ts | 70 + src/api/pay/merchant/index.ts | 77 + src/api/pay/order/index.ts | 109 + src/api/pay/refund/index.ts | 116 + src/api/system/dept/index.ts | 48 + src/api/system/dict/dict.data.ts | 36 + src/api/system/dict/dict.type.ts | 36 + src/api/system/dict/types.ts | 46 + src/api/system/errorCode/index.ts | 48 + src/api/system/loginLog/index.ts | 30 + src/api/system/mail/account/index.ts | 46 + src/api/system/mail/log/index.ts | 40 + src/api/system/mail/template/index.ts | 58 + src/api/system/menu/index.ts | 54 + src/api/system/notice/index.ts | 42 + src/api/system/notify/message/index.ts | 66 + src/api/system/notify/template/index.ts | 55 + src/api/system/oauth2/client.ts | 51 + src/api/system/oauth2/token.ts | 28 + src/api/system/operatelog/index.ts | 41 + src/api/system/permission/index.ts | 42 + src/api/system/post/index.ts | 58 + src/api/system/role/index.ts | 58 + src/api/system/sensitiveWord/index.ts | 64 + src/api/system/sms/smsChannel/index.ts | 50 + src/api/system/sms/smsLog/index.ts | 57 + src/api/system/sms/smsTemplate/index.ts | 80 + src/api/system/tenant/index.ts | 62 + src/api/system/tenantPackage/index.ts | 49 + src/api/system/user/index.ts | 91 + src/api/system/user/profile.ts | 77 + src/api/system/user/socialUser.ts | 31 + src/assets/imgs/avatar.gif | Bin 0 -> 6334 bytes src/assets/imgs/avatar.jpg | Bin 0 -> 6264 bytes src/assets/imgs/logo.png | Bin 0 -> 2801 bytes src/assets/svgs/403.svg | 1 + src/assets/svgs/404.svg | 1 + src/assets/svgs/500.svg | 1 + src/assets/svgs/icon.svg | 1 + src/assets/svgs/login-bg.svg | 1 + src/assets/svgs/login-box-bg.svg | 1 + src/assets/svgs/message.svg | 1 + src/assets/svgs/money.svg | 1 + src/assets/svgs/peoples.svg | 1 + src/assets/svgs/shopping.svg | 1 + src/components/Backtop/index.ts | 3 + src/components/Backtop/src/Backtop.vue | 15 + src/components/ConfigGlobal/index.ts | 3 + .../ConfigGlobal/src/ConfigGlobal.vue | 61 + src/components/ContentDetailWrap/index.ts | 3 + .../src/ContentDetailWrap.vue | 54 + src/components/ContentWrap/index.ts | 3 + .../ContentWrap/src/ContentWrap.vue | 32 + src/components/CountTo/index.ts | 3 + src/components/CountTo/src/CountTo.vue | 180 ++ src/components/Crontab/index.ts | 2 + src/components/Crontab/src/Crontab.vue | 998 +++++++++ src/components/Cropper/index.ts | 4 + src/components/Cropper/src/CopperModal.vue | 254 +++ src/components/Cropper/src/Cropper.vue | 181 ++ src/components/Cropper/src/CropperAvatar.vue | 135 ++ src/components/Cropper/src/types.ts | 8 + src/components/Descriptions/index.ts | 3 + .../Descriptions/src/Descriptions.vue | 155 ++ src/components/Dialog/index.ts | 3 + src/components/Dialog/src/Dialog.vue | 118 + src/components/DictTag/index.ts | 3 + src/components/DictTag/src/DictTag.vue | 56 + src/components/Echart/index.ts | 3 + src/components/Echart/src/Echart.vue | 113 + src/components/Editor/index.ts | 8 + src/components/Editor/src/Editor.vue | 200 ++ src/components/Error/index.ts | 3 + src/components/Error/src/Error.vue | 56 + src/components/Form/index.ts | 15 + src/components/Form/src/Form.vue | 302 +++ src/components/Form/src/componentMap.ts | 55 + .../Form/src/components/useRenderCheckbox.tsx | 26 + .../Form/src/components/useRenderRadio.tsx | 26 + .../Form/src/components/useRenderSelect.tsx | 57 + src/components/Form/src/helper.ts | 148 ++ src/components/Form/src/types.ts | 17 + src/components/Highlight/index.ts | 3 + src/components/Highlight/src/Highlight.vue | 65 + src/components/IFrame/index.ts | 3 + src/components/IFrame/src/IFrame.vue | 30 + src/components/Icon/index.ts | 4 + src/components/Icon/src/Icon.vue | 83 + src/components/Icon/src/IconSelect.vue | 222 ++ src/components/Icon/src/data.ts | 1961 +++++++++++++++++ src/components/ImageViewer/index.ts | 33 + .../ImageViewer/src/ImageViewer.vue | 33 + src/components/ImageViewer/src/types.ts | 9 + src/components/Infotip/index.ts | 3 + src/components/Infotip/src/Infotip.vue | 52 + src/components/InputPassword/index.ts | 3 + .../InputPassword/src/InputPassword.vue | 148 ++ src/components/Qrcode/index.ts | 3 + src/components/Qrcode/src/Qrcode.vue | 252 +++ src/components/Search/index.ts | 3 + src/components/Search/src/Search.vue | 144 ++ src/components/Sticky/index.ts | 3 + src/components/Sticky/src/Sticky.vue | 140 ++ src/components/Table/index.ts | 12 + src/components/Table/src/Table.vue | 307 +++ src/components/Table/src/helper.ts | 8 + src/components/Table/src/types.ts | 26 + src/components/Tooltip/index.ts | 3 + src/components/Tooltip/src/Tooltip.vue | 14 + src/components/UploadFile/index.ts | 5 + src/components/UploadFile/src/UploadFile.vue | 164 ++ src/components/UploadFile/src/UploadImg.vue | 251 +++ src/components/UploadFile/src/UploadImgs.vue | 277 +++ src/components/Verifition/index.ts | 3 + src/components/Verifition/src/Verify.vue | 443 ++++ .../Verifition/src/Verify/VerifyPoints.vue | 250 +++ .../Verifition/src/Verify/VerifySlide.vue | 376 ++++ src/components/Verifition/src/Verify/index.ts | 4 + src/components/Verifition/src/utils/ase.ts | 14 + src/components/Verifition/src/utils/util.ts | 97 + src/components/XButton/index.ts | 4 + src/components/XButton/src/XButton.vue | 47 + src/components/XButton/src/XTextButton.vue | 46 + src/components/XModal/index.ts | 3 + src/components/XModal/src/XModal.vue | 42 + src/components/XTable/index.ts | 3 + src/components/XTable/src/XTable.vue | 419 ++++ src/components/XTable/src/style/dark.scss | 81 + src/components/XTable/src/style/index.scss | 6 + src/components/XTable/src/style/light.scss | 16 + src/components/XTable/src/type.ts | 26 + .../package/designer/ProcessDesigner.vue | 698 ++++++ .../package/designer/ProcessViewer.vue | 595 +++++ .../package/designer/index.ts | 8 + .../package/designer/index2.ts | 8 + .../plugins/content-pad/contentPadProvider.js | 423 ++++ .../designer/plugins/content-pad/index.js | 6 + .../package/designer/plugins/defaultEmpty.js | 24 + .../descriptor/activitiDescriptor.json | 994 +++++++++ .../plugins/descriptor/camundaDescriptor.json | 1010 +++++++++ .../descriptor/flowableDescriptor.json | 1207 ++++++++++ .../activiti/activitiExtension.js | 83 + .../extension-moddle/activiti/index.js | 11 + .../extension-moddle/camunda/extension.js | 151 ++ .../plugins/extension-moddle/camunda/index.js | 8 + .../flowable/flowableExtension.js | 83 + .../extension-moddle/flowable/index.js | 10 + .../designer/plugins/palette/CustomPalette.js | 221 ++ .../package/designer/plugins/palette/index.js | 22 + .../plugins/palette/paletteProvider.js | 213 ++ .../plugins/translate/customTranslate.js | 44 + .../package/designer/plugins/translate/zh.js | 240 ++ .../bpmnProcessDesigner/package/index.ts | 37 + .../package/palette/ProcessPalette.vue | 41 + .../package/penal/PropertiesPanel.vue | 205 ++ .../package/penal/base/ElementBaseInfo.vue | 180 ++ .../penal/flow-condition/FlowCondition.vue | 185 ++ .../package/penal/form/ElementForm.vue | 444 ++++ .../package/penal/index.js | 7 + .../penal/listeners/ElementListeners.vue | 399 ++++ .../penal/listeners/UserTaskListeners.vue | 447 ++++ .../package/penal/listeners/template.js | 178 ++ .../package/penal/listeners/utilSelf.ts | 62 + .../multi-instance/ElementMultiInstance.vue | 253 +++ .../penal/other/ElementOtherConfig.vue | 53 + .../penal/properties/ElementProperties.vue | 166 ++ .../penal/signal-message/SignalAndMessage.vue | 110 + .../package/penal/task/ElementTask.vue | 83 + .../task/task-components/ReceiveTask.vue | 123 ++ .../penal/task/task-components/ScriptTask.vue | 96 + .../penal/task/task-components/UserTask.vue | 95 + .../package/theme/element-variables.scss | 70 + .../package/theme/index.scss | 2 + .../package/theme/process-designer.scss | 161 ++ .../package/theme/process-panel.scss | 107 + .../bpmnProcessDesigner/package/utils.ts | 76 + .../src/highlight/index.js | 5 + .../modules/custom-renderer/CustomRenderer.js | 14 + .../src/modules/custom-renderer/index.js | 6 + .../src/modules/rules/CustomRules.js | 16 + .../src/modules/rules/index.js | 6 + .../bpmnProcessDesigner/src/translations.ts | 25 + .../src/utils/directive/clickOutSide.js | 39 + .../bpmnProcessDesigner/src/utils/index.js | 10 + .../bpmnProcessDesigner/src/utils/xml2json.js | 50 + src/components/index.ts | 25 + src/config/axios/config.ts | 28 + src/config/axios/errorCode.ts | 6 + src/config/axios/index.ts | 50 + src/config/axios/service.ts | 241 ++ src/directives/index.ts | 13 + src/directives/permission/hasPermi.ts | 27 + src/directives/permission/hasRole.ts | 27 + src/hooks/event/useScrollTo.ts | 60 + src/hooks/web/useCache.ts | 27 + src/hooks/web/useConfigGlobal.ts | 9 + src/hooks/web/useCrudSchemas.ts | 293 +++ src/hooks/web/useDesign.ts | 18 + src/hooks/web/useEmitt.ts | 22 + src/hooks/web/useForm.ts | 94 + src/hooks/web/useI18n.ts | 52 + src/hooks/web/useIcon.ts | 8 + src/hooks/web/useIntro.ts | 47 + src/hooks/web/useLocale.ts | 35 + src/hooks/web/useMessage.ts | 95 + src/hooks/web/useNProgress.ts | 33 + src/hooks/web/usePageLoading.ts | 18 + src/hooks/web/useTable.ts | 223 ++ src/hooks/web/useTimeAgo.ts | 47 + src/hooks/web/useTitle.ts | 24 + src/hooks/web/useValidator.ts | 62 + src/hooks/web/useVxeCrudSchemas.ts | 354 +++ src/hooks/web/useVxeGrid.ts | 264 +++ src/hooks/web/useWatermark.ts | 55 + src/hooks/web/useXTable.ts | 39 + src/layout/Layout.vue | 78 + src/layout/components/AppView.vue | 51 + src/layout/components/Breadcrumb/index.ts | 3 + .../components/Breadcrumb/src/Breadcrumb.vue | 128 ++ .../components/Breadcrumb/src/helper.ts | 31 + src/layout/components/Collapse/index.ts | 3 + .../components/Collapse/src/Collapse.vue | 34 + src/layout/components/ContextMenu/index.ts | 10 + .../ContextMenu/src/ContextMenu.vue | 73 + src/layout/components/Footer/index.ts | 3 + src/layout/components/Footer/src/Footer.vue | 21 + src/layout/components/LocaleDropdown/index.ts | 3 + .../LocaleDropdown/src/LocaleDropdown.vue | 50 + src/layout/components/Logo/index.ts | 3 + src/layout/components/Logo/src/Logo.vue | 87 + src/layout/components/Menu/index.ts | 3 + src/layout/components/Menu/src/Menu.vue | 298 +++ .../Menu/src/components/useRenderMenuItem.tsx | 59 + .../src/components/useRenderMenuTitle.tsx | 22 + src/layout/components/Menu/src/helper.ts | 54 + src/layout/components/Message/index.ts | 3 + src/layout/components/Message/src/Message.vue | 113 + src/layout/components/Screenfull/index.ts | 3 + .../components/Screenfull/src/Screenfull.vue | 30 + src/layout/components/Setting/index.ts | 3 + src/layout/components/Setting/src/Setting.vue | 298 +++ .../src/components/ColorRadioPicker.vue | 65 + .../src/components/InterfaceDisplay.vue | 222 ++ .../src/components/LayoutRadioPicker.vue | 170 ++ src/layout/components/SizeDropdown/index.ts | 3 + .../SizeDropdown/src/SizeDropdown.vue | 38 + src/layout/components/TabMenu/index.ts | 3 + src/layout/components/TabMenu/src/TabMenu.vue | 251 +++ src/layout/components/TabMenu/src/helper.ts | 51 + src/layout/components/TagsView/index.ts | 3 + .../components/TagsView/src/TagsView.vue | 581 +++++ src/layout/components/TagsView/src/helper.ts | 21 + src/layout/components/ThemeSwitch/index.ts | 3 + .../ThemeSwitch/src/ThemeSwitch.vue | 39 + src/layout/components/ToolHeader.vue | 90 + src/layout/components/UserInfo/index.ts | 3 + .../components/UserInfo/src/UserInfo.vue | 76 + src/layout/components/useRenderLayout.tsx | 276 +++ src/locales/en.ts | 445 ++++ src/locales/zh-CN.ts | 438 ++++ src/main.ts | 99 + src/permission.ts | 70 + src/plugins/animate.css/index.ts | 1 + src/plugins/echarts/index.ts | 41 + src/plugins/elementPlus/index.ts | 17 + src/plugins/formCreate/index.ts | 43 + src/plugins/svgIcon/index.ts | 3 + src/plugins/vueI18n/helper.ts | 3 + src/plugins/vueI18n/index.ts | 42 + src/plugins/vxeTable/index.ts | 223 ++ src/plugins/vxeTable/renderer/dataPicker.tsx | 20 + .../vxeTable/renderer/dataTimeRangePicker.tsx | 23 + src/plugins/vxeTable/renderer/dict.tsx | 12 + src/plugins/vxeTable/renderer/html.tsx | 10 + src/plugins/vxeTable/renderer/img.tsx | 20 + src/plugins/vxeTable/renderer/index.tsx | 7 + src/plugins/vxeTable/renderer/link.tsx | 15 + src/plugins/vxeTable/renderer/preview.tsx | 34 + src/plugins/windi.css/index.ts | 3 + src/router/index.ts | 28 + src/router/modules/remaining.ts | 300 +++ src/store/index.ts | 10 + src/store/modules/app.ts | 274 +++ src/store/modules/dict.ts | 103 + src/store/modules/locale.ts | 59 + src/store/modules/permission.ts | 70 + src/store/modules/tagsView.ts | 140 ++ src/store/modules/user.ts | 83 + src/styles/global.module.scss | 6 + src/styles/index.scss | 23 + src/styles/theme.scss | 6 + src/styles/var.css | 73 + src/styles/variables.scss | 6 + src/styles/vxe.css | 30 + src/types/auto-components.d.ts | 119 + src/types/auto-imports.d.ts | 67 + src/types/components.d.ts | 56 + src/types/configGlobal.d.ts | 4 + src/types/contextMenu.d.ts | 7 + src/types/descriptions.d.ts | 13 + src/types/elementPlus.d.ts | 3 + src/types/form.d.ts | 44 + src/types/icon.d.ts | 5 + src/types/infoTip.d.ts | 4 + src/types/layout.d.ts | 1 + src/types/localeDropdown.d.ts | 10 + src/types/qrcode.d.ts | 9 + src/types/table.d.ts | 42 + src/types/theme.d.ts | 16 + src/utils/Logger.ts | 100 + src/utils/auth.ts | 92 + src/utils/color.ts | 153 ++ src/utils/constants.ts | 218 ++ src/utils/dict.ts | 127 ++ src/utils/domUtils.ts | 289 +++ src/utils/download.ts | 38 + src/utils/env.ts | 8 + src/utils/filt.ts | 157 ++ src/utils/formCreate.ts | 54 + src/utils/formRules.ts | 7 + src/utils/formatTime.ts | 176 ++ src/utils/index.ts | 139 ++ src/utils/is.ts | 105 + src/utils/jsencrypt.ts | 31 + src/utils/propTypes.ts | 28 + src/utils/routerHelper.ts | 224 ++ src/utils/tree.ts | 301 +++ src/utils/tsxHelper.ts | 16 + src/views/Error/403.vue | 6 + src/views/Error/404.vue | 6 + src/views/Error/500.vue | 6 + src/views/Home/Index.vue | 379 ++++ src/views/Home/Index2.vue | 312 +++ src/views/Home/echarts-data.ts | 308 +++ src/views/Home/types.ts | 55 + src/views/Login/Login.vue | 95 + src/views/Login/components/LoginForm.vue | 307 +++ src/views/Login/components/LoginFormTitle.vue | 23 + src/views/Login/components/MobileForm.vue | 213 ++ src/views/Login/components/QrCodeForm.vue | 28 + src/views/Login/components/RegisterForm.vue | 140 ++ src/views/Login/components/index.ts | 7 + src/views/Login/components/useLogin.ts | 41 + src/views/Profile/Index.vue | 60 + src/views/Profile/components/BasicInfo.vue | 89 + src/views/Profile/components/ProfileUser.vue | 88 + src/views/Profile/components/ResetPwd.vue | 68 + src/views/Profile/components/UserAvatar.vue | 38 + src/views/Profile/components/UserSocial.vue | 67 + src/views/Profile/components/index.ts | 7 + src/views/Redirect/Redirect.vue | 26 + src/views/bpm/definition/definition.data.ts | 73 + src/views/bpm/definition/index.vue | 104 + src/views/bpm/form/form.data.ts | 43 + src/views/bpm/form/formEditor.vue | 115 + src/views/bpm/form/index.vue | 93 + src/views/bpm/group/group.data.ts | 63 + src/views/bpm/group/index.vue | 182 ++ src/views/bpm/model/index.vue | 586 +++++ src/views/bpm/model/model.data.ts | 101 + src/views/bpm/model/modelEditor.vue | 204 ++ src/views/bpm/oa/leave/create.vue | 56 + src/views/bpm/oa/leave/detail.vue | 36 + src/views/bpm/oa/leave/index.vue | 83 + src/views/bpm/oa/leave/leave.data.ts | 90 + src/views/bpm/processInstance/create.vue | 149 ++ src/views/bpm/processInstance/detail.vue | 490 ++++ src/views/bpm/processInstance/index.vue | 88 + .../bpm/processInstance/process.create.ts | 34 + src/views/bpm/processInstance/process.data.ts | 89 + src/views/bpm/task/done/done.data.ts | 52 + src/views/bpm/task/done/index.vue | 36 + src/views/bpm/task/todo/index.vue | 37 + src/views/bpm/task/todo/todo.data.ts | 57 + src/views/bpm/taskAssignRule/index.vue | 350 +++ .../bpm/taskAssignRule/taskAssignRule.data.ts | 46 + .../infra/apiAccessLog/apiAccessLog.data.ts | 73 + src/views/infra/apiAccessLog/index.vue | 62 + .../infra/apiErrorLog/apiErrorLog.data.ts | 76 + src/views/infra/apiErrorLog/index.vue | 99 + src/views/infra/build/index.vue | 115 + src/views/infra/codegen/EditTable.vue | 67 + src/views/infra/codegen/codegen.data.ts | 53 + .../codegen/components/BasicInfoForm.vue | 183 ++ .../codegen/components/CloumInfoForm.vue | 137 ++ .../infra/codegen/components/ImportTable.vue | 125 ++ .../infra/codegen/components/Preview.vue | 145 ++ src/views/infra/codegen/components/index.ts | 5 + src/views/infra/codegen/index.vue | 107 + src/views/infra/config/config.data.ts | 90 + src/views/infra/config/index.vue | 162 ++ .../dataSourceConfig/dataSourceConfig.data.ts | 52 + src/views/infra/dataSourceConfig/index.vue | 145 ++ src/views/infra/dbDoc/index.vue | 61 + src/views/infra/druid/index.vue | 9 + src/views/infra/fileConfig/fileConfig.data.ts | 77 + src/views/infra/fileConfig/index.vue | 294 +++ src/views/infra/fileList/fileList.data.ts | 52 + src/views/infra/fileList/index.vue | 173 ++ src/views/infra/job/JobLog.vue | 78 + src/views/infra/job/index.vue | 302 +++ src/views/infra/job/job.data.ts | 69 + src/views/infra/job/jobLog.data.ts | 75 + src/views/infra/redis/index.vue | 277 +++ src/views/infra/server/index.vue | 9 + src/views/infra/skywalking/index.vue | 8 + src/views/infra/swagger/index.vue | 10 + src/views/infra/testDemo/index.vue | 4 + src/views/infra/webSocket/index.vue | 116 + src/views/mp/account/index.vue | 3 + src/views/mp/autoReply/index.vue | 3 + src/views/mp/draft/index.vue | 3 + src/views/mp/freePublish/index.vue | 3 + src/views/mp/material/index.vue | 3 + src/views/mp/menu/index.vue | 3 + src/views/mp/message/index.vue | 3 + src/views/mp/mpuser/index.vue | 3 + src/views/mp/statistics/index.vue | 3 + src/views/pay/app/app.data.ts | 71 + src/views/pay/app/index.vue | 155 ++ src/views/pay/merchant/index.vue | 153 ++ src/views/pay/merchant/merchant.data.ts | 70 + src/views/pay/order/index.vue | 79 + src/views/pay/order/order.data.ts | 152 ++ src/views/pay/refund/index.vue | 59 + src/views/pay/refund/refund.data.ts | 173 ++ src/views/report/goview/index.vue | 8 + src/views/report/jmreport/index.vue | 11 + src/views/system/dept/dept.data.ts | 76 + src/views/system/dept/index.vue | 189 ++ src/views/system/dict/dict.data.ts | 104 + src/views/system/dict/dict.type.ts | 65 + src/views/system/dict/index.vue | 251 +++ src/views/system/errorCode/errorCode.data.ts | 54 + src/views/system/errorCode/index.vue | 145 ++ src/views/system/loginlog/index.vue | 53 + src/views/system/loginlog/loginLog.data.ts | 53 + src/views/system/mail/account/account.data.ts | 65 + src/views/system/mail/account/index.vue | 151 ++ src/views/system/mail/log/index.vue | 97 + src/views/system/mail/log/log.data.ts | 121 + src/views/system/mail/template/index.vue | 273 +++ .../system/mail/template/template.data.ts | 98 + src/views/system/menu/index.vue | 351 +++ src/views/system/menu/menu.data.ts | 76 + src/views/system/notice/index.vue | 148 ++ src/views/system/notice/notice.data.ts | 59 + src/views/system/notify/message/index.vue | 66 + .../system/notify/message/message.data.ts | 101 + src/views/system/notify/my/index.vue | 58 + src/views/system/notify/my/my.data.ts | 58 + src/views/system/notify/template/index.vue | 251 +++ .../system/notify/template/template.data.ts | 85 + src/views/system/oauth2/client/client.data.ts | 193 ++ src/views/system/oauth2/client/index.vue | 207 ++ src/views/system/oauth2/token/index.vue | 64 + src/views/system/oauth2/token/token.data.ts | 48 + src/views/system/operatelog/index.vue | 67 + .../system/operatelog/operatelog.data.ts | 106 + src/views/system/post/PostForm.vue | 92 + src/views/system/post/index.vue | 67 + src/views/system/post/post.data.ts | 52 + src/views/system/role/index.vue | 314 +++ src/views/system/role/role.data.ts | 75 + src/views/system/sensitiveWord/index.vue | 191 ++ .../sensitiveWord/sensitiveWord.data.ts | 74 + src/views/system/sms/smsChannel/index.vue | 147 ++ .../system/sms/smsChannel/sms.channel.data.ts | 63 + src/views/system/sms/smsLog/index.vue | 56 + src/views/system/sms/smsLog/sms.log.data.ts | 82 + src/views/system/sms/smsTemplate/index.vue | 232 ++ .../sms/smsTemplate/sms.template.data.ts | 81 + src/views/system/tenant/index.vue | 174 ++ src/views/system/tenant/tenant.data.ts | 135 ++ src/views/system/tenantPackage/index.vue | 168 ++ .../tenantPackage/tenantPackage.data.ts | 68 + src/views/system/user/index.vue | 547 +++++ src/views/system/user/user.data.ts | 107 + stylelint.config.js | 231 ++ tsconfig.json | 44 + types/components.d.ts | 7 + types/custom-types.d.ts | 27 + types/env.d.ts | 32 + types/global.d.ts | 50 + types/router.d.ts | 81 + vite.config.ts | 81 + windi.config.ts | 61 + 548 files changed, 52096 insertions(+), 61 deletions(-) create mode 100644 .editorconfig create mode 100644 .env create mode 100644 .env.base create mode 100644 .env.dev create mode 100644 .env.pro create mode 100644 .env.test create mode 100644 .eslintignore create mode 100644 .eslintrc-auto-import.json create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 .prettierignore create mode 100644 .stylelintignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 LICENSE delete mode 100644 README.en.md create mode 100644 build/vite/index.ts create mode 100644 build/vite/optimize.ts create mode 100644 index.html create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 prettier.config.js create mode 100644 public/favicon.ico create mode 100644 public/logo.gif create mode 100644 src/App.vue create mode 100644 src/api/bpm/activity/index.ts create mode 100644 src/api/bpm/definition/index.ts create mode 100644 src/api/bpm/form/index.ts create mode 100644 src/api/bpm/leave/index.ts create mode 100644 src/api/bpm/model/index.ts create mode 100644 src/api/bpm/processInstance/index.ts create mode 100644 src/api/bpm/task/index.ts create mode 100644 src/api/bpm/taskAssignRule/index.ts create mode 100644 src/api/bpm/userGroup/index.ts create mode 100644 src/api/infra/apiAccessLog/index.ts create mode 100644 src/api/infra/apiErrorLog/index.ts create mode 100644 src/api/infra/codegen/index.ts create mode 100644 src/api/infra/codegen/types.ts create mode 100644 src/api/infra/config/index.ts create mode 100644 src/api/infra/dataSourceConfig/index.ts create mode 100644 src/api/infra/dbDoc/index.ts create mode 100644 src/api/infra/fileConfig/index.ts create mode 100644 src/api/infra/fileList/index.ts create mode 100644 src/api/infra/job/index.ts create mode 100644 src/api/infra/jobLog/index.ts create mode 100644 src/api/infra/redis/index.ts create mode 100644 src/api/infra/redis/types.ts create mode 100644 src/api/login/index.ts create mode 100644 src/api/login/types.ts create mode 100644 src/api/mp/account/index.ts create mode 100644 src/api/mp/autoReply/index.ts create mode 100644 src/api/mp/draft/index.ts create mode 100644 src/api/mp/freePublish/index.ts create mode 100644 src/api/mp/material/index.ts create mode 100644 src/api/mp/menu/index.ts create mode 100644 src/api/mp/message/index.ts create mode 100644 src/api/mp/mpuser/index.ts create mode 100644 src/api/mp/statistics/index.ts create mode 100644 src/api/mp/tag/index.ts create mode 100644 src/api/pay/app/index.ts create mode 100644 src/api/pay/channel/index.ts create mode 100644 src/api/pay/merchant/index.ts create mode 100644 src/api/pay/order/index.ts create mode 100644 src/api/pay/refund/index.ts create mode 100644 src/api/system/dept/index.ts create mode 100644 src/api/system/dict/dict.data.ts create mode 100644 src/api/system/dict/dict.type.ts create mode 100644 src/api/system/dict/types.ts create mode 100644 src/api/system/errorCode/index.ts create mode 100644 src/api/system/loginLog/index.ts create mode 100644 src/api/system/mail/account/index.ts create mode 100644 src/api/system/mail/log/index.ts create mode 100644 src/api/system/mail/template/index.ts create mode 100644 src/api/system/menu/index.ts create mode 100644 src/api/system/notice/index.ts create mode 100644 src/api/system/notify/message/index.ts create mode 100644 src/api/system/notify/template/index.ts create mode 100644 src/api/system/oauth2/client.ts create mode 100644 src/api/system/oauth2/token.ts create mode 100644 src/api/system/operatelog/index.ts create mode 100644 src/api/system/permission/index.ts create mode 100644 src/api/system/post/index.ts create mode 100644 src/api/system/role/index.ts create mode 100644 src/api/system/sensitiveWord/index.ts create mode 100644 src/api/system/sms/smsChannel/index.ts create mode 100644 src/api/system/sms/smsLog/index.ts create mode 100644 src/api/system/sms/smsTemplate/index.ts create mode 100644 src/api/system/tenant/index.ts create mode 100644 src/api/system/tenantPackage/index.ts create mode 100644 src/api/system/user/index.ts create mode 100644 src/api/system/user/profile.ts create mode 100644 src/api/system/user/socialUser.ts create mode 100644 src/assets/imgs/avatar.gif create mode 100644 src/assets/imgs/avatar.jpg create mode 100644 src/assets/imgs/logo.png create mode 100644 src/assets/svgs/403.svg create mode 100644 src/assets/svgs/404.svg create mode 100644 src/assets/svgs/500.svg create mode 100644 src/assets/svgs/icon.svg create mode 100644 src/assets/svgs/login-bg.svg create mode 100644 src/assets/svgs/login-box-bg.svg create mode 100644 src/assets/svgs/message.svg create mode 100644 src/assets/svgs/money.svg create mode 100644 src/assets/svgs/peoples.svg create mode 100644 src/assets/svgs/shopping.svg create mode 100644 src/components/Backtop/index.ts create mode 100644 src/components/Backtop/src/Backtop.vue create mode 100644 src/components/ConfigGlobal/index.ts create mode 100644 src/components/ConfigGlobal/src/ConfigGlobal.vue create mode 100644 src/components/ContentDetailWrap/index.ts create mode 100644 src/components/ContentDetailWrap/src/ContentDetailWrap.vue create mode 100644 src/components/ContentWrap/index.ts create mode 100644 src/components/ContentWrap/src/ContentWrap.vue create mode 100644 src/components/CountTo/index.ts create mode 100644 src/components/CountTo/src/CountTo.vue create mode 100644 src/components/Crontab/index.ts create mode 100644 src/components/Crontab/src/Crontab.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/types.ts create mode 100644 src/components/Descriptions/index.ts create mode 100644 src/components/Descriptions/src/Descriptions.vue create mode 100644 src/components/Dialog/index.ts create mode 100644 src/components/Dialog/src/Dialog.vue create mode 100644 src/components/DictTag/index.ts create mode 100644 src/components/DictTag/src/DictTag.vue create mode 100644 src/components/Echart/index.ts create mode 100644 src/components/Echart/src/Echart.vue create mode 100644 src/components/Editor/index.ts create mode 100644 src/components/Editor/src/Editor.vue create mode 100644 src/components/Error/index.ts create mode 100644 src/components/Error/src/Error.vue create mode 100644 src/components/Form/index.ts create mode 100644 src/components/Form/src/Form.vue create mode 100644 src/components/Form/src/componentMap.ts create mode 100644 src/components/Form/src/components/useRenderCheckbox.tsx create mode 100644 src/components/Form/src/components/useRenderRadio.tsx create mode 100644 src/components/Form/src/components/useRenderSelect.tsx create mode 100644 src/components/Form/src/helper.ts create mode 100644 src/components/Form/src/types.ts create mode 100644 src/components/Highlight/index.ts create mode 100644 src/components/Highlight/src/Highlight.vue create mode 100644 src/components/IFrame/index.ts create mode 100644 src/components/IFrame/src/IFrame.vue create mode 100644 src/components/Icon/index.ts create mode 100644 src/components/Icon/src/Icon.vue create mode 100644 src/components/Icon/src/IconSelect.vue create mode 100644 src/components/Icon/src/data.ts create mode 100644 src/components/ImageViewer/index.ts create mode 100644 src/components/ImageViewer/src/ImageViewer.vue create mode 100644 src/components/ImageViewer/src/types.ts create mode 100644 src/components/Infotip/index.ts create mode 100644 src/components/Infotip/src/Infotip.vue create mode 100644 src/components/InputPassword/index.ts create mode 100644 src/components/InputPassword/src/InputPassword.vue create mode 100644 src/components/Qrcode/index.ts create mode 100644 src/components/Qrcode/src/Qrcode.vue create mode 100644 src/components/Search/index.ts create mode 100644 src/components/Search/src/Search.vue create mode 100644 src/components/Sticky/index.ts create mode 100644 src/components/Sticky/src/Sticky.vue create mode 100644 src/components/Table/index.ts create mode 100644 src/components/Table/src/Table.vue create mode 100644 src/components/Table/src/helper.ts create mode 100644 src/components/Table/src/types.ts create mode 100644 src/components/Tooltip/index.ts create mode 100644 src/components/Tooltip/src/Tooltip.vue create mode 100644 src/components/UploadFile/index.ts create mode 100644 src/components/UploadFile/src/UploadFile.vue create mode 100644 src/components/UploadFile/src/UploadImg.vue create mode 100644 src/components/UploadFile/src/UploadImgs.vue create mode 100644 src/components/Verifition/index.ts create mode 100644 src/components/Verifition/src/Verify.vue create mode 100644 src/components/Verifition/src/Verify/VerifyPoints.vue create mode 100644 src/components/Verifition/src/Verify/VerifySlide.vue create mode 100644 src/components/Verifition/src/Verify/index.ts create mode 100644 src/components/Verifition/src/utils/ase.ts create mode 100644 src/components/Verifition/src/utils/util.ts create mode 100644 src/components/XButton/index.ts create mode 100644 src/components/XButton/src/XButton.vue create mode 100644 src/components/XButton/src/XTextButton.vue create mode 100644 src/components/XModal/index.ts create mode 100644 src/components/XModal/src/XModal.vue create mode 100644 src/components/XTable/index.ts create mode 100644 src/components/XTable/src/XTable.vue create mode 100644 src/components/XTable/src/style/dark.scss create mode 100644 src/components/XTable/src/style/index.scss create mode 100644 src/components/XTable/src/style/light.scss create mode 100644 src/components/XTable/src/type.ts create mode 100644 src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue create mode 100644 src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue create mode 100644 src/components/bpmnProcessDesigner/package/designer/index.ts create mode 100644 src/components/bpmnProcessDesigner/package/designer/index2.ts create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/contentPadProvider.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/index.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/defaultEmpty.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/activitiDescriptor.json create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/camundaDescriptor.json create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/activitiExtension.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/index.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/extension.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/index.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/flowableExtension.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/index.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/palette/index.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/translate/customTranslate.js create mode 100644 src/components/bpmnProcessDesigner/package/designer/plugins/translate/zh.js create mode 100644 src/components/bpmnProcessDesigner/package/index.ts create mode 100644 src/components/bpmnProcessDesigner/package/palette/ProcessPalette.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/form/ElementForm.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/index.js create mode 100644 src/components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/listeners/template.js create mode 100644 src/components/bpmnProcessDesigner/package/penal/listeners/utilSelf.ts create mode 100644 src/components/bpmnProcessDesigner/package/penal/multi-instance/ElementMultiInstance.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/task/ElementTask.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue create mode 100644 src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue create mode 100644 src/components/bpmnProcessDesigner/package/theme/element-variables.scss create mode 100644 src/components/bpmnProcessDesigner/package/theme/index.scss create mode 100644 src/components/bpmnProcessDesigner/package/theme/process-designer.scss create mode 100644 src/components/bpmnProcessDesigner/package/theme/process-panel.scss create mode 100644 src/components/bpmnProcessDesigner/package/utils.ts create mode 100644 src/components/bpmnProcessDesigner/src/highlight/index.js create mode 100644 src/components/bpmnProcessDesigner/src/modules/custom-renderer/CustomRenderer.js create mode 100644 src/components/bpmnProcessDesigner/src/modules/custom-renderer/index.js create mode 100644 src/components/bpmnProcessDesigner/src/modules/rules/CustomRules.js create mode 100644 src/components/bpmnProcessDesigner/src/modules/rules/index.js create mode 100644 src/components/bpmnProcessDesigner/src/translations.ts create mode 100644 src/components/bpmnProcessDesigner/src/utils/directive/clickOutSide.js create mode 100644 src/components/bpmnProcessDesigner/src/utils/index.js create mode 100644 src/components/bpmnProcessDesigner/src/utils/xml2json.js create mode 100644 src/components/index.ts create mode 100644 src/config/axios/config.ts create mode 100644 src/config/axios/errorCode.ts create mode 100644 src/config/axios/index.ts create mode 100644 src/config/axios/service.ts create mode 100644 src/directives/index.ts create mode 100644 src/directives/permission/hasPermi.ts create mode 100644 src/directives/permission/hasRole.ts create mode 100644 src/hooks/event/useScrollTo.ts create mode 100644 src/hooks/web/useCache.ts create mode 100644 src/hooks/web/useConfigGlobal.ts create mode 100644 src/hooks/web/useCrudSchemas.ts create mode 100644 src/hooks/web/useDesign.ts create mode 100644 src/hooks/web/useEmitt.ts create mode 100644 src/hooks/web/useForm.ts create mode 100644 src/hooks/web/useI18n.ts create mode 100644 src/hooks/web/useIcon.ts create mode 100644 src/hooks/web/useIntro.ts create mode 100644 src/hooks/web/useLocale.ts create mode 100644 src/hooks/web/useMessage.ts create mode 100644 src/hooks/web/useNProgress.ts create mode 100644 src/hooks/web/usePageLoading.ts create mode 100644 src/hooks/web/useTable.ts create mode 100644 src/hooks/web/useTimeAgo.ts create mode 100644 src/hooks/web/useTitle.ts create mode 100644 src/hooks/web/useValidator.ts create mode 100644 src/hooks/web/useVxeCrudSchemas.ts create mode 100644 src/hooks/web/useVxeGrid.ts create mode 100644 src/hooks/web/useWatermark.ts create mode 100644 src/hooks/web/useXTable.ts create mode 100644 src/layout/Layout.vue create mode 100644 src/layout/components/AppView.vue create mode 100644 src/layout/components/Breadcrumb/index.ts create mode 100644 src/layout/components/Breadcrumb/src/Breadcrumb.vue create mode 100644 src/layout/components/Breadcrumb/src/helper.ts create mode 100644 src/layout/components/Collapse/index.ts create mode 100644 src/layout/components/Collapse/src/Collapse.vue create mode 100644 src/layout/components/ContextMenu/index.ts create mode 100644 src/layout/components/ContextMenu/src/ContextMenu.vue create mode 100644 src/layout/components/Footer/index.ts create mode 100644 src/layout/components/Footer/src/Footer.vue create mode 100644 src/layout/components/LocaleDropdown/index.ts create mode 100644 src/layout/components/LocaleDropdown/src/LocaleDropdown.vue create mode 100644 src/layout/components/Logo/index.ts create mode 100644 src/layout/components/Logo/src/Logo.vue create mode 100644 src/layout/components/Menu/index.ts create mode 100644 src/layout/components/Menu/src/Menu.vue create mode 100644 src/layout/components/Menu/src/components/useRenderMenuItem.tsx create mode 100644 src/layout/components/Menu/src/components/useRenderMenuTitle.tsx create mode 100644 src/layout/components/Menu/src/helper.ts create mode 100644 src/layout/components/Message/index.ts create mode 100644 src/layout/components/Message/src/Message.vue create mode 100644 src/layout/components/Screenfull/index.ts create mode 100644 src/layout/components/Screenfull/src/Screenfull.vue create mode 100644 src/layout/components/Setting/index.ts create mode 100644 src/layout/components/Setting/src/Setting.vue create mode 100644 src/layout/components/Setting/src/components/ColorRadioPicker.vue create mode 100644 src/layout/components/Setting/src/components/InterfaceDisplay.vue create mode 100644 src/layout/components/Setting/src/components/LayoutRadioPicker.vue create mode 100644 src/layout/components/SizeDropdown/index.ts create mode 100644 src/layout/components/SizeDropdown/src/SizeDropdown.vue create mode 100644 src/layout/components/TabMenu/index.ts create mode 100644 src/layout/components/TabMenu/src/TabMenu.vue create mode 100644 src/layout/components/TabMenu/src/helper.ts create mode 100644 src/layout/components/TagsView/index.ts create mode 100644 src/layout/components/TagsView/src/TagsView.vue create mode 100644 src/layout/components/TagsView/src/helper.ts create mode 100644 src/layout/components/ThemeSwitch/index.ts create mode 100644 src/layout/components/ThemeSwitch/src/ThemeSwitch.vue create mode 100644 src/layout/components/ToolHeader.vue create mode 100644 src/layout/components/UserInfo/index.ts create mode 100644 src/layout/components/UserInfo/src/UserInfo.vue create mode 100644 src/layout/components/useRenderLayout.tsx create mode 100644 src/locales/en.ts create mode 100644 src/locales/zh-CN.ts create mode 100644 src/main.ts create mode 100644 src/permission.ts create mode 100644 src/plugins/animate.css/index.ts create mode 100644 src/plugins/echarts/index.ts create mode 100644 src/plugins/elementPlus/index.ts create mode 100644 src/plugins/formCreate/index.ts create mode 100644 src/plugins/svgIcon/index.ts create mode 100644 src/plugins/vueI18n/helper.ts create mode 100644 src/plugins/vueI18n/index.ts create mode 100644 src/plugins/vxeTable/index.ts create mode 100644 src/plugins/vxeTable/renderer/dataPicker.tsx create mode 100644 src/plugins/vxeTable/renderer/dataTimeRangePicker.tsx create mode 100644 src/plugins/vxeTable/renderer/dict.tsx create mode 100644 src/plugins/vxeTable/renderer/html.tsx create mode 100644 src/plugins/vxeTable/renderer/img.tsx create mode 100644 src/plugins/vxeTable/renderer/index.tsx create mode 100644 src/plugins/vxeTable/renderer/link.tsx create mode 100644 src/plugins/vxeTable/renderer/preview.tsx create mode 100644 src/plugins/windi.css/index.ts create mode 100644 src/router/index.ts create mode 100644 src/router/modules/remaining.ts create mode 100644 src/store/index.ts create mode 100644 src/store/modules/app.ts create mode 100644 src/store/modules/dict.ts create mode 100644 src/store/modules/locale.ts create mode 100644 src/store/modules/permission.ts create mode 100644 src/store/modules/tagsView.ts create mode 100644 src/store/modules/user.ts create mode 100644 src/styles/global.module.scss create mode 100644 src/styles/index.scss create mode 100644 src/styles/theme.scss create mode 100644 src/styles/var.css create mode 100644 src/styles/variables.scss create mode 100644 src/styles/vxe.css create mode 100644 src/types/auto-components.d.ts create mode 100644 src/types/auto-imports.d.ts create mode 100644 src/types/components.d.ts create mode 100644 src/types/configGlobal.d.ts create mode 100644 src/types/contextMenu.d.ts create mode 100644 src/types/descriptions.d.ts create mode 100644 src/types/elementPlus.d.ts create mode 100644 src/types/form.d.ts create mode 100644 src/types/icon.d.ts create mode 100644 src/types/infoTip.d.ts create mode 100644 src/types/layout.d.ts create mode 100644 src/types/localeDropdown.d.ts create mode 100644 src/types/qrcode.d.ts create mode 100644 src/types/table.d.ts create mode 100644 src/types/theme.d.ts create mode 100644 src/utils/Logger.ts create mode 100644 src/utils/auth.ts create mode 100644 src/utils/color.ts create mode 100644 src/utils/constants.ts create mode 100644 src/utils/dict.ts create mode 100644 src/utils/domUtils.ts create mode 100644 src/utils/download.ts create mode 100644 src/utils/env.ts create mode 100644 src/utils/filt.ts create mode 100644 src/utils/formCreate.ts create mode 100644 src/utils/formRules.ts create mode 100644 src/utils/formatTime.ts create mode 100644 src/utils/index.ts create mode 100644 src/utils/is.ts create mode 100644 src/utils/jsencrypt.ts create mode 100644 src/utils/propTypes.ts create mode 100644 src/utils/routerHelper.ts create mode 100644 src/utils/tree.ts create mode 100644 src/utils/tsxHelper.ts create mode 100644 src/views/Error/403.vue create mode 100644 src/views/Error/404.vue create mode 100644 src/views/Error/500.vue create mode 100644 src/views/Home/Index.vue create mode 100644 src/views/Home/Index2.vue create mode 100644 src/views/Home/echarts-data.ts create mode 100644 src/views/Home/types.ts create mode 100644 src/views/Login/Login.vue create mode 100644 src/views/Login/components/LoginForm.vue create mode 100644 src/views/Login/components/LoginFormTitle.vue create mode 100644 src/views/Login/components/MobileForm.vue create mode 100644 src/views/Login/components/QrCodeForm.vue create mode 100644 src/views/Login/components/RegisterForm.vue create mode 100644 src/views/Login/components/index.ts create mode 100644 src/views/Login/components/useLogin.ts create mode 100644 src/views/Profile/Index.vue create mode 100644 src/views/Profile/components/BasicInfo.vue create mode 100644 src/views/Profile/components/ProfileUser.vue create mode 100644 src/views/Profile/components/ResetPwd.vue create mode 100644 src/views/Profile/components/UserAvatar.vue create mode 100644 src/views/Profile/components/UserSocial.vue create mode 100644 src/views/Profile/components/index.ts create mode 100644 src/views/Redirect/Redirect.vue create mode 100644 src/views/bpm/definition/definition.data.ts create mode 100644 src/views/bpm/definition/index.vue create mode 100644 src/views/bpm/form/form.data.ts create mode 100644 src/views/bpm/form/formEditor.vue create mode 100644 src/views/bpm/form/index.vue create mode 100644 src/views/bpm/group/group.data.ts create mode 100644 src/views/bpm/group/index.vue create mode 100644 src/views/bpm/model/index.vue create mode 100644 src/views/bpm/model/model.data.ts create mode 100644 src/views/bpm/model/modelEditor.vue create mode 100644 src/views/bpm/oa/leave/create.vue create mode 100644 src/views/bpm/oa/leave/detail.vue create mode 100644 src/views/bpm/oa/leave/index.vue create mode 100644 src/views/bpm/oa/leave/leave.data.ts create mode 100644 src/views/bpm/processInstance/create.vue create mode 100644 src/views/bpm/processInstance/detail.vue create mode 100644 src/views/bpm/processInstance/index.vue create mode 100644 src/views/bpm/processInstance/process.create.ts create mode 100644 src/views/bpm/processInstance/process.data.ts create mode 100644 src/views/bpm/task/done/done.data.ts create mode 100644 src/views/bpm/task/done/index.vue create mode 100644 src/views/bpm/task/todo/index.vue create mode 100644 src/views/bpm/task/todo/todo.data.ts create mode 100644 src/views/bpm/taskAssignRule/index.vue create mode 100644 src/views/bpm/taskAssignRule/taskAssignRule.data.ts create mode 100644 src/views/infra/apiAccessLog/apiAccessLog.data.ts create mode 100644 src/views/infra/apiAccessLog/index.vue create mode 100644 src/views/infra/apiErrorLog/apiErrorLog.data.ts create mode 100644 src/views/infra/apiErrorLog/index.vue create mode 100644 src/views/infra/build/index.vue create mode 100644 src/views/infra/codegen/EditTable.vue create mode 100644 src/views/infra/codegen/codegen.data.ts create mode 100644 src/views/infra/codegen/components/BasicInfoForm.vue create mode 100644 src/views/infra/codegen/components/CloumInfoForm.vue create mode 100644 src/views/infra/codegen/components/ImportTable.vue create mode 100644 src/views/infra/codegen/components/Preview.vue create mode 100644 src/views/infra/codegen/components/index.ts create mode 100644 src/views/infra/codegen/index.vue create mode 100644 src/views/infra/config/config.data.ts create mode 100644 src/views/infra/config/index.vue create mode 100644 src/views/infra/dataSourceConfig/dataSourceConfig.data.ts create mode 100644 src/views/infra/dataSourceConfig/index.vue create mode 100644 src/views/infra/dbDoc/index.vue create mode 100644 src/views/infra/druid/index.vue create mode 100644 src/views/infra/fileConfig/fileConfig.data.ts create mode 100644 src/views/infra/fileConfig/index.vue create mode 100644 src/views/infra/fileList/fileList.data.ts create mode 100644 src/views/infra/fileList/index.vue create mode 100644 src/views/infra/job/JobLog.vue create mode 100644 src/views/infra/job/index.vue create mode 100644 src/views/infra/job/job.data.ts create mode 100644 src/views/infra/job/jobLog.data.ts create mode 100644 src/views/infra/redis/index.vue create mode 100644 src/views/infra/server/index.vue create mode 100644 src/views/infra/skywalking/index.vue create mode 100644 src/views/infra/swagger/index.vue create mode 100644 src/views/infra/testDemo/index.vue create mode 100644 src/views/infra/webSocket/index.vue create mode 100644 src/views/mp/account/index.vue create mode 100644 src/views/mp/autoReply/index.vue create mode 100644 src/views/mp/draft/index.vue create mode 100644 src/views/mp/freePublish/index.vue create mode 100644 src/views/mp/material/index.vue create mode 100644 src/views/mp/menu/index.vue create mode 100644 src/views/mp/message/index.vue create mode 100644 src/views/mp/mpuser/index.vue create mode 100644 src/views/mp/statistics/index.vue create mode 100644 src/views/pay/app/app.data.ts create mode 100644 src/views/pay/app/index.vue create mode 100644 src/views/pay/merchant/index.vue create mode 100644 src/views/pay/merchant/merchant.data.ts create mode 100644 src/views/pay/order/index.vue create mode 100644 src/views/pay/order/order.data.ts create mode 100644 src/views/pay/refund/index.vue create mode 100644 src/views/pay/refund/refund.data.ts create mode 100644 src/views/report/goview/index.vue create mode 100644 src/views/report/jmreport/index.vue create mode 100644 src/views/system/dept/dept.data.ts create mode 100644 src/views/system/dept/index.vue create mode 100644 src/views/system/dict/dict.data.ts create mode 100644 src/views/system/dict/dict.type.ts create mode 100644 src/views/system/dict/index.vue create mode 100644 src/views/system/errorCode/errorCode.data.ts create mode 100644 src/views/system/errorCode/index.vue create mode 100644 src/views/system/loginlog/index.vue create mode 100644 src/views/system/loginlog/loginLog.data.ts create mode 100644 src/views/system/mail/account/account.data.ts create mode 100644 src/views/system/mail/account/index.vue create mode 100644 src/views/system/mail/log/index.vue create mode 100644 src/views/system/mail/log/log.data.ts create mode 100644 src/views/system/mail/template/index.vue create mode 100644 src/views/system/mail/template/template.data.ts create mode 100644 src/views/system/menu/index.vue create mode 100644 src/views/system/menu/menu.data.ts create mode 100644 src/views/system/notice/index.vue create mode 100644 src/views/system/notice/notice.data.ts create mode 100644 src/views/system/notify/message/index.vue create mode 100644 src/views/system/notify/message/message.data.ts create mode 100644 src/views/system/notify/my/index.vue create mode 100644 src/views/system/notify/my/my.data.ts create mode 100644 src/views/system/notify/template/index.vue create mode 100644 src/views/system/notify/template/template.data.ts create mode 100644 src/views/system/oauth2/client/client.data.ts create mode 100644 src/views/system/oauth2/client/index.vue create mode 100644 src/views/system/oauth2/token/index.vue create mode 100644 src/views/system/oauth2/token/token.data.ts create mode 100644 src/views/system/operatelog/index.vue create mode 100644 src/views/system/operatelog/operatelog.data.ts create mode 100644 src/views/system/post/PostForm.vue create mode 100644 src/views/system/post/index.vue create mode 100644 src/views/system/post/post.data.ts create mode 100644 src/views/system/role/index.vue create mode 100644 src/views/system/role/role.data.ts create mode 100644 src/views/system/sensitiveWord/index.vue create mode 100644 src/views/system/sensitiveWord/sensitiveWord.data.ts create mode 100644 src/views/system/sms/smsChannel/index.vue create mode 100644 src/views/system/sms/smsChannel/sms.channel.data.ts create mode 100644 src/views/system/sms/smsLog/index.vue create mode 100644 src/views/system/sms/smsLog/sms.log.data.ts create mode 100644 src/views/system/sms/smsTemplate/index.vue create mode 100644 src/views/system/sms/smsTemplate/sms.template.data.ts create mode 100644 src/views/system/tenant/index.vue create mode 100644 src/views/system/tenant/tenant.data.ts create mode 100644 src/views/system/tenantPackage/index.vue create mode 100644 src/views/system/tenantPackage/tenantPackage.data.ts create mode 100644 src/views/system/user/index.vue create mode 100644 src/views/system/user/user.data.ts create mode 100644 stylelint.config.js create mode 100644 tsconfig.json create mode 100644 types/components.d.ts create mode 100644 types/custom-types.d.ts create mode 100644 types/env.d.ts create mode 100644 types/global.d.ts create mode 100644 types/router.d.ts create mode 100644 vite.config.ts create mode 100644 windi.config.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..79a12ffa --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true +[*.{js,ts,vue}] +charset = utf-8 # 设置文件字符集为 utf-8 +end_of_line = lf # 控制换行类型(lf | cr | crlf) +insert_final_newline = true # 始终在文件末尾插入一个新行 +indent_style = space # 缩进风格(tab | space) +indent_size = 2 # 缩进大小 +max_line_length = 100 # 最大行长度 + +[*.md] # 仅 md 文件适用以下规则 +max_line_length = off # 关闭最大行长度限制 +trim_trailing_whitespace = false # 关闭末尾空格修剪 diff --git a/.env b/.env new file mode 100644 index 00000000..6307b2d7 --- /dev/null +++ b/.env @@ -0,0 +1,15 @@ +# 标题 +VITE_APP_TITLE=芋道管理系统 + +# 项目本地运行端口号 +VITE_PORT=80 + +# open 运行 npm run dev 时自动打开浏览器 +VITE_OPEN=true + +# 租户开关 +VITE_APP_TENANT_ENABLE=true + +# 验证码的开关 +VITE_APP_CAPTCHA_ENABLE=true + diff --git a/.env.base b/.env.base new file mode 100644 index 00000000..fdb26d85 --- /dev/null +++ b/.env.base @@ -0,0 +1,19 @@ +# 本地开发环境 +NODE_ENV=development + +VITE_DEV=true + +# 请求路径 +VITE_BASE_URL='http://localhost:48080' + +# 上传路径 +VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload' + +# 接口前缀 +VITE_API_BASEPATH=/dev-api + +# 接口地址 +VITE_API_URL=/admin-api + +# 打包路径 +VITE_BASE_PATH=/ diff --git a/.env.dev b/.env.dev new file mode 100644 index 00000000..3b85e481 --- /dev/null +++ b/.env.dev @@ -0,0 +1,31 @@ +# 开发环境 +NODE_ENV=production + +VITE_DEV=false + +# 请求路径 +VITE_BASE_URL='http://localhost:48080' + +# 上传路径 +VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload' + +# 接口前缀 +VITE_API_BASEPATH=/dev-api + +# 接口地址 +VITE_API_URL=/admin-api + +# 打包路径 +VITE_BASE_PATH=/dist-dev/ + +# 是否删除debugger +VITE_DROP_DEBUGGER=false + +# 是否删除console.log +VITE_DROP_CONSOLE=false + +# 是否sourcemap +VITE_SOURCEMAP=true + +# 输出路径 +VITE_OUT_DIR=dist-dev diff --git a/.env.pro b/.env.pro new file mode 100644 index 00000000..8348e02e --- /dev/null +++ b/.env.pro @@ -0,0 +1,31 @@ +# 生产环境 +NODE_ENV=production + +VITE_DEV=false + +# 请求路径 +VITE_BASE_URL='http://localhost:48080' + +# 上传路径 +VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload' + +# 接口前缀 +VITE_API_BASEPATH= + +# 接口地址 +VITE_API_URL=/admin-api + +# 是否删除debugger +VITE_DROP_DEBUGGER=true + +# 是否删除console.log +VITE_DROP_CONSOLE=true + +# 是否sourcemap +VITE_SOURCEMAP=false + +# 打包路径 +VITE_BASE_PATH=/ + +# 输出路径 +VITE_OUT_DIR=dist-pro diff --git a/.env.test b/.env.test new file mode 100644 index 00000000..85e2cf54 --- /dev/null +++ b/.env.test @@ -0,0 +1,31 @@ +# 测试环境 +NODE_ENV=production + +VITE_DEV=false + +# 请求路径 +VITE_BASE_URL='http://localhost:48080' + +# 上传路径 +VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload' + +# 接口前缀 +VITE_API_BASEPATH= + +# 接口地址 +VITE_API_URL=/admin-api + +# 是否删除debugger +VITE_DROP_DEBUGGER=false + +# 是否删除console.log +VITE_DROP_CONSOLE=false + +# 是否sourcemap +VITE_SOURCEMAP=true + +# 打包路径 +VITE_BASE_PATH=/dist-test/ + +# 输出路径 +VITE_OUT_DIR=dist-test diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..1e85c0fb --- /dev/null +++ b/.eslintignore @@ -0,0 +1,8 @@ +/build/ +/config/ +/dist/ +/*.js +/test/unit/coverage/ +/node_modules/* +/dist* +/src/main.ts diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json new file mode 100644 index 00000000..024c96a3 --- /dev/null +++ b/.eslintrc-auto-import.json @@ -0,0 +1,259 @@ +{ + "globals": { + "EffectScope": true, + "ElMessage": true, + "ElMessageBox": true, + "ElTag": true, + "asyncComputed": true, + "autoResetRef": true, + "computed": true, + "computedAsync": true, + "computedEager": true, + "computedInject": true, + "computedWithControl": true, + "controlledComputed": true, + "controlledRef": true, + "createApp": true, + "createEventHook": true, + "createGlobalState": true, + "createInjectionState": true, + "createReactiveFn": true, + "createSharedComposable": true, + "createUnrefFn": true, + "customRef": true, + "debouncedRef": true, + "debouncedWatch": true, + "defineAsyncComponent": true, + "defineComponent": true, + "eagerComputed": true, + "effectScope": true, + "extendRef": true, + "getCurrentInstance": true, + "getCurrentScope": true, + "h": true, + "ignorableWatch": true, + "inject": true, + "isDefined": true, + "isProxy": true, + "isReactive": true, + "isReadonly": true, + "isRef": true, + "makeDestructurable": true, + "markRaw": true, + "nextTick": true, + "onActivated": true, + "onBeforeMount": true, + "onBeforeUnmount": true, + "onBeforeUpdate": true, + "onClickOutside": true, + "onDeactivated": true, + "onErrorCaptured": true, + "onKeyStroke": true, + "onLongPress": true, + "onMounted": true, + "onRenderTracked": true, + "onRenderTriggered": true, + "onScopeDispose": true, + "onServerPrefetch": true, + "onStartTyping": true, + "onUnmounted": true, + "onUpdated": true, + "pausableWatch": true, + "provide": true, + "reactify": true, + "reactifyObject": true, + "reactive": true, + "reactiveComputed": true, + "reactiveOmit": true, + "reactivePick": true, + "readonly": true, + "ref": true, + "refAutoReset": true, + "refDebounced": true, + "refDefault": true, + "refThrottled": true, + "refWithControl": true, + "resolveComponent": true, + "resolveRef": true, + "resolveUnref": true, + "shallowReactive": true, + "shallowReadonly": true, + "shallowRef": true, + "syncRef": true, + "syncRefs": true, + "templateRef": true, + "throttledRef": true, + "throttledWatch": true, + "toRaw": true, + "toReactive": true, + "toRef": true, + "toRefs": true, + "triggerRef": true, + "tryOnBeforeMount": true, + "tryOnBeforeUnmount": true, + "tryOnMounted": true, + "tryOnScopeDispose": true, + "tryOnUnmounted": true, + "unref": true, + "unrefElement": true, + "until": true, + "useActiveElement": true, + "useArrayEvery": true, + "useArrayFilter": true, + "useArrayFind": true, + "useArrayFindIndex": true, + "useArrayJoin": true, + "useArrayMap": true, + "useArrayReduce": true, + "useArraySome": true, + "useAsyncQueue": true, + "useAsyncState": true, + "useAttrs": true, + "useBase64": true, + "useBattery": true, + "useBluetooth": true, + "useBreakpoints": true, + "useBroadcastChannel": true, + "useBrowserLocation": true, + "useCached": true, + "useClipboard": true, + "useColorMode": true, + "useConfirmDialog": true, + "useCounter": true, + "useCssModule": true, + "useCssVar": true, + "useCssVars": true, + "useCurrentElement": true, + "useCycleList": true, + "useDark": true, + "useDateFormat": true, + "useDebounce": true, + "useDebounceFn": true, + "useDebouncedRefHistory": true, + "useDeviceMotion": true, + "useDeviceOrientation": true, + "useDevicePixelRatio": true, + "useDevicesList": true, + "useDisplayMedia": true, + "useDocumentVisibility": true, + "useDraggable": true, + "useDropZone": true, + "useElementBounding": true, + "useElementByPoint": true, + "useElementHover": true, + "useElementSize": true, + "useElementVisibility": true, + "useEventBus": true, + "useEventListener": true, + "useEventSource": true, + "useEyeDropper": true, + "useFavicon": true, + "useFetch": true, + "useFileDialog": true, + "useFileSystemAccess": true, + "useFocus": true, + "useFocusWithin": true, + "useFps": true, + "useFullscreen": true, + "useGamepad": true, + "useGeolocation": true, + "useIdle": true, + "useImage": true, + "useInfiniteScroll": true, + "useIntersectionObserver": true, + "useInterval": true, + "useIntervalFn": true, + "useKeyModifier": true, + "useLastChanged": true, + "useLocalStorage": true, + "useMagicKeys": true, + "useManualRefHistory": true, + "useMediaControls": true, + "useMediaQuery": true, + "useMemoize": true, + "useMemory": true, + "useMounted": true, + "useMouse": true, + "useMouseInElement": true, + "useMousePressed": true, + "useMutationObserver": true, + "useNavigatorLanguage": true, + "useNetwork": true, + "useNow": true, + "useObjectUrl": true, + "useOffsetPagination": true, + "useOnline": true, + "usePageLeave": true, + "useParallax": true, + "usePermission": true, + "usePointer": true, + "usePointerSwipe": true, + "usePreferredColorScheme": true, + "usePreferredDark": true, + "usePreferredLanguages": true, + "useRafFn": true, + "useRefHistory": true, + "useResizeObserver": true, + "useRoute": true, + "useRouter": true, + "useScreenOrientation": true, + "useScreenSafeArea": true, + "useScriptTag": true, + "useScroll": true, + "useScrollLock": true, + "useSessionStorage": true, + "useShare": true, + "useSlots": true, + "useSpeechRecognition": true, + "useSpeechSynthesis": true, + "useStepper": true, + "useStorage": true, + "useStorageAsync": true, + "useStyleTag": true, + "useSupported": true, + "useSwipe": true, + "useTemplateRefsList": true, + "useTextDirection": true, + "useTextSelection": true, + "useTextareaAutosize": true, + "useThrottle": true, + "useThrottleFn": true, + "useThrottledRefHistory": true, + "useTimeAgo": true, + "useTimeout": true, + "useTimeoutFn": true, + "useTimeoutPoll": true, + "useTimestamp": true, + "useTitle": true, + "useToggle": true, + "useTransition": true, + "useUrlSearchParams": true, + "useUserMedia": true, + "useVModel": true, + "useVModels": true, + "useVibrate": true, + "useVirtualList": true, + "useWakeLock": true, + "useWebNotification": true, + "useWebSocket": true, + "useWebWorker": true, + "useWebWorkerFn": true, + "useWindowFocus": true, + "useWindowScroll": true, + "useWindowSize": true, + "watch": true, + "watchArray": true, + "watchAtMost": true, + "watchDebounced": true, + "watchEffect": true, + "watchIgnorable": true, + "watchOnce": true, + "watchPausable": true, + "watchPostEffect": true, + "watchSyncEffect": true, + "watchThrottled": true, + "watchTriggerable": true, + "watchWithFilter": true, + "whenever": true + } +} diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..244dbbbf --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,69 @@ +// @ts-check +const { defineConfig } = require('eslint-define-config') +module.exports = defineConfig({ + root: true, + env: { + browser: true, + node: true, + es6: true + }, + parser: 'vue-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser', + ecmaVersion: 2020, + sourceType: 'module', + jsxPragma: 'React', + ecmaFeatures: { + jsx: true + } + }, + extends: [ + 'plugin:vue/vue3-recommended', + 'plugin:@typescript-eslint/recommended', + 'prettier', + 'plugin:prettier/recommended', + './.eslintrc-auto-import.json' + ], + rules: { + 'vue/script-setup-uses-vars': 'error', + 'vue/no-reserved-component-names': 'off', + '@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', + 'no-unused-vars': 'error', + '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/.gitignore b/.gitignore new file mode 100644 index 00000000..0b05e6a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +/dist* +*-lock.* +pnpm-debug diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..21dcc7fb --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +/node_modules/** +/dist/ +/dist* +/public/* +/docs/* +/vite.config.ts +/src/types/env.d.ts +/docs/**/* +CHANGELOG diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 00000000..aa605b49 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,6 @@ +/dist/* +/public/* +public/* +/dist* +/src/types/env.d.ts +/docs/**/* diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..d1530933 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,15 @@ +{ + "recommendations": [ + "voorjaar.windicss-intellisense", + "vscode-icons-team.vscode-icons", + "davidanson.vscode-markdownlint", + "stylelint.vscode-stylelint", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "vue.volar", + "lokalise.i18n-ally", + "mikestead.dotenv", + "eamodio.gitlens", + "antfu.iconify" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..38cc3052 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,44 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "prettier.enable": true, + "editor.formatOnType": true, + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "[vue]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[scss]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "i18n-ally.localesPaths": ["src/locales"], + "i18n-ally.keystyle": "nested", + "i18n-ally.sortKeys": true, + "i18n-ally.namespace": true, + "i18n-ally.enabledParsers": ["ts"], + "i18n-ally.sourceLanguage": "en", + "i18n-ally.displayLanguage": "zh-CN", + "i18n-ally.enabledFrameworks": ["vue", "react"], + "god.tsconfig": "./tsconfig.json", + "vue-i18n.i18nPaths": "src/locales" +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..9861118a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present Archer + +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.en.md b/README.en.md deleted file mode 100644 index b91969e7..00000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# yudao-ui-admin-vue3 - -#### Description -芋道管理后台,基于 Vue3 + Element Plus 实现,支持 RBAC 动态权限、数据权限、SaaS 多租户、Flowable 工作流、三方登录、支付、短信、商城等功能。 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 322a731f..2ed9b31b 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,231 @@ -# yudao-ui-admin-vue3 +**严肃声明:现在、未来都不会有商业版本,所有代码全部开源!!** -#### 介绍 -芋道管理后台,基于 Vue3 + Element Plus 实现,支持 RBAC 动态权限、数据权限、SaaS 多租户、Flowable 工作流、三方登录、支付、短信、商城等功能。 +**「我喜欢写代码,乐此不疲」** +**「我喜欢做开源,以此为乐」** -#### 软件架构 -软件架构说明 +我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。 +如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。 -#### 安装教程 +## 🐶 新手必读 -1. xxxx -2. xxxx -3. xxxx +* 演示地址: +* 启动文档: +* 视频教程: -#### 使用说明 +## 🐯 平台简介 -1. xxxx -2. xxxx -3. xxxx +**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。 -#### 参与贡献 +基于 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) 实现,使用 Element Plus 免费开源的中后台模版,具备如下特性: -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +TODO +* 最新技术栈:使用 Vue3、Vite4 等前端前沿技术开发 +* TypeScript: 应用程序级 JavaScript 的语言 +* 主题: 可配置的主题 +* 国际化:内置完善的国际化方案 +* 权限:内置完善的动态路由权限生成方案 +* 组件:二次封装了多个常用的组件 +* 示例:内置丰富的示例 -#### 特技 +## 技术栈 -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +| 框架 | 说明 | 版本 | +|----------------------------------------------------------------------|------------------|--------| +| [Vue](https://staging-cn.vuejs.org/) | vue 框架 | 3.2.47 | +| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 4.1.1 | +| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.2.29 | +| [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 的超集 | 4.9.5 | +| [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.0.30 | +| [vueuse](https://vueuse.org/) | 常用工具集 | 9.12.0 | +| [vxe-table](https://vxetable.cn/) | vue 最强表单 | 4.3.9 | +| [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.0.1 | +| [wangeditor](https://www.wangeditor.com/) | 富文本编辑器 | 5.1.23 | + +## 开发工具 + +推荐 VS Code 开发,配合插件如下: + +| 插件名 | 功能 | +|-------------------------------|--------------------------| +| TypeScript Vue Plugin (Volar) | 用于 TypeScript 的 Vue 插件 | +| Vue Language Features (Volar) | Vue3.0 语法支持 | +| WindiCSS IntelliSense | 自动完成、语法突出显示、代码折叠和构建等高级功能 | +| Iconify IntelliSense | Iconify 预览和搜索 | +| i18n Ally | 国际化智能提示 | +| Stylelint | Css 格式化 | +| Prettier | 代码格式化 | +| ESLint | 脚本代码检查 | +| DotENV | env 文件高亮 | + +## 内置功能 + +系统内置多种多种业务功能,可以用于快速你的业务系统: + +* 系统功能 +* 基础设施 +* 工作流程 +* 支付系统 +* 会员中心 +* 数据报表 +* 商城系统 +* 微信公众号 + +### 系统功能 + +| | 功能 | 描述 | +|-----|-------|---------------------------------| +| | 用户管理 | 用户是系统操作者,该功能主要完成系统用户配置 | +| ⭐️ | 在线用户 | 当前系统中活跃用户状态监控,支持手动踢下线 | +| | 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | +| | 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等,本地缓存提供性能 | +| | 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限 | +| | 岗位管理 | 配置系统用户所属担任职务 | +| 🚀 | 租户管理 | 配置系统租户,支持 SaaS 场景下的多租户功能 | +| 🚀 | 租户套餐 | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限 | +| | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | +| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 | +| 🚀 | 邮件管理 | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台 | +| 🚀 | 站内信 | 系统内的消息通知,提供站内信模版、站内信消息 | +| 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 | +| ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 | +| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 | +| | 通知公告 | 系统通知公告信息发布维护 | +| 🚀 | 敏感词 | 配置系统敏感词,支持标签分组 | +| 🚀 | 应用管理 | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 | +| 🚀 | 地区管理 | 展示省份、城市、区镇等城市信息,支持 IP 对应城市 | + +### 工作流程 + +| | 功能 | 描述 | +|-----|-------|----------------------------------------| +| 🚀 | 流程模型 | 配置工作流的流程模型,支持文件导入与在线设计流程图,提供 7 种任务分配规则 | +| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 | +| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 | +| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 | +| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转发、委派、退回等操作 | +| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,未来会支持回退操作 | +| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 | + +### 支付系统 + +| | 功能 | 描述 | +|-----|------|---------------------------| +| 🚀 | 商户信息 | 管理商户信息,支持 Saas 场景下的多商户功能 | +| 🚀 | 应用信息 | 配置商户的应用信息,对接支付宝、微信等多个支付渠道 | +| 🚀 | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单 | +| 🚀 | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单 | + +ps:核心功能已经实现,正在对接微信小程序中... + +### 基础设施 + +| | 功能 | 描述 | +|-----|----------|----------------------------------------------| +| 🚀 | 代码生成 | 前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载 | +| 🚀 | 系统接口 | 基于 Swagger 自动生成相关的 RESTful API 接口文档 | +| 🚀 | 数据库文档 | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式 | +| | 表单构建 | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件 | +| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 | +| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 | +| 🚀 | 文件服务 | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等 | +| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 | +| | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 | +| | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 | +| 🚀 | 消息队列 | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 | +| 🚀 | Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 | +| 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 | +| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 | +| 🚀 | 分布式锁 | 基于 Redis 实现分布式锁,满足并发场景 | +| 🚀 | 幂等组件 | 基于 Redis 实现幂等组件,解决重复请求问题 | +| 🚀 | 服务保障 | 基于 Resilience4j 实现服务的稳定性,包括限流、熔断等功能 | +| 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 | +| 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 | + +### 数据报表 + +| | 功能 | 描述 | +|-----|-------|--------------------| +| 🚀 | 报表设计器 | 支持数据报表、图形报表、打印设计等 | +| 🚀 | 大屏设计器 | 拖拽生成数据大屏,内置几十种图表组件 | + +### 微信公众号 + +| | 功能 | 描述 | +|-----|--------|-------------------------------| +| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 | +| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 | +| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 | +| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 | +| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 | +| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 | +| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 | +| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 | +| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 | +| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 | + +### 商城系统 + +建设中... + +![功能图](http://static.iocoder.cn/mall%20%E5%8A%9F%E8%83%BD%E5%9B%BE-min.png) + +![GIF 图-耐心等待](https://raw.githubusercontent.com/YunaiV/Blog/master/Mall/onemall-admin-min.gif) + +![GIF 图-耐心等待](https://raw.githubusercontent.com/YunaiV/Blog/master/Mall/onemall-h5-min.gif) + +## 🐷 演示图 + +### 系统功能 + +| 模块 | biu | biu | biu | +|------------|--------------------------------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------| +| 登录 & 首页 | ![登录](https://static.iocoder.cn/images/ruoyi-vue-pro/登录.jpg?imageView2/2/format/webp/w/1280) | ![首页](https://static.iocoder.cn/images/ruoyi-vue-pro/首页.jpg?imageView2/2/format/webp/w/1280) | ![个人中心](https://static.iocoder.cn/images/ruoyi-vue-pro/个人中心.jpg?imageView2/2/format/webp/w/1280) | +| 用户 & 应用 | ![用户管理](https://static.iocoder.cn/images/ruoyi-vue-pro/用户管理.jpg?imageView2/2/format/webp/w/1280) | ![令牌管理](https://static.iocoder.cn/images/ruoyi-vue-pro/令牌管理.jpg?imageView2/2/format/webp/w/1280) | ![应用管理](https://static.iocoder.cn/images/ruoyi-vue-pro/应用管理.jpg?imageView2/2/format/webp/w/1280) | +| 租户 & 套餐 | ![租户管理](https://static.iocoder.cn/images/ruoyi-vue-pro/租户管理.jpg?imageView2/2/format/webp/w/1280) | ![租户套餐](https://static.iocoder.cn/images/ruoyi-vue-pro/租户套餐.png) | - | +| 部门 & 岗位 | ![部门管理](https://static.iocoder.cn/images/ruoyi-vue-pro/部门管理.jpg?imageView2/2/format/webp/w/1280) | ![岗位管理](https://static.iocoder.cn/images/ruoyi-vue-pro/岗位管理.jpg?imageView2/2/format/webp/w/1280) | - | +| 菜单 & 角色 | ![菜单管理](https://static.iocoder.cn/images/ruoyi-vue-pro/菜单管理.jpg?imageView2/2/format/webp/w/1280) | ![角色管理](https://static.iocoder.cn/images/ruoyi-vue-pro/角色管理.jpg?imageView2/2/format/webp/w/1280) | - | +| 审计日志 | ![操作日志](https://static.iocoder.cn/images/ruoyi-vue-pro/操作日志.jpg?imageView2/2/format/webp/w/1280) | ![登录日志](https://static.iocoder.cn/images/ruoyi-vue-pro/登录日志.jpg?imageView2/2/format/webp/w/1280) | - | +| 短信 | ![短信渠道](https://static.iocoder.cn/images/ruoyi-vue-pro/短信渠道.jpg?imageView2/2/format/webp/w/1280) | ![短信模板](https://static.iocoder.cn/images/ruoyi-vue-pro/短信模板.jpg?imageView2/2/format/webp/w/1280) | ![短信日志](https://static.iocoder.cn/images/ruoyi-vue-pro/短信日志.jpg?imageView2/2/format/webp/w/1280) | +| 字典 & 敏感词 | ![字典类型](https://static.iocoder.cn/images/ruoyi-vue-pro/字典类型.jpg?imageView2/2/format/webp/w/1280) | ![字典数据](https://static.iocoder.cn/images/ruoyi-vue-pro/字典数据.jpg?imageView2/2/format/webp/w/1280) | ![敏感词](https://static.iocoder.cn/images/ruoyi-vue-pro/敏感词.jpg?imageView2/2/format/webp/w/1280) | +| 错误码 & 通知 | ![错误码管理](https://static.iocoder.cn/images/ruoyi-vue-pro/错误码管理.jpg?imageView2/2/format/webp/w/1280) | ![通知公告](https://static.iocoder.cn/images/ruoyi-vue-pro/通知公告.jpg?imageView2/2/format/webp/w/1280) | - | + +### 工作流程 + +| 模块 | biu | biu | biu | +|---------|------------------------------------------------------------------------|------------------------------------------------------------------------|------------------------------------------------------------------------| +| 流程模型 | ![流程模型-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-列表.jpg?imageView2/2/format/webp/w/1280) | ![流程模型-设计](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-设计.jpg?imageView2/2/format/webp/w/1280) | ![流程模型-定义](https://static.iocoder.cn/images/ruoyi-vue-pro/流程模型-定义.jpg?imageView2/2/format/webp/w/1280) | +| 表单 & 分组 | ![流程表单](https://static.iocoder.cn/images/ruoyi-vue-pro/流程表单.jpg?imageView2/2/format/webp/w/1280) | ![用户分组](https://static.iocoder.cn/images/ruoyi-vue-pro/用户分组.jpg?imageView2/2/format/webp/w/1280) | - | +| 我的流程 | ![我的流程-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-列表.jpg?imageView2/2/format/webp/w/1280) | ![我的流程-发起](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-发起.jpg?imageView2/2/format/webp/w/1280) | ![我的流程-详情](https://static.iocoder.cn/images/ruoyi-vue-pro/我的流程-详情.jpg?imageView2/2/format/webp/w/1280) | +| 待办 & 已办 | ![任务列表-审批](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-审批.jpg?imageView2/2/format/webp/w/1280) | ![任务列表-待办](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-待办.jpg?imageView2/2/format/webp/w/1280) | ![任务列表-已办](https://static.iocoder.cn/images/ruoyi-vue-pro/任务列表-已办.jpg?imageView2/2/format/webp/w/1280) | +| OA 请假 | ![OA请假-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/OA请假-列表.jpg?imageView2/2/format/webp/w/1280) | ![OA请假-发起](https://static.iocoder.cn/images/ruoyi-vue-pro/OA请假-发起.jpg?imageView2/2/format/webp/w/1280) | ![OA请假-详情](https://static.iocoder.cn/images/ruoyi-vue-pro/OA请假-详情.jpg?imageView2/2/format/webp/w/1280) | + +### 基础设施 + +| 模块 | biu | biu | biu | +|---------------|----------------------------------------------------------------------|--------------------------------------------------------------------|------------------------------------------------------------------| +| 代码生成 | ![代码生成](https://static.iocoder.cn/images/ruoyi-vue-pro/代码生成.jpg?imageView2/2/format/webp/w/1280) | ![生成效果](https://static.iocoder.cn/images/ruoyi-vue-pro/生成效果.jpg?imageView2/2/format/webp/w/1280) | - | +| 文档 | ![系统接口](https://static.iocoder.cn/images/ruoyi-vue-pro/系统接口.jpg?imageView2/2/format/webp/w/1280) | ![数据库文档](https://static.iocoder.cn/images/ruoyi-vue-pro/数据库文档.jpg?imageView2/2/format/webp/w/1280) | - | +| 文件 & 配置 | ![文件配置](https://static.iocoder.cn/images/ruoyi-vue-pro/文件配置.jpg?imageView2/2/format/webp/w/1280) | ![文件管理](https://static.iocoder.cn/images/ruoyi-vue-pro/文件管理2.jpg?imageView2/2/format/webp/w/1280) | ![配置管理](https://static.iocoder.cn/images/ruoyi-vue-pro/配置管理.jpg?imageView2/2/format/webp/w/1280) | +| 定时任务 | ![定时任务](https://static.iocoder.cn/images/ruoyi-vue-pro/定时任务.jpg?imageView2/2/format/webp/w/1280) | ![任务日志](https://static.iocoder.cn/images/ruoyi-vue-pro/任务日志.jpg?imageView2/2/format/webp/w/1280) | - | +| API 日志 | ![访问日志](https://static.iocoder.cn/images/ruoyi-vue-pro/访问日志.jpg?imageView2/2/format/webp/w/1280) | ![错误日志](https://static.iocoder.cn/images/ruoyi-vue-pro/错误日志.jpg?imageView2/2/format/webp/w/1280) | - | +| MySQL & Redis | ![MySQL](https://static.iocoder.cn/images/ruoyi-vue-pro/MySQL.jpg?imageView2/2/format/webp/w/1280) | ![Redis](https://static.iocoder.cn/images/ruoyi-vue-pro/Redis.jpg?imageView2/2/format/webp/w/1280) | - | +| 监控平台 | ![Java监控](https://static.iocoder.cn/images/ruoyi-vue-pro/Java监控.jpg?imageView2/2/format/webp/w/1280) | ![链路追踪](https://static.iocoder.cn/images/ruoyi-vue-pro/链路追踪.jpg?imageView2/2/format/webp/w/1280) | ![日志中心](https://static.iocoder.cn/images/ruoyi-vue-pro/日志中心.jpg?imageView2/2/format/webp/w/1280) | + +### 支付系统 + +| 模块 | biu | biu | biu | +|---------|------------------------------------------------------------------|------------------------------------------------------------------------|------------------------------------------------------------------------| +| 商家 & 应用 | ![商户信息](https://static.iocoder.cn/images/ruoyi-vue-pro/商户信息.jpg?imageView2/2/format/webp/w/1280) | ![应用信息-列表](https://static.iocoder.cn/images/ruoyi-vue-pro/应用信息-列表.jpg?imageView2/2/format/webp/w/1280) | ![应用信息-编辑](https://static.iocoder.cn/images/ruoyi-vue-pro/应用信息-编辑.jpg?imageView2/2/format/webp/w/1280) | +| 支付 & 退款 | ![支付订单](https://static.iocoder.cn/images/ruoyi-vue-pro/支付订单.jpg?imageView2/2/format/webp/w/1280) | ![退款订单](https://static.iocoder.cn/images/ruoyi-vue-pro/退款订单.jpg?imageView2/2/format/webp/w/1280) | --- | + +### 数据报表 + +| 模块 | biu | biu | biu | +|-------|--------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| +| 报表设计器 | ![数据报表](https://static.iocoder.cn/images/ruoyi-vue-pro/报表设计器-数据报表.jpg?imageView2/2/format/webp/w/1280) | ![图形报表](https://static.iocoder.cn/images/ruoyi-vue-pro/报表设计器-图形报表.jpg?imageView2/2/format/webp/w/1280) | ![报表设计器-打印设计](https://static.iocoder.cn/images/ruoyi-vue-pro/报表设计器-打印设计.jpg?imageView2/2/format/webp/w/1280) | +| 大屏设计器 | ![大屏列表](https://static.iocoder.cn/images/ruoyi-vue-pro/大屏设计器-列表.jpg?imageView2/2/format/webp/w/1280) | ![大屏预览](https://static.iocoder.cn/images/ruoyi-vue-pro/大屏设计器-预览.jpg?imageView2/2/format/webp/w/1280) | ![大屏编辑](https://static.iocoder.cn/images/ruoyi-vue-pro/大屏设计器-编辑.jpg?imageView2/2/format/webp/w/1280) | diff --git a/build/vite/index.ts b/build/vite/index.ts new file mode 100644 index 00000000..717db992 --- /dev/null +++ b/build/vite/index.ts @@ -0,0 +1,98 @@ +import { resolve } from 'path' +import Vue from '@vitejs/plugin-vue' +import VueJsx from '@vitejs/plugin-vue-jsx' +import WindiCSS from 'vite-plugin-windicss' +import progress from 'vite-plugin-progress' +import EslintPlugin from 'vite-plugin-eslint' +import PurgeIcons from 'vite-plugin-purge-icons' +import { ViteEjsPlugin } from 'vite-plugin-ejs' +import ElementPlus from 'unplugin-element-plus/vite' +import AutoImport from 'unplugin-auto-import/vite' +import Components from 'unplugin-vue-components/vite' +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' +import viteCompression from 'vite-plugin-compression' +import vueSetupExtend from 'vite-plugin-vue-setup-extend' +import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' +import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' +export function createVitePlugins(VITE_APP_TITLE: string) { + const root = process.cwd() + // 路径查找 + function pathResolve(dir: string) { + return resolve(root, '.', dir) + } + return [ + Vue(), + VueJsx(), + WindiCSS(), + progress(), + PurgeIcons(), + vueSetupExtend(), + ElementPlus(), + AutoImport({ + include: [ + /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx + /\.vue$/, + /\.vue\?vue/, // .vue + /\.md$/ // .md + ], + imports: [ + 'vue', + 'vue-router', + { + '@/hooks/web/useI18n': ['useI18n'], + '@/hooks/web/useXTable': ['useXTable'], + '@/hooks/web/useMessage': ['useMessage'], + '@/hooks/web/useVxeCrudSchemas': ['useVxeCrudSchemas'], + '@/utils/formRules': ['required'], + '@/utils/dict': ['DICT_TYPE'] + } + ], + dts: 'src/types/auto-imports.d.ts', + resolvers: [ElementPlusResolver()], + eslintrc: { + enabled: false, // Default `false` + filepath: './.eslintrc-auto-import.json', // Default `./.eslintrc-auto-import.json` + globalsPropValue: true // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable') + } + }), + Components({ + // 要搜索组件的目录的相对路径 + dirs: ['src/components'], + // 组件的有效文件扩展名 + extensions: ['vue', 'md'], + // 搜索子目录 + deep: true, + include: [/\.vue$/, /\.vue\?vue/], + // 生成自定义 `auto-components.d.ts` 全局声明 + dts: 'src/types/auto-components.d.ts', + // 自定义组件的解析器 + resolvers: [ElementPlusResolver()], + exclude: [/[\\/]node_modules[\\/]/] + }), + EslintPlugin({ + cache: false, + include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件 + }), + VueI18nPlugin({ + runtimeOnly: true, + compositionOnly: true, + include: [resolve(__dirname, 'src/locales/**')] + }), + createSvgIconsPlugin({ + iconDirs: [pathResolve('src/assets/svgs')], + symbolId: 'icon-[dir]-[name]', + svgoOptions: true + }), + viteCompression({ + verbose: true, // 是否在控制台输出压缩结果 + disable: false, // 是否禁用 + threshold: 10240, // 体积大于 threshold 才会被压缩,单位 b + algorithm: 'gzip', // 压缩算法,可选 [ 'gzip' , 'brotliCompress' ,'deflate' , 'deflateRaw'] + ext: '.gz', // 生成的压缩包后缀 + deleteOriginFile: false //压缩后是否删除源文件 + }), + ViteEjsPlugin({ + title: VITE_APP_TITLE + }) + ] +} diff --git a/build/vite/optimize.ts b/build/vite/optimize.ts new file mode 100644 index 00000000..f9108a7f --- /dev/null +++ b/build/vite/optimize.ts @@ -0,0 +1,68 @@ +const include = [ + 'qs', + 'url', + 'vue', + 'sass', + 'mitt', + 'axios', + 'pinia', + 'dayjs', + 'qrcode', + 'windicss', + 'vue-router', + 'vue-types', + 'vue-i18n', + 'xe-utils', + 'crypto-js', + 'cropperjs', + 'lodash-es', + 'vxe-table', + 'nprogress', + 'animate.css', + 'vxe-table/es/style', + 'web-storage-cache', + '@iconify/iconify', + '@vueuse/core', + '@zxcvbn-ts/core', + 'echarts/core', + 'echarts/charts', + 'echarts/components', + 'echarts/renderers', + 'echarts-wordcloud', + '@wangeditor/editor', + '@wangeditor/editor-for-vue', + 'element-plus', + 'element-plus/es', + 'element-plus/es/locale/lang/zh-cn', + 'element-plus/es/locale/lang/en', + 'element-plus/es/components/form/style/index', + 'element-plus/es/components/radio-group/style/index', + 'element-plus/es/components/radio/style/index', + 'element-plus/es/components/checkbox/style/index', + 'element-plus/es/components/checkbox-group/style/index', + 'element-plus/es/components/switch/style/index', + 'element-plus/es/components/time-picker/style/index', + 'element-plus/es/components/date-picker/style/index', + 'element-plus/es/components/col/style/index', + 'element-plus/es/components/form-item/style/index', + 'element-plus/es/components/alert/style/index', + 'element-plus/es/components/breadcrumb/style/index', + 'element-plus/es/components/select/style/index', + 'element-plus/es/components/input/style/index', + 'element-plus/es/components/breadcrumb-item/style/index', + 'element-plus/es/components/tag/style/index', + 'element-plus/es/components/pagination/style/index', + 'element-plus/es/components/table/style/index', + 'element-plus/es/components/table-column/style/index', + 'element-plus/es/components/card/style/index', + 'element-plus/es/components/row/style/index', + 'element-plus/es/components/button/style/index', + 'element-plus/es/components/menu/style/index', + 'element-plus/es/components/sub-menu/style/index', + 'element-plus/es/components/menu-item/style/index', + 'element-plus/es/components/option/style/index' +] + +const exclude = ['@iconify/json'] + +export { include, exclude } diff --git a/index.html b/index.html new file mode 100644 index 00000000..cce65bdc --- /dev/null +++ b/index.html @@ -0,0 +1,151 @@ + + + + + + + + + + <%= title %> + + +
+ +
+
+
+ +
<%= title %>
+
+
+
+
+
+
+
+
+ + + diff --git a/package.json b/package.json new file mode 100644 index 00000000..3f040dd1 --- /dev/null +++ b/package.json @@ -0,0 +1,137 @@ +{ + "name": "yudao-ui-admin-vue3", + "version": "1.7.0-snapshot.1922", + "description": "基于vue3、vite4、element-plus、typesScript", + "author": "xingyu", + "private": false, + "scripts": { + "i": "pnpm install", + "dev": "vite --mode base", + "ts:check": "vue-tsc --noEmit", + "build:pro": "vite build --mode pro", + "build:dev": "vite build --mode dev", + "build:test": "npm run ts:check && vite build --mode test", + "serve:pro": "vite preview --mode pro", + "serve:dev": "vite preview --mode dev", + "serve:test": "vite preview --mode test", + "npm:check": "npx npm-check-updates", + "clean": "npx rimraf node_modules", + "clean:cache": "npx rimraf node_modules/.cache", + "lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src", + "lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"", + "lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", + "lint:lint-staged": "lint-staged -c ", + "lint:pretty": "pretty-quick --staged" + }, + "dependencies": { + "@form-create/designer": "^3.1.0", + "@form-create/element-ui": "^3.1.17", + "@iconify/iconify": "^3.1.0", + "@vueuse/core": "^9.12.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.10", + "@zxcvbn-ts/core": "^2.2.1", + "animate.css": "^4.1.1", + "axios": "^1.3.2", + "bpmn-js-token-simulation": "^0.10.0", + "camunda-bpmn-moddle": "^7.0.1", + "cropperjs": "^1.5.13", + "crypto-js": "^4.1.1", + "dayjs": "^1.11.7", + "diagram-js": "^11.6.0", + "echarts": "^5.4.1", + "echarts-wordcloud": "^2.1.0", + "element-plus": "2.2.29", + "fast-xml-parser": "^4.0.13", + "highlight.js": "^11.7.0", + "intro.js": "^6.0.0", + "jsencrypt": "^3.3.1", + "lodash-es": "^4.17.21", + "min-dash": "^4.0.0", + "mitt": "^3.0.0", + "nprogress": "^0.2.0", + "pinia": "^2.0.30", + "qrcode": "^1.5.1", + "qs": "^6.11.0", + "steady-xml": "^0.1.0", + "url": "^0.11.0", + "vue": "3.2.47", + "vue-i18n": "9.2.2", + "vue-router": "^4.1.6", + "vue-types": "^5.0.2", + "vuedraggable": "^4.1.0", + "vxe-table": "^4.3.9", + "web-storage-cache": "^1.1.1", + "xe-utils": "^3.5.7", + "xml-js": "^1.6.11" + }, + "devDependencies": { + "@commitlint/cli": "^17.4.2", + "@commitlint/config-conventional": "^17.4.2", + "@iconify/json": "^2.2.19", + "@intlify/unplugin-vue-i18n": "^0.8.2", + "@purge-icons/generated": "^0.9.0", + "@types/intro.js": "^5.1.0", + "@types/lodash-es": "^4.17.6", + "@types/node": "^18.13.0", + "@types/nprogress": "^0.2.0", + "@types/qrcode": "^1.5.0", + "@types/qs": "^6.9.7", + "@typescript-eslint/eslint-plugin": "^5.51.0", + "@typescript-eslint/parser": "^5.51.0", + "@vitejs/plugin-legacy": "^4.0.1", + "@vitejs/plugin-vue": "^4.0.0", + "@vitejs/plugin-vue-jsx": "^3.0.0", + "autoprefixer": "^10.4.13", + "bpmn-js": "^8.9.0", + "bpmn-js-properties-panel": "^0.46.0", + "consola": "^2.15.3", + "eslint": "^8.33.0", + "eslint-config-prettier": "^8.6.0", + "eslint-define-config": "^1.15.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-vue": "^9.9.0", + "lint-staged": "^13.1.1", + "postcss": "^8.4.21", + "postcss-html": "^1.5.0", + "postcss-scss": "^4.0.6", + "prettier": "^2.8.4", + "rimraf": "^4.1.2", + "rollup": "^3.14.0", + "sass": "^1.58.0", + "stylelint": "^14.16.1", + "stylelint-config-html": "^1.1.0", + "stylelint-config-prettier": "^9.0.4", + "stylelint-config-recommended": "^9.0.0", + "stylelint-config-standard": "^29.0.0", + "stylelint-order": "^6.0.1", + "terser": "^5.16.3", + "typescript": "4.9.5", + "unplugin-auto-import": "^0.14.2", + "unplugin-element-plus": "^0.6.0", + "unplugin-vue-components": "^0.23.0", + "vite": "4.1.1", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-ejs": "^1.6.4", + "vite-plugin-eslint": "^1.8.1", + "vite-plugin-progress": "^0.0.6", + "vite-plugin-purge-icons": "^0.9.2", + "vite-plugin-svg-icons": "^2.0.1", + "vite-plugin-vue-setup-extend": "^0.4.0", + "vite-plugin-windicss": "^1.8.10", + "vue-tsc": "^1.0.24", + "windicss": "^3.5.6" + }, + "engines": { + "node": ">=16.0.0" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://gitee.com/zhijiantianya/ruoyi-vue-pro" + }, + "bugs": { + "url": "https://gitee.com/zhijiantianya/ruoyi-vue-pro/issues" + }, + "homepage": "https://gitee.com/zhijiantianya/ruoyi-vue-pro" +} 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..b014bbf1 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,22 @@ +module.exports = { + printWidth: 100, // 每行代码长度(默认80) + tabWidth: 2, // 每个tab相当于多少个空格(默认2)ab进行缩进(默认false) + useTabs: false, // 是否使用tab + semi: false, // 声明结尾使用分号(默认true) + vueIndentScriptAndStyle: false, + singleQuote: true, // 使用单引号(默认false) + quoteProps: 'as-needed', + bracketSpacing: true, // 对象字面量的大括号间使用空格(默认true) + trailingComma: 'none', // 多行使用拖尾逗号(默认none) + jsxSingleQuote: false, + // 箭头函数参数括号 默认avoid 可选 avoid| always + // avoid 能省略括号的时候就省略 例如x => x + // always 总是有括号 + arrowParens: 'always', + insertPragma: false, + requirePragma: false, + proseWrap: 'never', + htmlWhitespaceSensitivity: 'strict', + endOfLine: 'auto', + rangeStart: 0 +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5a7de08267811feb034bb0c54ab38ca417731aab GIT binary patch literal 4286 zcmcJTdr*|u8OGm?f3Tad7j|KHxq~xpVq;A@HfgF7QY~O)Go9%q6O&2POq*#slbPnf zwzf$$rcG^ZYDE+@N@~3q7I49+L1DRyKt#F7{U!nn%U;+^j39aYobU7VmKqZ!%ltTJ zSayHU`<%1G3`F1^k7e(&;Z`VJu(?k;=T7X%N)jqMq!dMyZeDC3iF!z6rw+67kxV&jDKh*=XMMH7BzC%0ZZgW^F*GR;m`LJ8Nxh;MnV_2 zo#Aeu7~L8}V>!_RD+rBki)NV^cA^7!L1e&FY)JDI!(TGDXW-mnR@`bh?a0EnGI=P9 zy>>$Z2m|CjGIt$Z+!8}0*)asCbu>FBggr}a!3yv^PwX`@<_>sE4e5ZqN9V>rmTRUF zbU(T+S`C+7@m92s-#UA`Fo4_`PSqdlNi_8Le1D!YO<=@vm4O z$%$c4iIKfW(ZR8ZbU?mO?%glC2lvBQG7OL4g2E>3CE|D(x!lD@#>|2OxWk0scT#t^ zS6+JEa8?X~2srB%*M&`SJoL{S_(0yf@}t;S27g&Z0Hpg)z+Z89e{|ZcvsAWtlnyU! zrooK~1ov;@oE;J_FsSek>;)U+*tyTdzLW6F6!^;ZVSuI_-@Jq7m6vJu4@{*G56tUW zpGZTSVx!<}kX-N$2;dyp7%$QPemsxlS=d(&zn~%_Fjb*XpK`!o{mnXPeP))mj8zZ^lWb$NF4{~3AH+q%ce-ErK%W3!iN7WAAnu?S-NPqStq)-K z^r;$jQ1|uDpfTeToz0w~uIE_Ym=?0w^$yVAEIfqUV+#(N3e zk2~QhGcbJDsny&3bvhLwfBl_1IF%8kGpjCBvvt~9zrr8d9#JR!m$M(Jd}05>Eb4nd zJ`&DTz3>To1b>z3MIY{C28N5L>h#j;Kcx$R41YcR4PV_s^-rhhbVi8kGeYX7r!U!S zQoWp`s}X+Qa=*iY4)hJLtt9w=1?Lsv?f#1(Q9@*K9WgR|GWA#mr*$@egH|Q{flqJW zfn&Lc>e2!ND{_P?Q{22wTP^vKVHzF$V&C_+Q16C#BfNFp?VUHBP>oj2B zhz=UjK_mQU{=dKMNf%Wv_mX3|SB)O*b?E`FSmwIXFFL5s)z(Cu`t>S;|CO%wi8Gz> zcD{3+@uDaWG)hk4Wq*?l=C;AD68_*>&6hq4$5NbB{u8&riVPuS;Bxqt{>2%98FcW< z-UZpz{a%6r-kQ#JN!+2ZHi^e>j@Xfb z(FH3R_9nI5>KFImbdI(Z5j@?YYO33 z34gFFoDTl5j|!g{5?GNTRI+4*l?*4In&hHK`i1Yt`Ne(~9lZ9R?`G5acM|n*SGN8s znFXUbf8oK55?7tV%W!hALlXom0foB5=@9%tUGVsTz3@jvj0}t(u4OpB%*i{yd6`D*|GZP1pkYzYvxb4teMYa#Zj_Lo>g|rUJiD`FX%!C-Qg-g2Nw4TGLa#a|M&na z8A`8a_#|~g>R_v;>eddvKnJhy{!TWvy`7|kyQ1Zf^VxYMQp~8#D(_{d@N%#l^n?dP zJ>eOEc0bbbz}_Ep9p1mVhmnEN1LG{D9z=$tSMNjqf-V*rZoG@=pcx%l%Ab3d&aIih z8eE{+mp&i+1G+xk}WiODJh<6N)JDmfKzAKZd|2|w0ncYeRMI&V=sBLj;JI14`X znA}4Vday6K$fZk1`Dluh#aSF*>g2+Oy}Ud24Cm@=8E1fdP!-j*CXKqY;$HSP>1S?o zig|DLDt@t+^XG}oKAp^97_-1JPiEbH%mN~Xwb-w{LX;}_oG4M?Bhm>5iNXX9qIrUK L_}7DgzT)vecywrn literal 0 HcmV?d00001 diff --git a/public/logo.gif b/public/logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..fdbd32c675f85af4ed57021ac0638a21a3c6cad3 GIT binary patch literal 6334 zcmd_tc|cQFz6bD|n?QDwkc0#TNdjn8Kv1-Z)+S*KSS<=wq-p_?L8l8^YSlNjA@?SP zkU&^uQ)0pzWRX=+DJJYFYCuFlTiIk2D>&L}rxG$$9JAR-LhrN_UcMBnKUyqQ#v!fFg@4U*x23O{ju@-hnE_2a&q=w?aX>G z9^QLHCX=l_{JCF()HOP)p`pPqJF}#uB)s=#adGkSKQkaeQ8BY2J7DJPv=pSTT@#wL5EgaDPSFKy6!cR#tDdAr+RZ?hUgS@!Nbug1z}c zzH40FkO)%@>=!6A6o3GnP2Z-Ff&c)$C<4R~ERiP266Hw>WpautH7z~kRAyFoPVVV5 zdFrzoZGJ&vQE`c`^jukaMdkUb>YCcR`f?iaLK>4q7h8iM4tptuU?2e$1Bh==?g5>c zz1=sHNWCt^Uh6waj2;5^ngMq|3Ex8na6K2NW!O#wQE#}hAS3ro%^KRC+Wov75MZO5 zQ8xL~)reX=4!x$8xB6or!9CT>{jt?Sl9R0LFKHp-eIBWHwRxL{C|y_6ZS4+LQC5v# zi$TzL8%KG_a#WlrhRF9oCFHY@WQ1mb9irtP;a|&^MTu?`qk9?`^zdE|-NHQRAZP4U zYmA?*BF4MzniXf;B1*)oqZ$BIAV$B#S{aXx_8G7{BaLBGU+{ z1Mhb9s%SMlq#>DB|2%RV*dA}j$IkYo8Ykjh#0vZ|CTpTF(WBk!?s_q2PDC~c-H`bY z=eAf%E5dxkQU0gCy2Si`Lfdo#?LoEjyl~*p(X!c{1t0!W~?rlKZ z2gRxc8xjV?4IzMbEotrdtS8&uTr>`OyJSoZf72|Xw#$8dIz63(^@yDmesW=E@^oy+ zim5Yk0|%$_j!l+MssHq2j*#`HRw%&P8n6T$0q*Nsp-i;`0N7NkL}>yFAd{umPS~U@ zg%YNkIz!0J&R0rV5LFG5O#LE)VjQYeTb@)1;HywNwbO;{Lb@7NrpD{D3bAc0oSM*m zRauJfQrGv%ay##UL&K2Nr52=$&yF@s)ppg+&1SXL5*HvQm2mli_=g`Ur8IX8FYq+y660Ir(VErRi_H8Cy`>#K$H`J31^2DoMQDnDf=ZQI zSY)yWBiQXNyOOS-9MQGtc>wb8CxjY}l{p?*d5;s;9vEn~{28ifFkU1z%Yc8`d*sQ} zEii`1)2Ipr<3%yqxMNP?s9vz^qX$^B%QT$me3_|n?b$zp=i3Tm-Po9}Ili@J4#r(0 zJja2&3WY{a>*w!(m|<}&qOb!auz%zp%lh^Z2kREO>EpJ-%TIkd9jk`jM4}guxAw2% z9Dezdh4uJ#7~<$QvYG1kVBUYp6~^8CY(*?+jwHZka z(u*pA3N^0`Us<)#up`D;)d3#MTz?liOipVqIdZMQgS$SKG#E zLRbcC?aE=x$jNDVi!A{xyQ|8ND3FWgBOwlYRN^;7*-g5%{gP|o(lc~eZONvY_99Ox>ieYh{1rDQ4rdodoMu~@XMd?{W`0E)n(f-Ha_#$m!xz1!c2i1${WwaKop6HMgb=z-;> zi2V->Y8FvBBZsL5^Rrz`Jsj&4*?H&2f8o}K(RBk(t_EkS$GViw|lW~OA((#4A zz`4EgR~_%MpG9cwH*Mn^+30}W!ww;UjzwB6L{%;mwMmIPr_SF`VUgb)GbG?l1?Ye+ zu*8(vhzR-Weue-UAWc#vBq9^#N>g5&DXNt8B!9dZR1t}3r(xP=2&Acz*+md>vqqc= zm7h0dg{&zhpGQ(JDT(JxT9fep=gF5Y!<)&4N?OZJnB;#;as3W7bXP&_l5QT>n{t&E zeRp~)sd_UJ-?iNKxy8>GwtunU--^P9s(wg!WkY(VTWkv?J$M6iVo6nmJYk7X(nYi;&dK@iObK2bqM;I6yo_B2!wFhdO5T4vINJVZ&#*NR!JCLnOEfW^DLOfH1;NI zE{YjL!^z*lb=_r%?X5E6XbNLC0ryCbon5l_`HSa)9xpjbiee&Xp+KWTV0QI9-@YHE zJdSXFSM<(wrw9lZ9vkXAV1}1va)Qb|O?Mn;FRBWO;e~U)Jk{bG1hg;;P*csQpdGq> z++fR_?3M>}X-4k`I497fC>T0$KA96Z56pU&!Zw=#ZTQSZyHNi#c+`K>j07-%rLTA4 z;cvSjW=gQqBq^4R2SI$YDhYRC(dhEkca#FzrR1)TshG)JBxMw!L=JZ zvD5aM1@d|DGh3wFB;0^G?&Z!iCa&7kSgu+4Tvg_alY54;MO~SzcJA?@JylW8g%72f z-7D7bsRLRsn335K*=0w6i0g@*gYoZsdmy-%c4I_V&P}#O6Du%yS3k>;ee-P@X9Afv z^7Zcq_V*^>8Jl?`o(J!(A*8EI5#=IQ&&+roE#{OQM4tKB!cmq;p(T31Cr z=m7YhI1O(>(D9z2B9kIm(=JK?_khRvoH6#Qcu1wGvQ!XLBbS<)p_Y%>mAN~!>#jvV zf)&RBo_IeBn7nax^5oLX(u!2bFIZ6@xIARVt-L@gN;13B+x6gke>iwGOi15ZX~OgAU>y1V%oC|v=dDW zl8TB-01^nf5G$>#vujHj5CKck;+k@sfr1Pw8mA?8%DYPvjV{`PKDfW6G@}Er&AN;J zPB)f8cG9Lkn9`+Z;IUf$<0-Gveq-cKIhg@?Gt>p_Ri%Uo7czVNU z_L!|9rw$I(MQPSw@*4bOruh=aA1JgL`8|KYD$diWvgCnYwnwCc)?(yM@`ga?C#PQ9 z{802SCY#^-&E|zUrMp+!5;T22K7mO?_;y?H16V>2$TduA_aq-1tMm3Ri#kKh4sSJmBwQc#z*pme1x5G zPpB$%XC0s8&CMUJ7%R1^P}X6;jHD1K&U?DN?q8CkS7f|@j62a^Dt`fJ&}v|{ZICzj zT|(d4;19AHn$OIRkOL_=uLmMa;9%5uh)vm|mXmsSU6>SRAvo-p0X$}+MCQ(uyqwXG zaXxjP&OmP(TsMx8o~njW4#Ihmx-mKuGTZX$@BaE{!j`@gwoD3$e;0P5GMOTNW9-6Q zz+`L~d=2WH#;Gwom##roXz+%tTx>fFry*RurYysEYZ`7T61oP# z;kzbc*PDo~9lJPP-(COcVOD!R@yRPs2|p>8bz_P@tYjWTKEQH_cDBy^s#XCmvD#?$ zQomgi9}k$14Kkx5TnJV`NRw~>)l(VH+vc!=)kp*pzY_cM@`U4T7KS1c@!0-moxXi; z^yh(kOuktv^?(oRP@RUltY$t^HSCs>wi)zx*frWFCDnkyH6y5PO8ZU;uKGHJ#TzBo{g4Z85W-m}_FLaE8GHP?K7}%C zGB)`>Onq!J_SSLQQwjMe+k?}_2pOK=;hJYzNi+w-?LnI zNQLR}<>9_`J*qIer}+l+nMXHUCV>f^doOI`v5E~kJaojCOWQ4K78q^XuD;H(oCY7H=X(|Oq~1SX`!E*Kn`PGfRR-W zCL-)RlzPkrI}2&~(TuiwC#-woGppMXpRToRw%$MFuYXXnp8T5$Ffo#yZf7G+R@;@0BhXe!y5NHtZ~1?`s^!LGhVy8<*lo6CRh6# zZkSx%r|21g28R{IPAP8aN=-ULR&?+5WKuPbi0@o(`;)~B3)`P9_IW_6<^i1@$3dh!1f)?P1HzJ_(`Z?K-o-rfo&{|0NW z3Dy?EdqaZg-(cd$1x^3ScIXcrz@HGirP$xd5 zHNUoxLL39<-VNIkYWI&^|f| zhjp3F$3==X)Lo6SyZPhyS-X;GwA3mAaN1>8d1EXW!*K{n^>M;Cx`@Cpdf(3^umtAp zutHsh2WsstagO8=7*vPpUJry-%^&e7SJBamP0y`iGsZS=#-;#z=nhCz9Q@w9b`#UB zV!SRB-2iGS6n@^tTJeRQ!K^{I(dsHB`^g|fF21okRt{$I^+gCPlXi@`O*lcwl tSeX}8JPlhsJH^s%`^fr8K)RRk_D-Z(;fYh+bq~SM_Ebo|rD1@Le+4VAC8PiV literal 0 HcmV?d00001 diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 00000000..a7867a1c --- /dev/null +++ b/src/App.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/api/bpm/activity/index.ts b/src/api/bpm/activity/index.ts new file mode 100644 index 00000000..870d0d6c --- /dev/null +++ b/src/api/bpm/activity/index.ts @@ -0,0 +1,8 @@ +import request from '@/config/axios' + +export const getActivityList = async (params) => { + return await request.get({ + url: '/bpm/activity/list', + params + }) +} diff --git a/src/api/bpm/definition/index.ts b/src/api/bpm/definition/index.ts new file mode 100644 index 00000000..477d6729 --- /dev/null +++ b/src/api/bpm/definition/index.ts @@ -0,0 +1,21 @@ +import request from '@/config/axios' + +export const getProcessDefinitionBpmnXMLApi = async (id: number) => { + return await request.get({ + url: '/bpm/process-definition/get-bpmn-xml?id=' + id + }) +} + +export const getProcessDefinitionPageApi = async (params) => { + return await request.get({ + url: '/bpm/process-definition/page', + params + }) +} + +export const getProcessDefinitionListApi = async (params) => { + return await request.get({ + url: '/bpm/process-definition/list', + params + }) +} diff --git a/src/api/bpm/form/index.ts b/src/api/bpm/form/index.ts new file mode 100644 index 00000000..c745201f --- /dev/null +++ b/src/api/bpm/form/index.ts @@ -0,0 +1,56 @@ +import request from '@/config/axios' + +export type FormVO = { + id: number + name: string + conf: string + fields: string[] + status: number + remark: string + createTime: string +} + +// 创建工作流的表单定义 +export const createFormApi = async (data: FormVO) => { + return await request.post({ + url: '/bpm/form/create', + data: data + }) +} + +// 更新工作流的表单定义 +export const updateFormApi = async (data: FormVO) => { + return await request.put({ + url: '/bpm/form/update', + data: data + }) +} + +// 删除工作流的表单定义 +export const deleteFormApi = async (id: number) => { + return await request.delete({ + url: '/bpm/form/delete?id=' + id + }) +} + +// 获得工作流的表单定义 +export const getFormApi = async (id: number) => { + return await request.get({ + url: '/bpm/form/get?id=' + id + }) +} + +// 获得工作流的表单定义分页 +export const getFormPageApi = async (params) => { + return await request.get({ + url: '/bpm/form/page', + params + }) +} + +// 获得动态表单的精简列表 +export const getSimpleFormsApi = async () => { + return await request.get({ + url: '/bpm/form/list-all-simple' + }) +} diff --git a/src/api/bpm/leave/index.ts b/src/api/bpm/leave/index.ts new file mode 100644 index 00000000..ff6d86a0 --- /dev/null +++ b/src/api/bpm/leave/index.ts @@ -0,0 +1,27 @@ +import request from '@/config/axios' + +export type LeaveVO = { + id: number + result: number + type: number + reason: string + processInstanceId: string + startTime: string + endTime: string + createTime: string +} + +// 创建请假申请 +export const createLeaveApi = async (data: LeaveVO) => { + return await request.post({ url: '/bpm/oa/leave/create', data: data }) +} + +// 获得请假申请 +export const getLeaveApi = async (id: number) => { + return await request.get({ url: '/bpm/oa/leave/get?id=' + id }) +} + +// 获得请假申请分页 +export const getLeavePageApi = async (params) => { + return await request.get({ url: '/bpm/oa/leave/page', params }) +} diff --git a/src/api/bpm/model/index.ts b/src/api/bpm/model/index.ts new file mode 100644 index 00000000..0335a3db --- /dev/null +++ b/src/api/bpm/model/index.ts @@ -0,0 +1,59 @@ +import request from '@/config/axios' + +export type ProcessDefinitionVO = { + id: string + version: number + deploymentTIme: string + suspensionState: number +} + +export type ModelVO = { + id: number + formName: string + key: string + name: string + description: string + category: string + formType: number + formId: number + formCustomCreatePath: string + formCustomViewPath: string + processDefinition: ProcessDefinitionVO + status: number + remark: string + createTime: string + bpmnXml: string +} + +export const getModelPageApi = async (params) => { + return await request.get({ url: '/bpm/model/page', params }) +} + +export const getModelApi = async (id: number) => { + return await request.get({ url: '/bpm/model/get?id=' + id }) +} + +export const updateModelApi = async (data: ModelVO) => { + return await request.put({ url: '/bpm/model/update', data: data }) +} + +// 任务状态修改 +export const updateModelStateApi = async (id: number, state: number) => { + const data = { + id: id, + state: state + } + return await request.put({ url: '/bpm/model/update-state', data: data }) +} + +export const createModelApi = async (data: ModelVO) => { + return await request.post({ url: '/bpm/model/create', data: data }) +} + +export const deleteModelApi = async (id: number) => { + return await request.delete({ url: '/bpm/model/delete?id=' + id }) +} + +export const deployModelApi = async (id: number) => { + return await request.post({ url: '/bpm/model/deploy?id=' + id }) +} diff --git a/src/api/bpm/processInstance/index.ts b/src/api/bpm/processInstance/index.ts new file mode 100644 index 00000000..d48253c3 --- /dev/null +++ b/src/api/bpm/processInstance/index.ts @@ -0,0 +1,40 @@ +import request from '@/config/axios' + +export type Task = { + id: string + name: string +} +export type ProcessInstanceVO = { + id: number + name: string + processDefinitionId: string + category: string + result: number + tasks: Task[] + fields: string[] + status: number + remark: string + businessKey: string + createTime: string + endTime: string +} + +export const getMyProcessInstancePageApi = async (params) => { + return await request.get({ url: '/bpm/process-instance/my-page', params }) +} + +export const createProcessInstanceApi = async (data) => { + return await request.post({ url: '/bpm/process-instance/create', data: data }) +} + +export const cancelProcessInstanceApi = async (id: number, reason: string) => { + const data = { + id: id, + reason: reason + } + return await request.delete({ url: '/bpm/process-instance/cancel', data: data }) +} + +export const getProcessInstanceApi = async (id: number) => { + return await request.get({ url: '/bpm/process-instance/get?id=' + id }) +} diff --git a/src/api/bpm/task/index.ts b/src/api/bpm/task/index.ts new file mode 100644 index 00000000..d214d175 --- /dev/null +++ b/src/api/bpm/task/index.ts @@ -0,0 +1,34 @@ +import request from '@/config/axios' + +export const getTodoTaskPage = async (params) => { + return await request.get({ url: '/bpm/task/todo-page', params }) +} + +export const getDoneTaskPage = async (params) => { + return await request.get({ url: '/bpm/task/done-page', params }) +} + +export const completeTask = async (data) => { + return await request.put({ url: '/bpm/task/complete', data }) +} + +export const approveTask = async (data) => { + return await request.put({ url: '/bpm/task/approve', data }) +} + +export const rejectTask = async (data) => { + return await request.put({ url: '/bpm/task/reject', data }) +} +export const backTask = async (data) => { + return await request.put({ url: '/bpm/task/back', data }) +} + +export const updateTaskAssignee = async (data) => { + return await request.put({ url: '/bpm/task/update-assignee', data }) +} + +export const getTaskListByProcessInstanceId = async (processInstanceId) => { + return await request.get({ + url: '/bpm/task/list-by-process-instance-id?processInstanceId=' + processInstanceId + }) +} diff --git a/src/api/bpm/taskAssignRule/index.ts b/src/api/bpm/taskAssignRule/index.ts new file mode 100644 index 00000000..5fbe342d --- /dev/null +++ b/src/api/bpm/taskAssignRule/index.ts @@ -0,0 +1,29 @@ +import request from '@/config/axios' + +export type TaskAssignVO = { + id: number + modelId: string + processDefinitionId: string + taskDefinitionKey: string + taskDefinitionName: string + options: string[] + type: number +} + +export const getTaskAssignRuleList = async (params) => { + return await request.get({ url: '/bpm/task-assign-rule/list', params }) +} + +export const createTaskAssignRule = async (data: TaskAssignVO) => { + return await request.post({ + url: '/bpm/task-assign-rule/create', + data: data + }) +} + +export const updateTaskAssignRule = async (data: TaskAssignVO) => { + return await request.put({ + url: '/bpm/task-assign-rule/update', + data: data + }) +} diff --git a/src/api/bpm/userGroup/index.ts b/src/api/bpm/userGroup/index.ts new file mode 100644 index 00000000..88ee9619 --- /dev/null +++ b/src/api/bpm/userGroup/index.ts @@ -0,0 +1,47 @@ +import request from '@/config/axios' + +export type UserGroupVO = { + id: number + name: string + description: string + memberUserIds: number[] + status: number + remark: string + createTime: string +} + +// 创建用户组 +export const createUserGroupApi = async (data: UserGroupVO) => { + return await request.post({ + url: '/bpm/user-group/create', + data: data + }) +} + +// 更新用户组 +export const updateUserGroupApi = async (data: UserGroupVO) => { + return await request.put({ + url: '/bpm/user-group/update', + data: data + }) +} + +// 删除用户组 +export const deleteUserGroupApi = async (id: number) => { + return await request.delete({ url: '/bpm/user-group/delete?id=' + id }) +} + +// 获得用户组 +export const getUserGroupApi = async (id: number) => { + return await request.get({ url: '/bpm/user-group/get?id=' + id }) +} + +// 获得用户组分页 +export const getUserGroupPageApi = async (params) => { + return await request.get({ url: '/bpm/user-group/page', params }) +} + +// 获取用户组精简信息列表 +export const listSimpleUserGroupsApi = async () => { + return await request.get({ url: '/bpm/user-group/list-all-simple' }) +} diff --git a/src/api/infra/apiAccessLog/index.ts b/src/api/infra/apiAccessLog/index.ts new file mode 100644 index 00000000..b46199e4 --- /dev/null +++ b/src/api/infra/apiAccessLog/index.ts @@ -0,0 +1,50 @@ +import request from '@/config/axios' + +export interface ApiAccessLogVO { + id: number + traceId: string + userId: number + userType: number + applicationName: string + requestMethod: string + requestParams: string + requestUrl: string + userIp: string + userAgent: string + beginTime: Date + endTIme: Date + duration: number + resultCode: number + resultMsg: string + createTime: Date +} + +export interface ApiAccessLogPageReqVO extends PageParam { + userId?: number + userType?: number + applicationName?: string + requestUrl?: string + beginTime?: Date[] + duration?: number + resultCode?: number +} + +export interface ApiAccessLogExportReqVO { + userId?: number + userType?: number + applicationName?: string + requestUrl?: string + beginTime?: Date[] + duration?: number + resultCode?: number +} + +// 查询列表API 访问日志 +export const getApiAccessLogPageApi = (params: ApiAccessLogPageReqVO) => { + return request.get({ url: '/infra/api-access-log/page', params }) +} + +// 导出API 访问日志 +export const exportApiAccessLogApi = (params: ApiAccessLogExportReqVO) => { + return request.download({ url: '/infra/api-access-log/export-excel', params }) +} diff --git a/src/api/infra/apiErrorLog/index.ts b/src/api/infra/apiErrorLog/index.ts new file mode 100644 index 00000000..06515c36 --- /dev/null +++ b/src/api/infra/apiErrorLog/index.ts @@ -0,0 +1,66 @@ +import request from '@/config/axios' + +export interface ApiErrorLogVO { + id: number + traceId: string + userId: number + userType: number + applicationName: string + requestMethod: string + requestParams: string + requestUrl: string + userIp: string + userAgent: string + exceptionTime: Date + exceptionName: string + exceptionMessage: string + exceptionRootCauseMessage: string + exceptionStackTrace: string + exceptionClassName: string + exceptionFileName: string + exceptionMethodName: string + exceptionLineNumber: number + processUserId: number + processStatus: number + processTime: Date + resultCode: number + createTime: Date +} + +export interface ApiErrorLogPageReqVO extends PageParam { + userId?: number + userType?: number + applicationName?: string + requestUrl?: string + exceptionTime?: Date[] + processStatus: number +} + +export interface ApiErrorLogExportReqVO { + userId?: number + userType?: number + applicationName?: string + requestUrl?: string + exceptionTime?: Date[] + processStatus: number +} + +// 查询列表API 访问日志 +export const getApiErrorLogPageApi = (params: ApiErrorLogPageReqVO) => { + return request.get({ url: '/infra/api-error-log/page', params }) +} + +// 更新 API 错误日志的处理状态 +export const updateApiErrorLogPageApi = (id: number, processStatus: number) => { + return request.put({ + url: '/infra/api-error-log/update-status?id=' + id + '&processStatus=' + processStatus + }) +} + +// 导出API 访问日志 +export const exportApiErrorLogApi = (params: ApiErrorLogExportReqVO) => { + return request.download({ + url: '/infra/api-error-log/export-excel', + params + }) +} diff --git a/src/api/infra/codegen/index.ts b/src/api/infra/codegen/index.ts new file mode 100644 index 00000000..54f00ff0 --- /dev/null +++ b/src/api/infra/codegen/index.ts @@ -0,0 +1,57 @@ +import request from '@/config/axios' +import type { CodegenUpdateReqVO, CodegenCreateListReqVO } from './types' + +// 查询列表代码生成表定义 +export const getCodegenTablePageApi = (params) => { + return request.get({ url: '/infra/codegen/table/page', params }) +} + +// 查询详情代码生成表定义 +export const getCodegenTableApi = (id: number) => { + return request.get({ url: '/infra/codegen/detail?tableId=' + id }) +} + +// 新增代码生成表定义 +export const createCodegenTableApi = (data: CodegenCreateListReqVO) => { + return request.post({ url: '/infra/codegen/create', data }) +} + +// 修改代码生成表定义 +export const updateCodegenTableApi = (data: CodegenUpdateReqVO) => { + return request.put({ url: '/infra/codegen/update', data }) +} + +// 基于数据库的表结构,同步数据库的表和字段定义 +export const syncCodegenFromDBApi = (id: number) => { + return request.put({ url: '/infra/codegen/sync-from-db?tableId=' + id }) +} + +// 基于 SQL 建表语句,同步数据库的表和字段定义 +export const syncCodegenFromSQLApi = (id: number, sql: string) => { + return request.put({ url: '/infra/codegen/sync-from-sql?tableId=' + id + '&sql=' + sql }) +} + +// 预览生成代码 +export const previewCodegenApi = (id: number) => { + return request.get({ url: '/infra/codegen/preview?tableId=' + id }) +} + +// 下载生成代码 +export const downloadCodegenApi = (id: number) => { + return request.download({ url: '/infra/codegen/download?tableId=' + id }) +} + +// 获得表定义 +export const getSchemaTableListApi = (params) => { + return request.get({ url: '/infra/codegen/db/table/list', params }) +} + +// 基于数据库的表结构,创建代码生成器的表定义 +export const createCodegenListApi = (data) => { + return request.post({ url: '/infra/codegen/create-list', data }) +} + +// 删除代码生成表定义 +export const deleteCodegenTableApi = (id: number) => { + return request.delete({ url: '/infra/codegen/delete?tableId=' + id }) +} diff --git a/src/api/infra/codegen/types.ts b/src/api/infra/codegen/types.ts new file mode 100644 index 00000000..be6a66ed --- /dev/null +++ b/src/api/infra/codegen/types.ts @@ -0,0 +1,61 @@ +export type CodegenTableVO = { + id: number + tableId: number + isParentMenuIdValid: boolean + dataSourceConfigId: number + scene: number + tableName: string + tableComment: string + remark: string + moduleName: string + businessName: string + className: string + classComment: string + author: string + createTime: Date + updateTime: Date + templateType: number + parentMenuId: number +} + +export type CodegenColumnVO = { + id: number + tableId: number + columnName: string + dataType: string + columnComment: string + nullable: number + primaryKey: number + autoIncrement: string + ordinalPosition: number + javaType: string + javaField: string + dictType: string + example: string + createOperation: number + updateOperation: number + listOperation: number + listOperationCondition: string + listOperationResult: number + htmlType: string +} +export type DatabaseTableVO = { + name: string + comment: string +} +export type CodegenDetailVO = { + table: CodegenTableVO + columns: CodegenColumnVO[] +} +export type CodegenPreviewVO = { + filePath: string + code: string +} +export type CodegenUpdateReqVO = { + table: CodegenTableVO + columns: CodegenColumnVO[] +} +export type CodegenCreateListReqVO = { + dataSourceConfigId: number + tableNames: string[] +} diff --git a/src/api/infra/config/index.ts b/src/api/infra/config/index.ts new file mode 100644 index 00000000..5a9bee1b --- /dev/null +++ b/src/api/infra/config/index.ts @@ -0,0 +1,62 @@ +import request from '@/config/axios' + +export interface ConfigVO { + id: number + category: string + name: string + key: string + value: string + type: number + visible: boolean + remark: string + createTime: Date +} + +export interface ConfigPageReqVO extends PageParam { + name?: string + key?: string + type?: number + createTime?: Date[] +} + +export interface ConfigExportReqVO { + name?: string + key?: string + type?: number + createTime?: Date[] +} + +// 查询参数列表 +export const getConfigPageApi = (params: ConfigPageReqVO) => { + return request.get({ url: '/infra/config/page', params }) +} + +// 查询参数详情 +export const getConfigApi = (id: number) => { + return request.get({ url: '/infra/config/get?id=' + id }) +} + +// 根据参数键名查询参数值 +export const getConfigKeyApi = (configKey: string) => { + return request.get({ url: '/infra/config/get-value-by-key?key=' + configKey }) +} + +// 新增参数 +export const createConfigApi = (data: ConfigVO) => { + return request.post({ url: '/infra/config/create', data }) +} + +// 修改参数 +export const updateConfigApi = (data: ConfigVO) => { + return request.put({ url: '/infra/config/update', data }) +} + +// 删除参数 +export const deleteConfigApi = (id: number) => { + return request.delete({ url: '/infra/config/delete?id=' + id }) +} + +// 导出参数 +export const exportConfigApi = (params: ConfigExportReqVO) => { + return request.download({ url: '/infra/config/export', params }) +} diff --git a/src/api/infra/dataSourceConfig/index.ts b/src/api/infra/dataSourceConfig/index.ts new file mode 100644 index 00000000..c0f9f624 --- /dev/null +++ b/src/api/infra/dataSourceConfig/index.ts @@ -0,0 +1,35 @@ +import request from '@/config/axios' + +export interface DataSourceConfigVO { + id: number + name: string + url: string + username: string + password: string + createTime: Date +} + +// 查询数据源配置列表 +export const getDataSourceConfigListApi = () => { + return request.get({ url: '/infra/data-source-config/list' }) +} + +// 查询数据源配置详情 +export const getDataSourceConfigApi = (id: number) => { + return request.get({ url: '/infra/data-source-config/get?id=' + id }) +} + +// 新增数据源配置 +export const createDataSourceConfigApi = (data: DataSourceConfigVO) => { + return request.post({ url: '/infra/data-source-config/create', data }) +} + +// 修改数据源配置 +export const updateDataSourceConfigApi = (data: DataSourceConfigVO) => { + return request.put({ url: '/infra/data-source-config/update', data }) +} + +// 删除数据源配置 +export const deleteDataSourceConfigApi = (id: number) => { + return request.delete({ url: '/infra/data-source-config/delete?id=' + id }) +} diff --git a/src/api/infra/dbDoc/index.ts b/src/api/infra/dbDoc/index.ts new file mode 100644 index 00000000..11bce473 --- /dev/null +++ b/src/api/infra/dbDoc/index.ts @@ -0,0 +1,16 @@ +import request from '@/config/axios' + +// 导出Html +export const exportHtmlApi = () => { + return request.download({ url: '/infra/db-doc/export-html' }) +} + +// 导出Word +export const exportWordApi = () => { + return request.download({ url: '/infra/db-doc/export-word' }) +} + +// 导出Markdown +export const exportMarkdownApi = () => { + return request.download({ url: '/infra/db-doc/export-markdown' }) +} diff --git a/src/api/infra/fileConfig/index.ts b/src/api/infra/fileConfig/index.ts new file mode 100644 index 00000000..2151141c --- /dev/null +++ b/src/api/infra/fileConfig/index.ts @@ -0,0 +1,66 @@ +import request from '@/config/axios' + +export interface FileClientConfig { + basePath: string + host?: string + port?: number + username?: string + password?: string + mode?: string + endpoint?: string + bucket?: string + accessKey?: string + accessSecret?: string + domain: string +} +export interface FileConfigVO { + id: number + name: string + storage: number + master: boolean + visible: boolean + config: FileClientConfig + remark: string + createTime: Date +} + +export interface FileConfigPageReqVO extends PageParam { + name?: string + storage?: number + createTime?: Date[] +} + +// 查询文件配置列表 +export const getFileConfigPageApi = (params: FileConfigPageReqVO) => { + return request.get({ url: '/infra/file-config/page', params }) +} + +// 查询文件配置详情 +export const getFileConfigApi = (id: number) => { + return request.get({ url: '/infra/file-config/get?id=' + id }) +} + +// 更新文件配置为主配置 +export const updateFileConfigMasterApi = (id: number) => { + return request.put({ url: '/infra/file-config/update-master?id=' + id }) +} + +// 新增文件配置 +export const createFileConfigApi = (data: FileConfigVO) => { + return request.post({ url: '/infra/file-config/create', data }) +} + +// 修改文件配置 +export const updateFileConfigApi = (data: FileConfigVO) => { + return request.put({ url: '/infra/file-config/update', data }) +} + +// 删除文件配置 +export const deleteFileConfigApi = (id: number) => { + return request.delete({ url: '/infra/file-config/delete?id=' + id }) +} + +// 测试文件配置 +export const testFileConfigApi = (id: number) => { + return request.get({ url: '/infra/file-config/test?id=' + id }) +} diff --git a/src/api/infra/fileList/index.ts b/src/api/infra/fileList/index.ts new file mode 100644 index 00000000..48b3c991 --- /dev/null +++ b/src/api/infra/fileList/index.ts @@ -0,0 +1,28 @@ +import request from '@/config/axios' + +export interface FileVO { + id: number + configId: number + path: string + name: string + url: string + size: string + type: string + createTime: Date +} + +export interface FilePageReqVO extends PageParam { + path?: string + type?: string + createTime?: Date[] +} + +// 查询文件列表 +export const getFilePageApi = (params: FilePageReqVO) => { + return request.get({ url: '/infra/file/page', params }) +} + +// 删除文件 +export const deleteFileApi = (id: number) => { + return request.delete({ url: '/infra/file/delete?id=' + id }) +} diff --git a/src/api/infra/job/index.ts b/src/api/infra/job/index.ts new file mode 100644 index 00000000..63f15da0 --- /dev/null +++ b/src/api/infra/job/index.ts @@ -0,0 +1,75 @@ +import request from '@/config/axios' + +export interface JobVO { + id: number + name: string + status: number + handlerName: string + handlerParam: string + cronExpression: string + retryCount: number + retryInterval: number + monitorTimeout: number + createTime: Date +} + +export interface JobPageReqVO extends PageParam { + name?: string + status?: number + handlerName?: string +} + +export interface JobExportReqVO { + name?: string + status?: number + handlerName?: string +} + +// 任务列表 +export const getJobPageApi = (params: JobPageReqVO) => { + return request.get({ url: '/infra/job/page', params }) +} + +// 任务详情 +export const getJobApi = (id: number) => { + return request.get({ url: '/infra/job/get?id=' + id }) +} + +// 新增任务 +export const createJobApi = (data: JobVO) => { + return request.post({ url: '/infra/job/create', data }) +} + +// 修改定时任务调度 +export const updateJobApi = (data: JobVO) => { + return request.put({ url: '/infra/job/update', data }) +} + +// 删除定时任务调度 +export const deleteJobApi = (id: number) => { + return request.delete({ url: '/infra/job/delete?id=' + id }) +} + +// 导出定时任务调度 +export const exportJobApi = (params: JobExportReqVO) => { + return request.download({ url: '/infra/job/export-excel', params }) +} + +// 任务状态修改 +export const updateJobStatusApi = (id: number, status: number) => { + const params = { + id, + status + } + return request.put({ url: '/infra/job/update-status', params }) +} + +// 定时任务立即执行一次 +export const runJobApi = (id: number) => { + return request.put({ url: '/infra/job/trigger?id=' + id }) +} + +// 获得定时任务的下 n 次执行时间 +export const getJobNextTimesApi = (id: number) => { + return request.get({ url: '/infra/job/get_next_times?id=' + id }) +} diff --git a/src/api/infra/jobLog/index.ts b/src/api/infra/jobLog/index.ts new file mode 100644 index 00000000..84b74fbd --- /dev/null +++ b/src/api/infra/jobLog/index.ts @@ -0,0 +1,49 @@ +import request from '@/config/axios' + +export interface JobLogVO { + id: number + jobId: number + handlerName: string + handlerParam: string + cronExpression: string + executeIndex: string + beginTime: string + endTime: string + duration: string + status: number + createTime: string +} + +export interface JobLogPageReqVO extends PageParam { + jobId?: number + handlerName?: string + beginTime?: string + endTime?: string + status?: number +} + +export interface JobLogExportReqVO { + jobId?: number + handlerName?: string + beginTime?: string + endTime?: string + status?: number +} + +// 任务日志列表 +export const getJobLogPageApi = (params: JobLogPageReqVO) => { + return request.get({ url: '/infra/job-log/page', params }) +} + +// 任务日志详情 +export const getJobLogApi = (id: number) => { + return request.get({ url: '/infra/job-log/get?id=' + id }) +} + +// 导出定时任务日志 +export const exportJobLogApi = (params: JobLogExportReqVO) => { + return request.download({ + url: '/infra/job-log/export-excel', + params + }) +} diff --git a/src/api/infra/redis/index.ts b/src/api/infra/redis/index.ts new file mode 100644 index 00000000..1214a1da --- /dev/null +++ b/src/api/infra/redis/index.ts @@ -0,0 +1,41 @@ +import request from '@/config/axios' + +/** + * 获取redis 监控信息 + */ +export const getCacheApi = () => { + return request.get({ url: '/infra/redis/get-monitor-info' }) +} +// 获取模块 +export const getKeyDefineListApi = () => { + return request.get({ url: '/infra/redis/get-key-define-list' }) +} +/** + * 获取redis key列表 + */ +export const getKeyListApi = (keyTemplate: string) => { + return request.get({ + url: '/infra/redis/get-key-list', + params: { + keyTemplate + } + }) +} +// 获取缓存内容 +export const getKeyValueApi = (key: string) => { + return request.get({ url: '/infra/redis/get-key-value?key=' + key }) +} + +// 根据键名删除缓存 +export const deleteKeyApi = (key: string) => { + return request.delete({ url: '/infra/redis/delete-key?key=' + key }) +} + +export const deleteKeysApi = (keyTemplate: string) => { + return request.delete({ + url: '/infra/redis/delete-keys?', + params: { + keyTemplate + } + }) +} diff --git a/src/api/infra/redis/types.ts b/src/api/infra/redis/types.ts new file mode 100644 index 00000000..2342e543 --- /dev/null +++ b/src/api/infra/redis/types.ts @@ -0,0 +1,185 @@ +export interface RedisMonitorInfoVO { + info: RedisInfoVO + dbSize: number + commandStats: RedisCommandStatsVO[] +} + +export interface RedisInfoVO { + io_threaded_reads_processed: string + tracking_clients: string + uptime_in_seconds: string + cluster_connections: string + current_cow_size: string + maxmemory_human: string + aof_last_cow_size: string + master_replid2: string + mem_replication_backlog: string + aof_rewrite_scheduled: string + total_net_input_bytes: string + rss_overhead_ratio: string + hz: string + current_cow_size_age: string + redis_build_id: string + errorstat_BUSYGROUP: string + aof_last_bgrewrite_status: string + multiplexing_api: string + client_recent_max_output_buffer: string + allocator_resident: string + mem_fragmentation_bytes: string + aof_current_size: string + repl_backlog_first_byte_offset: string + tracking_total_prefixes: string + redis_mode: string + redis_git_dirty: string + aof_delayed_fsync: string + allocator_rss_bytes: string + repl_backlog_histlen: string + io_threads_active: string + rss_overhead_bytes: string + total_system_memory: string + loading: string + evicted_keys: string + maxclients: string + cluster_enabled: string + redis_version: string + repl_backlog_active: string + mem_aof_buffer: string + allocator_frag_bytes: string + io_threaded_writes_processed: string + instantaneous_ops_per_sec: string + used_memory_human: string + total_error_replies: string + role: string + maxmemory: string + used_memory_lua: string + rdb_current_bgsave_time_sec: string + used_memory_startup: string + used_cpu_sys_main_thread: string + lazyfree_pending_objects: string + aof_pending_bio_fsync: string + used_memory_dataset_perc: string + allocator_frag_ratio: string + arch_bits: string + used_cpu_user_main_thread: string + mem_clients_normal: string + expired_time_cap_reached_count: string + unexpected_error_replies: string + mem_fragmentation_ratio: string + aof_last_rewrite_time_sec: string + master_replid: string + aof_rewrite_in_progress: string + lru_clock: string + maxmemory_policy: string + run_id: string + latest_fork_usec: string + tracking_total_items: string + total_commands_processed: string + expired_keys: string + errorstat_ERR: string + used_memory: string + module_fork_in_progress: string + errorstat_WRONGPASS: string + aof_buffer_length: string + dump_payload_sanitizations: string + mem_clients_slaves: string + keyspace_misses: string + server_time_usec: string + executable: string + lazyfreed_objects: string + db0: string + used_memory_peak_human: string + keyspace_hits: string + rdb_last_cow_size: string + aof_pending_rewrite: string + used_memory_overhead: string + active_defrag_hits: string + tcp_port: string + uptime_in_days: string + used_memory_peak_perc: string + current_save_keys_processed: string + blocked_clients: string + total_reads_processed: string + expire_cycle_cpu_milliseconds: string + sync_partial_err: string + used_memory_scripts_human: string + aof_current_rewrite_time_sec: string + aof_enabled: string + process_supervised: string + master_repl_offset: string + used_memory_dataset: string + used_cpu_user: string + rdb_last_bgsave_status: string + tracking_total_keys: string + atomicvar_api: string + allocator_rss_ratio: string + client_recent_max_input_buffer: string + clients_in_timeout_table: string + aof_last_write_status: string + mem_allocator: string + used_memory_scripts: string + used_memory_peak: string + process_id: string + master_failover_state: string + errorstat_NOAUTH: string + used_cpu_sys: string + repl_backlog_size: string + connected_slaves: string + current_save_keys_total: string + gcc_version: string + total_system_memory_human: string + sync_full: string + connected_clients: string + module_fork_last_cow_size: string + total_writes_processed: string + allocator_active: string + total_net_output_bytes: string + pubsub_channels: string + current_fork_perc: string + active_defrag_key_hits: string + rdb_changes_since_last_save: string + instantaneous_input_kbps: string + used_memory_rss_human: string + configured_hz: string + expired_stale_perc: string + active_defrag_misses: string + used_cpu_sys_children: string + number_of_cached_scripts: string + sync_partial_ok: string + used_memory_lua_human: string + rdb_last_save_time: string + pubsub_patterns: string + slave_expires_tracked_keys: string + redis_git_sha1: string + used_memory_rss: string + rdb_last_bgsave_time_sec: string + os: string + mem_not_counted_for_evict: string + active_defrag_running: string + rejected_connections: string + aof_rewrite_buffer_length: string + total_forks: string + active_defrag_key_misses: string + allocator_allocated: string + aof_base_size: string + instantaneous_output_kbps: string + second_repl_offset: string + rdb_bgsave_in_progress: string + used_cpu_user_children: string + total_connections_received: string + migrate_cached_sockets: string +} + +export interface RedisCommandStatsVO { + command: string + calls: number + usec: number +} + +export interface RedisKeyInfo { + keyTemplate: string + keyType: string + valueType: string + timeoutType: number + timeout: number + memo: string +} diff --git a/src/api/login/index.ts b/src/api/login/index.ts new file mode 100644 index 00000000..0c895663 --- /dev/null +++ b/src/api/login/index.ts @@ -0,0 +1,73 @@ +import request from '@/config/axios' +import { getRefreshToken } from '@/utils/auth' +import type { UserLoginVO } from './types' + +export interface CodeImgResult { + captchaOnOff: boolean + img: string + uuid: string +} +export interface SmsCodeVO { + mobile: string + scene: number +} +export interface SmsLoginVO { + mobile: string + code: string +} + +// 登录 +export const loginApi = (data: UserLoginVO) => { + return request.post({ url: '/system/auth/login', data }) +} + +// 刷新访问令牌 +export const refreshToken = () => { + return request.post({ url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken() }) +} + +// 使用租户名,获得租户编号 +export const getTenantIdByNameApi = (name: string) => { + return request.get({ url: '/system/tenant/get-id-by-name?name=' + name }) +} + +// 登出 +export const loginOutApi = () => { + return request.post({ url: '/system/auth/logout' }) +} + +// 获取用户权限信息 +export const getInfoApi = () => { + return request.get({ url: '/system/auth/get-permission-info' }) +} + +// 路由 +export const getAsyncRoutesApi = () => { + return request.get({ url: '/system/auth/list-menus' }) +} + +//获取登录验证码 +export const sendSmsCodeApi = (data: SmsCodeVO) => { + return request.post({ url: '/system/auth/send-sms-code', data }) +} + +// 短信验证码登录 +export const smsLoginApi = (data: SmsLoginVO) => { + return request.post({ url: '/system/auth/sms-login', data }) +} + +// 社交授权的跳转 +export const socialAuthRedirectApi = (type: number, redirectUri: string) => { + return request.get({ + url: '/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri + }) +} +// 获取验证图片 以及token +export const getCodeApi = (data) => { + return request.postOriginal({ url: 'system/captcha/get', data }) +} + +// 滑动或者点选验证 +export const reqCheckApi = (data) => { + return request.postOriginal({ url: 'system/captcha/check', data }) +} diff --git a/src/api/login/types.ts b/src/api/login/types.ts new file mode 100644 index 00000000..1a91aecc --- /dev/null +++ b/src/api/login/types.ts @@ -0,0 +1,42 @@ +export type UserLoginVO = { + username: string + password: string + captchaVerification: string +} + +export type TokenType = { + id: number // 编号 + accessToken: string // 访问令牌 + refreshToken: string // 刷新令牌 + userId: number // 用户编号 + userType: number //用户类型 + clientId: string //客户端编号 + expiresTime: number //过期时间 +} + +export type UserVO = { + id: number + username: string + nickname: string + deptId: number + email: string + mobile: string + sex: number + avatar: string + loginIp: string + loginDate: string +} + +export type UserInfoVO = { + permissions: [] + roles: [] + user: { + avatar: string + id: number + nickname: string + } +} + +export type TentantNameVO = { + name: string +} diff --git a/src/api/mp/account/index.ts b/src/api/mp/account/index.ts new file mode 100644 index 00000000..cbdb1422 --- /dev/null +++ b/src/api/mp/account/index.ts @@ -0,0 +1,41 @@ +import request from '@/config/axios' + +// 创建公众号账号 +export const createAccount = async (data) => { + return await request.post({ url: '/mp/account/create', data }) +} + +// 更新公众号账号 +export const updateAccount = async (data) => { + return request.put({ url: '/mp/account/update', data: data }) +} + +// 删除公众号账号 +export const deleteAccount = async (id) => { + return request.delete({ url: '/mp/account/delete?id=' + id, method: 'delete' }) +} + +// 获得公众号账号 +export const getAccount = async (id) => { + return request.get({ url: '/mp/account/get?id=' + id }) +} + +// 获得公众号账号分页 +export const getAccountPage = async (query) => { + return request.get({ url: '/mp/account/page', params: query }) +} + +// 获取公众号账号精简信息列表 +export const getSimpleAccounts = async () => { + return request.get({ url: '/mp/account/list-all-simple' }) +} + +// 生成公众号二维码 +export const generateAccountQrCode = async (id) => { + return request.put({ url: '/mp/account/generate-qr-code?id=' + id }) +} + +// 清空公众号 API 配额 +export const clearAccountQuota = async (id) => { + return request.put({ url: '/mp/account/clear-quota?id=' + id }) +} diff --git a/src/api/mp/autoReply/index.ts b/src/api/mp/autoReply/index.ts new file mode 100644 index 00000000..5045e6d5 --- /dev/null +++ b/src/api/mp/autoReply/index.ts @@ -0,0 +1,39 @@ +import request from '@/config/axios' + +// 创建公众号的自动回复 +export const createAutoReply = (data) => { + return request.post({ + url: '/mp/auto-reply/create', + data: data + }) +} + +// 更新公众号的自动回复 +export const updateAutoReply = (data) => { + return request.put({ + url: '/mp/auto-reply/update', + data: data + }) +} + +// 删除公众号的自动回复 +export const deleteAutoReply = (id) => { + return request.delete({ + url: '/mp/auto-reply/delete?id=' + id + }) +} + +// 获得公众号的自动回复 +export const getAutoReply = (id) => { + return request.get({ + url: '/mp/auto-reply/get?id=' + id + }) +} + +// 获得公众号的自动回复分页 +export const getAutoReplyPage = (query) => { + return request.get({ + url: '/mp/auto-reply/page', + params: query + }) +} diff --git a/src/api/mp/draft/index.ts b/src/api/mp/draft/index.ts new file mode 100644 index 00000000..ce6a4431 --- /dev/null +++ b/src/api/mp/draft/index.ts @@ -0,0 +1,35 @@ +import request from '@/config/axios' + +// 获得公众号草稿分页 +export const getDraftPage = (query) => { + return request.get({ + url: '/mp/draft/page', + params: query + }) +} + +// 创建公众号草稿 +export const createDraft = (accountId, articles) => { + return request.post({ + url: '/mp/draft/create?accountId=' + accountId, + data: { + articles + } + }) +} + +// 更新公众号草稿 +export const updateDraft = (accountId, mediaId, articles) => { + return request.put({ + url: '/mp/draft/update?accountId=' + accountId + '&mediaId=' + mediaId, + method: 'put', + data: articles + }) +} + +// 删除公众号草稿 +export const deleteDraft = (accountId, mediaId) => { + return request.delete({ + url: '/mp/draft/delete?accountId=' + accountId + '&mediaId=' + mediaId + }) +} diff --git a/src/api/mp/freePublish/index.ts b/src/api/mp/freePublish/index.ts new file mode 100644 index 00000000..beef0262 --- /dev/null +++ b/src/api/mp/freePublish/index.ts @@ -0,0 +1,23 @@ +import request from '@/config/axios' + +// 获得公众号素材分页 +export const getFreePublishPage = (query) => { + return request.get({ + url: '/mp/free-publish/page', + params: query + }) +} + +// 删除公众号素材 +export const deleteFreePublish = (accountId, articleId) => { + return request.delete({ + url: '/mp/free-publish/delete?accountId=' + accountId + '&articleId=' + articleId + }) +} + +// 发布公众号素材 +export const submitFreePublish = (accountId, mediaId) => { + return request.post({ + url: '/mp/free-publish/submit?accountId=' + accountId + '&mediaId=' + mediaId + }) +} diff --git a/src/api/mp/material/index.ts b/src/api/mp/material/index.ts new file mode 100644 index 00000000..fcc37abe --- /dev/null +++ b/src/api/mp/material/index.ts @@ -0,0 +1,16 @@ +import request from '@/config/axios' + +// 获得公众号素材分页 +export const getMaterialPage = (query) => { + return request.get({ + url: '/mp/material/page', + params: query + }) +} + +// 删除公众号永久素材 +export const deletePermanentMaterial = (id) => { + return request.delete({ + url: '/mp/material/delete-permanent?id=' + id + }) +} diff --git a/src/api/mp/menu/index.ts b/src/api/mp/menu/index.ts new file mode 100644 index 00000000..cc78647c --- /dev/null +++ b/src/api/mp/menu/index.ts @@ -0,0 +1,26 @@ +import request from '@/config/axios' + +// 获得公众号菜单列表 +export const getMenuList = (accountId) => { + return request.get({ + url: '/mp/menu/list?accountId=' + accountId + }) +} + +// 保存公众号菜单 +export const saveMenu = (accountId, menus) => { + return request.post({ + url: '/mp/menu/save', + data: { + accountId, + menus + } + }) +} + +// 删除公众号菜单 +export const deleteMenu = (accountId) => { + return request.delete({ + url: '/mp/menu/delete?accountId=' + accountId + }) +} diff --git a/src/api/mp/message/index.ts b/src/api/mp/message/index.ts new file mode 100644 index 00000000..8b7d3cbd --- /dev/null +++ b/src/api/mp/message/index.ts @@ -0,0 +1,17 @@ +import request from '@/config/axios' + +// 获得公众号消息分页 +export const getMessagePage = (query) => { + return request.get({ + url: '/mp/message/page', + params: query + }) +} + +// 给粉丝发送消息 +export const sendMessage = (data) => { + return request.post({ + url: '/mp/message/send', + data: data + }) +} diff --git a/src/api/mp/mpuser/index.ts b/src/api/mp/mpuser/index.ts new file mode 100644 index 00000000..d954e9eb --- /dev/null +++ b/src/api/mp/mpuser/index.ts @@ -0,0 +1,31 @@ +import request from '@/config/axios' + +// 更新公众号粉丝 +export const updateUser = (data) => { + return request.put({ + url: '/mp/user/update', + data: data + }) +} + +// 获得公众号粉丝 +export const getUser = (id) => { + return request.get({ + url: '/mp/user/get?id=' + id + }) +} + +// 获得公众号粉丝分页 +export const getUserPage = (query) => { + return request.get({ + url: '/mp/user/page', + params: query + }) +} + +// 同步公众号粉丝 +export const syncUser = (accountId) => { + return request.post({ + url: '/mp/tag/sync?accountId=' + accountId + }) +} diff --git a/src/api/mp/statistics/index.ts b/src/api/mp/statistics/index.ts new file mode 100644 index 00000000..72cae601 --- /dev/null +++ b/src/api/mp/statistics/index.ts @@ -0,0 +1,33 @@ +import request from '@/config/axios' + +// 获取消息发送概况数据 +export const getUpstreamMessage = (query) => { + return request.get({ + url: '/mp/statistics/upstream-message', + params: query + }) +} + +// 用户增减数据 +export const getUserSummary = (query) => { + return request.get({ + url: '/mp/statistics/user-summary', + params: query + }) +} + +// 获得用户累计数据 +export const getUserCumulate = (query) => { + return request.get({ + url: '/mp/statistics/user-cumulate', + params: query + }) +} + +// 获得接口分析数据 +export const getInterfaceSummary = (query) => { + return request.get({ + url: '/mp/statistics/interface-summary', + params: query + }) +} diff --git a/src/api/mp/tag/index.ts b/src/api/mp/tag/index.ts new file mode 100644 index 00000000..e681e2e1 --- /dev/null +++ b/src/api/mp/tag/index.ts @@ -0,0 +1,53 @@ +import request from '@/config/axios' + +// 创建公众号标签 +export const createTag = (data) => { + return request.post({ + url: '/mp/tag/create', + data: data + }) +} + +// 更新公众号标签 +export const updateTag = (data) => { + return request.put({ + url: '/mp/tag/update', + data: data + }) +} + +// 删除公众号标签 +export const deleteTag = (id) => { + return request.delete({ + url: '/mp/tag/delete?id=' + id + }) +} + +// 获得公众号标签 +export const getTag = (id) => { + return request.get({ + url: '/mp/tag/get?id=' + id + }) +} + +// 获得公众号标签分页 +export const getTagPage = (query) => { + return request.get({ + url: '/mp/tag/page', + params: query + }) +} + +// 获取公众号标签精简信息列表 +export const getSimpleTags = () => { + return request.get({ + url: '/mp/tag/list-all-simple' + }) +} + +// 同步公众号标签 +export const syncTag = (accountId) => { + return request.post({ + url: '/mp/tag/sync?accountId=' + accountId + }) +} diff --git a/src/api/pay/app/index.ts b/src/api/pay/app/index.ts new file mode 100644 index 00000000..348185c8 --- /dev/null +++ b/src/api/pay/app/index.ts @@ -0,0 +1,78 @@ +import request from '@/config/axios' + +export interface AppVO { + id: number + name: string + status: number + remark: string + payNotifyUrl: string + refundNotifyUrl: string + merchantId: number + merchantName: string + createTime: Date +} + +export interface AppPageReqVO extends PageParam { + name?: string + status?: number + remark?: string + payNotifyUrl?: string + refundNotifyUrl?: string + merchantName?: string + createTime?: Date[] +} + +export interface AppExportReqVO { + name?: string + status?: number + remark?: string + payNotifyUrl?: string + refundNotifyUrl?: string + merchantName?: string + createTime?: Date[] +} + +export interface AppUpdateStatusReqVO { + id: number + status: number +} + +// 查询列表支付应用 +export const getAppPageApi = (params: AppPageReqVO) => { + return request.get({ url: '/pay/app/page', params }) +} + +// 查询详情支付应用 +export const getAppApi = (id: number) => { + return request.get({ url: '/pay/app/get?id=' + id }) +} + +// 新增支付应用 +export const createAppApi = (data: AppVO) => { + return request.post({ url: '/pay/app/create', data }) +} + +// 修改支付应用 +export const updateAppApi = (data: AppVO) => { + return request.put({ url: '/pay/app/update', data }) +} + +// 支付应用信息状态修改 +export const changeAppStatusApi = (data: AppUpdateStatusReqVO) => { + return request.put({ url: '/pay/app/update-status', data: data }) +} + +// 删除支付应用 +export const deleteAppApi = (id: number) => { + return request.delete({ url: '/pay/app/delete?id=' + id }) +} + +// 导出支付应用 +export const exportAppApi = (params: AppExportReqVO) => { + return request.download({ url: '/pay/app/export-excel', params }) +} + +// 根据商ID称搜索应用列表 +export const getAppListByMerchantIdApi = (merchantId: number) => { + return request.get({ url: '/pay/app/list-merchant-id', params: { merchantId: merchantId } }) +} diff --git a/src/api/pay/channel/index.ts b/src/api/pay/channel/index.ts new file mode 100644 index 00000000..a6da9eb8 --- /dev/null +++ b/src/api/pay/channel/index.ts @@ -0,0 +1,70 @@ +import request from '@/config/axios' + +export interface ChannelVO { + id: number + code: string + config: string + status: number + remark: string + feeRate: number + merchantId: number + appId: number + createTime: Date +} + +export interface ChannelPageReqVO extends PageParam { + code?: string + status?: number + remark?: string + feeRate?: number + merchantId?: number + appId?: number + config?: string + createTime?: Date[] +} + +export interface ChannelExportReqVO { + code?: string + status?: number + remark?: string + feeRate?: number + merchantId?: number + appId?: number + config?: string + createTime?: Date[] +} + +// 查询列表支付渠道 +export const getChannelPageApi = (params: ChannelPageReqVO) => { + return request.get({ url: '/pay/channel/page', params }) +} + +// 查询详情支付渠道 +export const getChannelApi = (merchantId: number, appId: string, code: string) => { + const params = { + merchantId: merchantId, + appId: appId, + code: code + } + return request.get({ url: '/pay/channel/get-channel', params: params }) +} + +// 新增支付渠道 +export const createChannelApi = (data: ChannelVO) => { + return request.post({ url: '/pay/channel/create', data }) +} + +// 修改支付渠道 +export const updateChannelApi = (data: ChannelVO) => { + return request.put({ url: '/pay/channel/update', data }) +} + +// 删除支付渠道 +export const deleteChannelApi = (id: number) => { + return request.delete({ url: '/pay/channel/delete?id=' + id }) +} + +// 导出支付渠道 +export const exportChannelApi = (params: ChannelExportReqVO) => { + return request.download({ url: '/pay/channel/export-excel', params }) +} diff --git a/src/api/pay/merchant/index.ts b/src/api/pay/merchant/index.ts new file mode 100644 index 00000000..b4b6ba51 --- /dev/null +++ b/src/api/pay/merchant/index.ts @@ -0,0 +1,77 @@ +import request from '@/config/axios' + +export interface MerchantVO { + id: number + no: string + name: string + shortName: string + status: number + remark: string + createTime: Date +} + +export interface MerchantPageReqVO extends PageParam { + no?: string + name?: string + shortName?: string + status?: number + remark?: string + createTime?: Date[] +} + +export interface MerchantExportReqVO { + no?: string + name?: string + shortName?: string + status?: number + remark?: string + createTime?: Date[] +} + +// 查询列表支付商户 +export const getMerchantPageApi = (params: MerchantPageReqVO) => { + return request.get({ url: '/pay/merchant/page', params }) +} + +// 查询详情支付商户 +export const getMerchantApi = (id: number) => { + return request.get({ url: '/pay/merchant/get?id=' + id }) +} + +// 根据商户名称搜索商户列表 +export const getMerchantListByNameApi = (name: string) => { + return request.get({ + url: '/pay/merchant/list-by-name?id=', + params: { + name: name + } + }) +} + +// 新增支付商户 +export const createMerchantApi = (data: MerchantVO) => { + return request.post({ url: '/pay/merchant/create', data }) +} + +// 修改支付商户 +export const updateMerchantApi = (data: MerchantVO) => { + return request.put({ url: '/pay/merchant/update', data }) +} + +// 删除支付商户 +export const deleteMerchantApi = (id: number) => { + return request.delete({ url: '/pay/merchant/delete?id=' + id }) +} + +// 导出支付商户 +export const exportMerchantApi = (params: MerchantExportReqVO) => { + return request.download({ url: '/pay/merchant/export-excel', params }) +} +// 支付商户状态修改 +export const changeMerchantStatusApi = (id: number, status: number) => { + const data = { + id, + status + } + return request.put({ url: '/pay/merchant/update-status', data: data }) +} diff --git a/src/api/pay/order/index.ts b/src/api/pay/order/index.ts new file mode 100644 index 00000000..53e35aa9 --- /dev/null +++ b/src/api/pay/order/index.ts @@ -0,0 +1,109 @@ +import request from '@/config/axios' + +export interface OrderVO { + id: number + merchantId: number + appId: number + channelId: number + channelCode: string + merchantOrderId: string + subject: string + body: string + notifyUrl: string + notifyStatus: number + amount: number + channelFeeRate: number + channelFeeAmount: number + status: number + userIp: string + expireTime: Date + successTime: Date + notifyTime: Date + successExtensionId: number + refundStatus: number + refundTimes: number + refundAmount: number + channelUserId: string + channelOrderNo: string + createTime: Date +} + +export interface OrderPageReqVO extends PageParam { + merchantId?: number + appId?: number + channelId?: number + channelCode?: string + merchantOrderId?: string + subject?: string + body?: string + notifyUrl?: string + notifyStatus?: number + amount?: number + channelFeeRate?: number + channelFeeAmount?: number + status?: number + expireTime?: Date[] + successTime?: Date[] + notifyTime?: Date[] + successExtensionId?: number + refundStatus?: number + refundTimes?: number + channelUserId?: string + channelOrderNo?: string + createTime?: Date[] +} + +export interface OrderExportReqVO { + merchantId?: number + appId?: number + channelId?: number + channelCode?: string + merchantOrderId?: string + subject?: string + body?: string + notifyUrl?: string + notifyStatus?: number + amount?: number + channelFeeRate?: number + channelFeeAmount?: number + status?: number + expireTime?: Date[] + successTime?: Date[] + notifyTime?: Date[] + successExtensionId?: number + refundStatus?: number + refundTimes?: number + channelUserId?: string + channelOrderNo?: string + createTime?: Date[] +} + +// 查询列表支付订单 +export const getOrderPageApi = async (params: OrderPageReqVO) => { + return await request.get({ url: '/pay/order/page', params }) +} + +// 查询详情支付订单 +export const getOrderApi = async (id: number) => { + return await request.get({ url: '/pay/order/get?id=' + id }) +} + +// 新增支付订单 +export const createOrderApi = async (data: OrderVO) => { + return await request.post({ url: '/pay/order/create', data }) +} + +// 修改支付订单 +export const updateOrderApi = async (data: OrderVO) => { + return await request.put({ url: '/pay/order/update', data }) +} + +// 删除支付订单 +export const deleteOrderApi = async (id: number) => { + return await request.delete({ url: '/pay/order/delete?id=' + id }) +} + +// 导出支付订单 +export const exportOrderApi = async (params: OrderExportReqVO) => { + return await request.download({ url: '/pay/order/export-excel', params }) +} diff --git a/src/api/pay/refund/index.ts b/src/api/pay/refund/index.ts new file mode 100644 index 00000000..cd68a144 --- /dev/null +++ b/src/api/pay/refund/index.ts @@ -0,0 +1,116 @@ +import request from '@/config/axios' + +export interface RefundVO { + id: number + merchantId: number + appId: number + channelId: number + channelCode: string + orderId: string + tradeNo: string + merchantOrderId: string + merchantRefundNo: string + notifyUrl: string + notifyStatus: number + status: number + type: number + payAmount: number + refundAmount: number + reason: string + userIp: string + channelOrderNo: string + channelRefundNo: string + channelErrorCode: string + channelErrorMsg: string + channelExtras: string + expireTime: Date + successTime: Date + notifyTime: Date + createTime: Date +} + +export interface RefundPageReqVO extends PageParam { + merchantId?: number + appId?: number + channelId?: number + channelCode?: string + orderId?: string + tradeNo?: string + merchantOrderId?: string + merchantRefundNo?: string + notifyUrl?: string + notifyStatus?: number + status?: number + type?: number + payAmount?: number + refundAmount?: number + reason?: string + userIp?: string + channelOrderNo?: string + channelRefundNo?: string + channelErrorCode?: string + channelErrorMsg?: string + channelExtras?: string + expireTime?: Date[] + successTime?: Date[] + notifyTime?: Date[] + createTime?: Date[] +} + +export interface PayRefundExportReqVO { + merchantId?: number + appId?: number + channelId?: number + channelCode?: string + orderId?: string + tradeNo?: string + merchantOrderId?: string + merchantRefundNo?: string + notifyUrl?: string + notifyStatus?: number + status?: number + type?: number + payAmount?: number + refundAmount?: number + reason?: string + userIp?: string + channelOrderNo?: string + channelRefundNo?: string + channelErrorCode?: string + channelErrorMsg?: string + channelExtras?: string + expireTime?: Date[] + successTime?: Date[] + notifyTime?: Date[] + createTime?: Date[] +} + +// 查询列表退款订单 +export const getRefundPageApi = (params: RefundPageReqVO) => { + return request.get({ url: '/pay/refund/page', params }) +} + +// 查询详情退款订单 +export const getRefundApi = (id: number) => { + return request.get({ url: '/pay/refund/get?id=' + id }) +} + +// 新增退款订单 +export const createRefundApi = (data: RefundVO) => { + return request.post({ url: '/pay/refund/create', data }) +} + +// 修改退款订单 +export const updateRefundApi = (data: RefundVO) => { + return request.put({ url: '/pay/refund/update', data }) +} + +// 删除退款订单 +export const deleteRefundApi = (id: number) => { + return request.delete({ url: '/pay/refund/delete?id=' + id }) +} + +// 导出退款订单 +export const exportRefundApi = (params: PayRefundExportReqVO) => { + return request.download({ url: '/pay/refund/export-excel', params }) +} diff --git a/src/api/system/dept/index.ts b/src/api/system/dept/index.ts new file mode 100644 index 00000000..d66de3f1 --- /dev/null +++ b/src/api/system/dept/index.ts @@ -0,0 +1,48 @@ +import request from '@/config/axios' + +export interface DeptVO { + id?: number + name: string + parentId: number + status: number + sort: number + leaderUserId: number + phone: string + email: string + createTime: Date +} + +export interface DeptPageReqVO { + name?: string + status?: number +} + +// 查询部门(精简)列表 +export const listSimpleDeptApi = async () => { + return await request.get({ url: '/system/dept/list-all-simple' }) +} + +// 查询部门列表 +export const getDeptPageApi = async (params: DeptPageReqVO) => { + return await request.get({ url: '/system/dept/list', params }) +} + +// 查询部门详情 +export const getDeptApi = async (id: number) => { + return await request.get({ url: '/system/dept/get?id=' + id }) +} + +// 新增部门 +export const createDeptApi = async (data: DeptVO) => { + return await request.post({ url: '/system/dept/create', data: data }) +} + +// 修改部门 +export const updateDeptApi = async (params: DeptVO) => { + return await request.put({ url: '/system/dept/update', data: params }) +} + +// 删除部门 +export const deleteDeptApi = async (id: number) => { + return await request.delete({ url: '/system/dept/delete?id=' + id }) +} diff --git a/src/api/system/dict/dict.data.ts b/src/api/system/dict/dict.data.ts new file mode 100644 index 00000000..fd97a2dc --- /dev/null +++ b/src/api/system/dict/dict.data.ts @@ -0,0 +1,36 @@ +import request from '@/config/axios' +import type { DictDataVO, DictDataPageReqVO, DictDataExportReqVO } from './types' + +// 查询字典数据(精简)列表 +export const listSimpleDictDataApi = () => { + return request.get({ url: '/system/dict-data/list-all-simple' }) +} + +// 查询字典数据列表 +export const getDictDataPageApi = (params: DictDataPageReqVO) => { + return request.get({ url: '/system/dict-data/page', params }) +} + +// 查询字典数据详情 +export const getDictDataApi = (id: number) => { + return request.get({ url: '/system/dict-data/get?id=' + id }) +} + +// 新增字典数据 +export const createDictDataApi = (data: DictDataVO) => { + return request.post({ url: '/system/dict-data/create', data }) +} + +// 修改字典数据 +export const updateDictDataApi = (data: DictDataVO) => { + return request.put({ url: '/system/dict-data/update', data }) +} + +// 删除字典数据 +export const deleteDictDataApi = (id: number) => { + return request.delete({ url: '/system/dict-data/delete?id=' + id }) +} +// 导出字典类型数据 +export const exportDictDataApi = (params: DictDataExportReqVO) => { + return request.get({ url: '/system/dict-data/export', params }) +} diff --git a/src/api/system/dict/dict.type.ts b/src/api/system/dict/dict.type.ts new file mode 100644 index 00000000..22e6411e --- /dev/null +++ b/src/api/system/dict/dict.type.ts @@ -0,0 +1,36 @@ +import request from '@/config/axios' +import type { DictTypeVO, DictTypePageReqVO, DictTypeExportReqVO } from './types' + +// 查询字典(精简)列表 +export const listSimpleDictTypeApi = () => { + return request.get({ url: '/system/dict-type/list-all-simple' }) +} + +// 查询字典列表 +export const getDictTypePageApi = (params: DictTypePageReqVO) => { + return request.get({ url: '/system/dict-type/page', params }) +} + +// 查询字典详情 +export const getDictTypeApi = (id: number) => { + return request.get({ url: '/system/dict-type/get?id=' + id }) +} + +// 新增字典 +export const createDictTypeApi = (data: DictTypeVO) => { + return request.post({ url: '/system/dict-type/create', data }) +} + +// 修改字典 +export const updateDictTypeApi = (data: DictTypeVO) => { + return request.put({ url: '/system/dict-type/update', data }) +} + +// 删除字典 +export const deleteDictTypeApi = (id: number) => { + return request.delete({ url: '/system/dict-type/delete?id=' + id }) +} +// 导出字典类型 +export const exportDictTypeApi = (params: DictTypeExportReqVO) => { + return request.get({ url: '/system/dict-type/export', params }) +} diff --git a/src/api/system/dict/types.ts b/src/api/system/dict/types.ts new file mode 100644 index 00000000..b630dccb --- /dev/null +++ b/src/api/system/dict/types.ts @@ -0,0 +1,46 @@ +export type DictTypeVO = { + id: number + name: string + type: string + status: number + remark: string + createTime: Date +} + +export type DictTypePageReqVO = { + name: string + type: string + status: number + createTime: Date[] +} + +export type DictTypeExportReqVO = { + name: string + type: string + status: number + createTime: Date[] +} + +export type DictDataVO = { + id: number + sort: number + label: string + value: string + dictType: string + status: number + colorType: string + cssClass: string + remark: string + createTime: Date +} +export type DictDataPageReqVO = { + label: string + dictType: string + status: number +} + +export type DictDataExportReqVO = { + label: string + dictType: string + status: number +} diff --git a/src/api/system/errorCode/index.ts b/src/api/system/errorCode/index.ts new file mode 100644 index 00000000..0e000794 --- /dev/null +++ b/src/api/system/errorCode/index.ts @@ -0,0 +1,48 @@ +import request from '@/config/axios' + +export interface ErrorCodeVO { + id: number + type: number + applicationName: string + code: number + message: string + memo: string + createTime: Date +} + +export interface ErrorCodePageReqVO extends PageParam { + type?: number + applicationName?: string + code?: number + message?: string + createTime?: Date[] +} + +// 查询错误码列表 +export const getErrorCodePageApi = (params: ErrorCodePageReqVO) => { + return request.get({ url: '/system/error-code/page', params }) +} + +// 查询错误码详情 +export const getErrorCodeApi = (id: number) => { + return request.get({ url: '/system/error-code/get?id=' + id }) +} + +// 新增错误码 +export const createErrorCodeApi = (data: ErrorCodeVO) => { + return request.post({ url: '/system/error-code/create', data }) +} + +// 修改错误码 +export const updateErrorCodeApi = (data: ErrorCodeVO) => { + return request.put({ url: '/system/error-code/update', data }) +} + +// 删除错误码 +export const deleteErrorCodeApi = (id: number) => { + return request.delete({ url: '/system/error-code/delete?id=' + id }) +} +// 导出错误码 +export const excelErrorCodeApi = (params: ErrorCodePageReqVO) => { + return request.download({ url: '/system/error-code/export-excel', params }) +} diff --git a/src/api/system/loginLog/index.ts b/src/api/system/loginLog/index.ts new file mode 100644 index 00000000..cadaeaf3 --- /dev/null +++ b/src/api/system/loginLog/index.ts @@ -0,0 +1,30 @@ +import request from '@/config/axios' + +export interface LoginLogVO { + id: number + logType: number + traceId: number + userId: number + userType: number + username: string + status: number + userIp: string + userAgent: string + createTime: Date +} + +export interface LoginLogReqVO extends PageParam { + userIp?: string + username?: string + status?: boolean + createTime?: Date[] +} + +// 查询登录日志列表 +export const getLoginLogPageApi = (params: LoginLogReqVO) => { + return request.get({ url: '/system/login-log/page', params }) +} +// 导出登录日志 +export const exportLoginLogApi = (params: LoginLogReqVO) => { + return request.download({ url: '/system/login-log/export', params }) +} diff --git a/src/api/system/mail/account/index.ts b/src/api/system/mail/account/index.ts new file mode 100644 index 00000000..8b662a70 --- /dev/null +++ b/src/api/system/mail/account/index.ts @@ -0,0 +1,46 @@ +import request from '@/config/axios' + +export interface MailAccountVO { + id: number + mail: string + username: string + password: string + host: string + port: number + sslEnable: boolean +} + +export interface MailAccountPageReqVO extends PageParam { + mail?: string + username?: string +} + +// 查询邮箱账号列表 +export const getMailAccountPageApi = async (params: MailAccountPageReqVO) => { + return await request.get({ url: '/system/mail-account/page', params }) +} + +// 查询邮箱账号详情 +export const getMailAccountApi = async (id: number) => { + return await request.get({ url: '/system/mail-account/get?id=' + id }) +} + +// 新增邮箱账号 +export const createMailAccountApi = async (data: MailAccountVO) => { + return await request.post({ url: '/system/mail-account/create', data }) +} + +// 修改邮箱账号 +export const updateMailAccountApi = async (data: MailAccountVO) => { + return await request.put({ url: '/system/mail-account/update', data }) +} + +// 删除邮箱账号 +export const deleteMailAccountApi = async (id: number) => { + return await request.delete({ url: '/system/mail-account/delete?id=' + id }) +} + +// 获得邮箱账号精简列表 +export const getSimpleMailAccounts = async () => { + return request.get({ url: '/system/mail-account/list-all-simple' }) +} diff --git a/src/api/system/mail/log/index.ts b/src/api/system/mail/log/index.ts new file mode 100644 index 00000000..9c6c60eb --- /dev/null +++ b/src/api/system/mail/log/index.ts @@ -0,0 +1,40 @@ +import request from '@/config/axios' + +export interface MailLogVO { + id: number + userId: number + userType: number + toMail: string + accountId: number + fromMail: string + templateId: number + templateCode: string + templateNickname: string + templateTitle: string + templateContent: string + templateParams: string + sendStatus: number + sendTime: Date + sendMessageId: string + sendException: string +} + +export interface MailLogPageReqVO extends PageParam { + userId?: number + userType?: number + toMail?: string + accountId?: number + templateId?: number + sendStatus?: number + sendTime?: Date[] +} + +// 查询邮件日志列表 +export const getMailLogPageApi = async (params: MailLogPageReqVO) => { + return await request.get({ url: '/system/mail-log/page', params }) +} + +// 查询邮件日志详情 +export const getMailLogApi = async (id: number) => { + return await request.get({ url: '/system/mail-log/get?id=' + id }) +} diff --git a/src/api/system/mail/template/index.ts b/src/api/system/mail/template/index.ts new file mode 100644 index 00000000..c044ddd4 --- /dev/null +++ b/src/api/system/mail/template/index.ts @@ -0,0 +1,58 @@ +import request from '@/config/axios' + +export interface MailTemplateVO { + id: number + name: string + code: string + accountId: number + nickname: string + title: string + content: string + params: string + status: number + remark: string +} + +export interface MailTemplatePageReqVO extends PageParam { + name?: string + code?: string + accountId?: number + status?: number + createTime?: Date[] +} + +export interface MailSendReqVO { + mail: string + templateCode: string + templateParams: Map +} + +// 查询邮件模版列表 +export const getMailTemplatePageApi = async (params: MailTemplatePageReqVO) => { + return await request.get({ url: '/system/mail-template/page', params }) +} + +// 查询邮件模版详情 +export const getMailTemplateApi = async (id: number) => { + return await request.get({ url: '/system/mail-template/get?id=' + id }) +} + +// 新增邮件模版 +export const createMailTemplateApi = async (data: MailTemplateVO) => { + return await request.post({ url: '/system/mail-template/create', data }) +} + +// 修改邮件模版 +export const updateMailTemplateApi = async (data: MailTemplateVO) => { + return await request.put({ url: '/system/mail-template/update', data }) +} + +// 删除邮件模版 +export const deleteMailTemplateApi = async (id: number) => { + return await request.delete({ url: '/system/mail-template/delete?id=' + id }) +} + +// 发送邮件 +export const sendMailApi = (data: MailSendReqVO) => { + return request.post({ url: '/system/mail-template/send-mail', data }) +} diff --git a/src/api/system/menu/index.ts b/src/api/system/menu/index.ts new file mode 100644 index 00000000..5913972b --- /dev/null +++ b/src/api/system/menu/index.ts @@ -0,0 +1,54 @@ +import request from '@/config/axios' + +export interface MenuVO { + id: number + name: string + permission: string + type: number + sort: number + parentId: number + path: string + icon: string + component: string + componentName?: string + status: number + visible: boolean + keepAlive: boolean + alwaysShow?: boolean + createTime: Date +} + +export interface MenuPageReqVO { + name?: string + status?: number +} + +// 查询菜单(精简)列表 +export const listSimpleMenusApi = () => { + return request.get({ url: '/system/menu/list-all-simple' }) +} + +// 查询菜单列表 +export const getMenuListApi = (params: MenuPageReqVO) => { + return request.get({ url: '/system/menu/list', params }) +} + +// 获取菜单详情 +export const getMenuApi = (id: number) => { + return request.get({ url: '/system/menu/get?id=' + id }) +} + +// 新增菜单 +export const createMenuApi = (data: MenuVO) => { + return request.post({ url: '/system/menu/create', data }) +} + +// 修改菜单 +export const updateMenuApi = (data: MenuVO) => { + return request.put({ url: '/system/menu/update', data }) +} + +// 删除菜单 +export const deleteMenuApi = (id: number) => { + return request.delete({ url: '/system/menu/delete?id=' + id }) +} diff --git a/src/api/system/notice/index.ts b/src/api/system/notice/index.ts new file mode 100644 index 00000000..f0f5d7e9 --- /dev/null +++ b/src/api/system/notice/index.ts @@ -0,0 +1,42 @@ +import request from '@/config/axios' + +export interface NoticeVO { + id: number + title: string + type: number + content: string + status: number + remark: string + creator: string + createTime: Date +} + +export interface NoticePageReqVO extends PageParam { + title?: string + status?: number +} + +// 查询公告列表 +export const getNoticePageApi = (params: NoticePageReqVO) => { + return request.get({ url: '/system/notice/page', params }) +} + +// 查询公告详情 +export const getNoticeApi = (id: number) => { + return request.get({ url: '/system/notice/get?id=' + id }) +} + +// 新增公告 +export const createNoticeApi = (data: NoticeVO) => { + return request.post({ url: '/system/notice/create', data }) +} + +// 修改公告 +export const updateNoticeApi = (data: NoticeVO) => { + return request.put({ url: '/system/notice/update', data }) +} + +// 删除公告 +export const deleteNoticeApi = (id: number) => { + return request.delete({ url: '/system/notice/delete?id=' + id }) +} diff --git a/src/api/system/notify/message/index.ts b/src/api/system/notify/message/index.ts new file mode 100644 index 00000000..a42d75c2 --- /dev/null +++ b/src/api/system/notify/message/index.ts @@ -0,0 +1,66 @@ +import request from '@/config/axios' +import qs from 'qs' + +export interface NotifyMessageVO { + id: number + userId: number + userType: number + templateId: number + templateCode: string + templateNickname: string + templateContent: string + templateType: number + templateParams: string + readStatus: boolean + readTime: Date +} + +export interface NotifyMessagePageReqVO extends PageParam { + userId?: number + userType?: number + templateCode?: string + templateType?: number + createTime?: Date[] +} + +export interface NotifyMessageMyPageReqVO extends PageParam { + readStatus?: boolean + createTime?: Date[] +} + +// 查询站内信消息列表 +export const getNotifyMessagePageApi = async (params: NotifyMessagePageReqVO) => { + return await request.get({ url: '/system/notify-message/page', params }) +} + +// 查询站内信消息详情 +export const getNotifyMessageApi = async (id: number) => { + return await request.get({ url: '/system/notify-message/get?id=' + id }) +} + +// 获得我的站内信分页 +export const getMyNotifyMessagePage = async (params: NotifyMessageMyPageReqVO) => { + return await request.get({ url: '/system/notify-message/my-page', params }) +} + +// 批量标记已读 +export const updateNotifyMessageRead = async (ids) => { + return await request.put({ + url: '/system/notify-message/update-read?' + qs.stringify({ ids: ids }, { indices: false }) + }) +} + +// 标记所有站内信为已读 +export const updateAllNotifyMessageRead = async () => { + return await request.put({ url: '/system/notify-message/update-all-read' }) +} + +// 获取当前用户的最新站内信列表 +export const getUnreadNotifyMessageListApi = async () => { + return await request.get({ url: '/system/notify-message/get-unread-list' }) +} + +// 获得当前用户的未读站内信数量 +export const getUnreadNotifyMessageCountApi = async () => { + return await request.get({ url: '/system/notify-message/get-unread-count' }) +} diff --git a/src/api/system/notify/template/index.ts b/src/api/system/notify/template/index.ts new file mode 100644 index 00000000..66530a90 --- /dev/null +++ b/src/api/system/notify/template/index.ts @@ -0,0 +1,55 @@ +import request from '@/config/axios' + +export interface NotifyTemplateVO { + id: number + name: string + code: string + content: string + type: number + params: string + status: number + remark: string +} + +export interface NotifyTemplatePageReqVO extends PageParam { + name?: string + code?: string + status?: number + createTime?: Date[] +} + +export interface NotifySendReqVO { + userId: number + templateCode: string + templateParams: Map +} + +// 查询站内信模板列表 +export const getNotifyTemplatePageApi = async (params: NotifyTemplatePageReqVO) => { + return await request.get({ url: '/system/notify-template/page', params }) +} + +// 查询站内信模板详情 +export const getNotifyTemplateApi = async (id: number) => { + return await request.get({ url: '/system/notify-template/get?id=' + id }) +} + +// 新增站内信模板 +export const createNotifyTemplateApi = async (data: NotifyTemplateVO) => { + return await request.post({ url: '/system/notify-template/create', data }) +} + +// 修改站内信模板 +export const updateNotifyTemplateApi = async (data: NotifyTemplateVO) => { + return await request.put({ url: '/system/notify-template/update', data }) +} + +// 删除站内信模板 +export const deleteNotifyTemplateApi = async (id: number) => { + return await request.delete({ url: '/system/notify-template/delete?id=' + id }) +} + +// 发送站内信 +export const sendNotifyApi = (data: NotifySendReqVO) => { + return request.post({ url: '/system/notify-template/send-notify', data }) +} diff --git a/src/api/system/oauth2/client.ts b/src/api/system/oauth2/client.ts new file mode 100644 index 00000000..4c06386d --- /dev/null +++ b/src/api/system/oauth2/client.ts @@ -0,0 +1,51 @@ +import request from '@/config/axios' + +export interface OAuth2ClientVO { + id: number + clientId: string + secret: string + name: string + logo: string + description: string + status: number + accessTokenValiditySeconds: number + refreshTokenValiditySeconds: number + redirectUris: string[] + autoApprove: boolean + authorizedGrantTypes: string[] + scopes: string[] + authorities: string[] + resourceIds: string[] + additionalInformation: string + isAdditionalInformationJson: boolean + createTime: Date +} + +export interface OAuth2ClientPageReqVO extends PageParam { + name?: string + status?: number +} +// 查询 OAuth2列表 +export const getOAuth2ClientPageApi = (params: OAuth2ClientPageReqVO) => { + return request.get({ url: '/system/oauth2-client/page', params }) +} + +// 查询 OAuth2详情 +export const getOAuth2ClientApi = (id: number) => { + return request.get({ url: '/system/oauth2-client/get?id=' + id }) +} + +// 新增 OAuth2 +export const createOAuth2ClientApi = (data: OAuth2ClientVO) => { + return request.post({ url: '/system/oauth2-client/create', data }) +} + +// 修改 OAuth2 +export const updateOAuth2ClientApi = (data: OAuth2ClientVO) => { + return request.put({ url: '/system/oauth2-client/update', data }) +} + +// 删除 OAuth2 +export const deleteOAuth2ClientApi = (id: number) => { + return request.delete({ url: '/system/oauth2-client/delete?id=' + id }) +} diff --git a/src/api/system/oauth2/token.ts b/src/api/system/oauth2/token.ts new file mode 100644 index 00000000..dc7c44f5 --- /dev/null +++ b/src/api/system/oauth2/token.ts @@ -0,0 +1,28 @@ +import request from '@/config/axios' + +export interface OAuth2TokenVO { + id: number + accessToken: string + refreshToken: string + userId: number + userType: number + clientId: string + createTime: Date + expiresTime: Date +} + +export interface OAuth2TokenPageReqVO extends PageParam { + userId?: number + userType?: number + clientId?: string +} + +// 查询 token列表 +export const getAccessTokenPageApi = (params: OAuth2TokenPageReqVO) => { + return request.get({ url: '/system/oauth2-token/page', params }) +} + +// 删除 token +export const deleteAccessTokenApi = (accessToken: number) => { + return request.delete({ url: '/system/oauth2-token/delete?accessToken=' + accessToken }) +} diff --git a/src/api/system/operatelog/index.ts b/src/api/system/operatelog/index.ts new file mode 100644 index 00000000..5fd31dba --- /dev/null +++ b/src/api/system/operatelog/index.ts @@ -0,0 +1,41 @@ +import request from '@/config/axios' + +export type OperateLogVO = { + id: number + userNickname: string + traceId: string + userId: number + module: string + name: string + type: number + content: string + exts: Map + requestMethod: string + requestUrl: string + userIp: string + userAgent: string + javaMethod: string + javaMethodArgs: string + startTime: Date + duration: number + resultCode: number + resultMsg: string + resultData: string +} + +export interface OperateLogPageReqVO extends PageParam { + module?: string + userNickname?: string + type?: number + success?: boolean + startTime?: Date[] +} + +// 查询操作日志列表 +export const getOperateLogPageApi = (params: OperateLogPageReqVO) => { + return request.get({ url: '/system/operate-log/page', params }) +} +// 导出操作日志 +export const exportOperateLogApi = (params: OperateLogPageReqVO) => { + return request.download({ url: '/system/operate-log/export', params }) +} diff --git a/src/api/system/permission/index.ts b/src/api/system/permission/index.ts new file mode 100644 index 00000000..aa355dfc --- /dev/null +++ b/src/api/system/permission/index.ts @@ -0,0 +1,42 @@ +import request from '@/config/axios' + +export interface PermissionAssignUserRoleReqVO { + userId: number + roleIds: number[] +} + +export interface PermissionAssignRoleMenuReqVO { + roleId: number + menuIds: number[] +} + +export interface PermissionAssignRoleDataScopeReqVO { + roleId: number + dataScope: number + dataScopeDeptIds: number[] +} + +// 查询角色拥有的菜单权限 +export const listRoleMenusApi = async (roleId: number) => { + return await request.get({ url: '/system/permission/list-role-resources?roleId=' + roleId }) +} + +// 赋予角色菜单权限 +export const assignRoleMenuApi = async (data: PermissionAssignRoleMenuReqVO) => { + return await request.post({ url: '/system/permission/assign-role-menu', data }) +} + +// 赋予角色数据权限 +export const assignRoleDataScopeApi = async (data: PermissionAssignRoleDataScopeReqVO) => { + return await request.post({ url: '/system/permission/assign-role-data-scope', data }) +} + +// 查询用户拥有的角色数组 +export const listUserRolesApi = async (userId: number) => { + return await request.get({ url: '/system/permission/list-user-roles?userId=' + userId }) +} + +// 赋予用户角色 +export const aassignUserRoleApi = async (data: PermissionAssignUserRoleReqVO) => { + return await request.post({ url: '/system/permission/assign-user-role', data }) +} diff --git a/src/api/system/post/index.ts b/src/api/system/post/index.ts new file mode 100644 index 00000000..9e2540f0 --- /dev/null +++ b/src/api/system/post/index.ts @@ -0,0 +1,58 @@ +import request from '@/config/axios' + +export interface PostVO { + id?: number + name: string + code: string + sort: number + status: number + remark: string + createTime?: Date +} + +export interface PostPageReqVO extends PageParam { + code?: string + name?: string + status?: number +} + +export interface PostExportReqVO { + code?: string + name?: string + status?: number +} + +// 查询岗位列表 +export const getPostPageApi = async (params: PostPageReqVO) => { + return await request.get({ url: '/system/post/page', params }) +} + +// 获取岗位精简信息列表 +export const listSimplePostsApi = async () => { + return await request.get({ url: '/system/post/list-all-simple' }) +} + +// 查询岗位详情 +export const getPostApi = async (id: number) => { + return await request.get({ url: '/system/post/get?id=' + id }) +} + +// 新增岗位 +export const createPostApi = async (data: PostVO) => { + return await request.post({ url: '/system/post/create', data }) +} + +// 修改岗位 +export const updatePostApi = async (data: PostVO) => { + return await request.put({ url: '/system/post/update', data }) +} + +// 删除岗位 +export const deletePostApi = async (id: number) => { + return await request.delete({ url: '/system/post/delete?id=' + id }) +} + +// 导出岗位 +export const exportPostApi = async (params: PostExportReqVO) => { + return await request.download({ url: '/system/post/export', params }) +} diff --git a/src/api/system/role/index.ts b/src/api/system/role/index.ts new file mode 100644 index 00000000..0d477555 --- /dev/null +++ b/src/api/system/role/index.ts @@ -0,0 +1,58 @@ +import request from '@/config/axios' + +export interface RoleVO { + id: number + name: string + code: string + sort: number + status: number + type: number + createTime: Date +} + +export interface RolePageReqVO extends PageParam { + name?: string + code?: string + status?: number + createTime?: Date[] +} + +export interface UpdateStatusReqVO { + id: number + status: number +} + +// 查询角色列表 +export const getRolePageApi = async (params: RolePageReqVO) => { + return await request.get({ url: '/system/role/page', params }) +} + +// 查询角色(精简)列表 +export const listSimpleRolesApi = async () => { + return await request.get({ url: '/system/role/list-all-simple' }) +} + +// 查询角色详情 +export const getRoleApi = async (id: number) => { + return await request.get({ url: '/system/role/get?id=' + id }) +} + +// 新增角色 +export const createRoleApi = async (data: RoleVO) => { + return await request.post({ url: '/system/role/create', data }) +} + +// 修改角色 +export const updateRoleApi = async (data: RoleVO) => { + return await request.put({ url: '/system/role/update', data }) +} + +// 修改角色状态 +export const updateRoleStatusApi = async (data: UpdateStatusReqVO) => { + return await request.put({ url: '/system/role/update-status', data }) +} + +// 删除角色 +export const deleteRoleApi = async (id: number) => { + return await request.delete({ url: '/system/role/delete?id=' + id }) +} diff --git a/src/api/system/sensitiveWord/index.ts b/src/api/system/sensitiveWord/index.ts new file mode 100644 index 00000000..ffda89c0 --- /dev/null +++ b/src/api/system/sensitiveWord/index.ts @@ -0,0 +1,64 @@ +import request from '@/config/axios' + +export interface SensitiveWordVO { + id: number + name: string + status: number + description: string + tags: string[] + createTime: Date +} + +export interface SensitiveWordPageReqVO extends PageParam { + name?: string + tag?: string + status?: number + createTime?: Date[] +} + +export interface SensitiveWordExportReqVO { + name?: string + tag?: string + status?: number + createTime?: Date[] +} + +// 查询敏感词列表 +export const getSensitiveWordPageApi = (params: SensitiveWordPageReqVO) => { + return request.get({ url: '/system/sensitive-word/page', params }) +} + +// 查询敏感词详情 +export const getSensitiveWordApi = (id: number) => { + return request.get({ url: '/system/sensitive-word/get?id=' + id }) +} + +// 新增敏感词 +export const createSensitiveWordApi = (data: SensitiveWordVO) => { + return request.post({ url: '/system/sensitive-word/create', data }) +} + +// 修改敏感词 +export const updateSensitiveWordApi = (data: SensitiveWordVO) => { + return request.put({ url: '/system/sensitive-word/update', data }) +} + +// 删除敏感词 +export const deleteSensitiveWordApi = (id: number) => { + return request.delete({ url: '/system/sensitive-word/delete?id=' + id }) +} + +// 导出敏感词 +export const exportSensitiveWordApi = (params: SensitiveWordExportReqVO) => { + return request.download({ url: '/system/sensitive-word/export-excel', params }) +} + +// 获取所有敏感词的标签数组 +export const getSensitiveWordTagsApi = () => { + return request.get({ url: '/system/sensitive-word/get-tags' }) +} + +// 获得文本所包含的不合法的敏感词数组 +export const validateTextApi = (id: number) => { + return request.get({ url: '/system/sensitive-word/validate-text?' + id }) +} diff --git a/src/api/system/sms/smsChannel/index.ts b/src/api/system/sms/smsChannel/index.ts new file mode 100644 index 00000000..176d075f --- /dev/null +++ b/src/api/system/sms/smsChannel/index.ts @@ -0,0 +1,50 @@ +import request from '@/config/axios' + +export interface SmsChannelVO { + id: number + code: string + status: number + signature: string + remark: string + apiKey: string + apiSecret: string + callbackUrl: string + createTime: Date +} + +export interface SmsChannelPageReqVO extends PageParam { + signature?: string + code?: string + status?: number + createTime?: Date[] +} + +// 查询短信渠道列表 +export const getSmsChannelPageApi = (params: SmsChannelPageReqVO) => { + return request.get({ url: '/system/sms-channel/page', params }) +} + +// 获得短信渠道精简列表 +export function getSimpleSmsChannels() { + return request.get({ url: '/system/sms-channel/list-all-simple' }) +} + +// 查询短信渠道详情 +export const getSmsChannelApi = (id: number) => { + return request.get({ url: '/system/sms-channel/get?id=' + id }) +} + +// 新增短信渠道 +export const createSmsChannelApi = (data: SmsChannelVO) => { + return request.post({ url: '/system/sms-channel/create', data }) +} + +// 修改短信渠道 +export const updateSmsChannelApi = (data: SmsChannelVO) => { + return request.put({ url: '/system/sms-channel/update', data }) +} + +// 删除短信渠道 +export const deleteSmsChannelApi = (id: number) => { + return request.delete({ url: '/system/sms-channel/delete?id=' + id }) +} diff --git a/src/api/system/sms/smsLog/index.ts b/src/api/system/sms/smsLog/index.ts new file mode 100644 index 00000000..52594b90 --- /dev/null +++ b/src/api/system/sms/smsLog/index.ts @@ -0,0 +1,57 @@ +import request from '@/config/axios' + +export interface SmsLogVO { + id: number + channelId: number + channelCode: string + templateId: number + templateCode: string + templateType: number + templateContent: string + templateParams: Map + mobile: string + userId: number + userType: number + sendStatus: number + sendTime: Date + sendCode: number + sendMsg: string + apiSendCode: string + apiSendMsg: string + apiRequestId: string + apiSerialNo: string + receiveStatus: number + receiveTime: Date + apiReceiveCode: string + apiReceiveMsg: string + createTime: Date +} + +export interface SmsLogPageReqVO extends PageParam { + channelId?: number + templateId?: number + mobile?: string + sendStatus?: number + sendTime?: Date[] + receiveStatus?: number + receiveTime?: Date[] +} +export interface SmsLogExportReqVO { + channelId?: number + templateId?: number + mobile?: string + sendStatus?: number + sendTime?: Date[] + receiveStatus?: number + receiveTime?: Date[] +} + +// 查询短信日志列表 +export const getSmsLogPageApi = (params: SmsLogPageReqVO) => { + return request.get({ url: '/system/sms-log/page', params }) +} + +// 导出短信日志 +export const exportSmsLogApi = (params: SmsLogExportReqVO) => { + return request.download({ url: '/system/sms-log/export', params }) +} diff --git a/src/api/system/sms/smsTemplate/index.ts b/src/api/system/sms/smsTemplate/index.ts new file mode 100644 index 00000000..0433fe3a --- /dev/null +++ b/src/api/system/sms/smsTemplate/index.ts @@ -0,0 +1,80 @@ +import request from '@/config/axios' + +export interface SmsTemplateVO { + id: number + type: number + status: number + code: string + name: string + content: string + remark: string + apiTemplateId: string + channelId: number + channelCode: string + params: string[] + createTime: Date +} + +export interface SendSmsReqVO { + mobile: string + templateCode: string + templateParams: Map +} + +export interface SmsTemplatePageReqVO { + type?: number + status?: number + code?: string + content?: string + apiTemplateId?: string + channelId?: number + createTime?: Date[] +} + +export interface SmsTemplateExportReqVO { + type?: number + status?: number + code?: string + content?: string + apiTemplateId?: string + channelId?: number + createTime?: Date[] +} + +// 查询短信模板列表 +export const getSmsTemplatePageApi = (params: SmsTemplatePageReqVO) => { + return request.get({ url: '/system/sms-template/page', params }) +} + +// 查询短信模板详情 +export const getSmsTemplateApi = (id: number) => { + return request.get({ url: '/system/sms-template/get?id=' + id }) +} + +// 新增短信模板 +export const createSmsTemplateApi = (data: SmsTemplateVO) => { + return request.post({ url: '/system/sms-template/create', data }) +} + +// 修改短信模板 +export const updateSmsTemplateApi = (data: SmsTemplateVO) => { + return request.put({ url: '/system/sms-template/update', data }) +} + +// 删除短信模板 +export const deleteSmsTemplateApi = (id: number) => { + return request.delete({ url: '/system/sms-template/delete?id=' + id }) +} + +// 发送短信 +export const sendSmsApi = (data: SendSmsReqVO) => { + return request.post({ url: '/system/sms-template/send-sms', data }) +} + +// 导出短信模板 +export const exportPostApi = (params: SmsTemplateExportReqVO) => { + return request.download({ + url: '/system/sms-template/export-excel', + params + }) +} diff --git a/src/api/system/tenant/index.ts b/src/api/system/tenant/index.ts new file mode 100644 index 00000000..d79fb7b2 --- /dev/null +++ b/src/api/system/tenant/index.ts @@ -0,0 +1,62 @@ +import request from '@/config/axios' + +export interface TenantVO { + id: number + name: string + contactName: string + contactMobile: string + status: number + domain: string + packageId: number + username: string + password: string + expireTime: Date + accountCount: number + createTime: Date +} + +export interface TenantPageReqVO extends PageParam { + name?: string + contactName?: string + contactMobile?: string + status?: number + createTime?: Date[] +} + +export interface TenantExportReqVO { + name?: string + contactName?: string + contactMobile?: string + status?: number + createTime?: Date[] +} + +// 查询租户列表 +export const getTenantPageApi = (params: TenantPageReqVO) => { + return request.get({ url: '/system/tenant/page', params }) +} + +// 查询租户详情 +export const getTenantApi = (id: number) => { + return request.get({ url: '/system/tenant/get?id=' + id }) +} + +// 新增租户 +export const createTenantApi = (data: TenantVO) => { + return request.post({ url: '/system/tenant/create', data }) +} + +// 修改租户 +export const updateTenantApi = (data: TenantVO) => { + return request.put({ url: '/system/tenant/update', data }) +} + +// 删除租户 +export const deleteTenantApi = (id: number) => { + return request.delete({ url: '/system/tenant/delete?id=' + id }) +} + +// 导出租户 +export const exportTenantApi = (params: TenantExportReqVO) => { + return request.download({ url: '/system/tenant/export-excel', params }) +} diff --git a/src/api/system/tenantPackage/index.ts b/src/api/system/tenantPackage/index.ts new file mode 100644 index 00000000..e431a9ee --- /dev/null +++ b/src/api/system/tenantPackage/index.ts @@ -0,0 +1,49 @@ +import request from '@/config/axios' + +export interface TenantPackageVO { + id: number + name: string + status: number + remark: string + creator: string + updater: string + updateTime: string + menuIds: number[] + createTime: Date +} + +export interface TenantPackagePageReqVO extends PageParam { + name?: string + status?: number + remark?: string + createTime?: Date[] +} + +// 查询租户套餐列表 +export const getTenantPackageTypePageApi = (params: TenantPackagePageReqVO) => { + return request.get({ url: '/system/tenant-package/page', params }) +} + +// 获得租户 +export const getTenantPackageApi = (id: number) => { + return request.get({ url: '/system/tenant-package/get?id=' + id }) +} + +// 新增租户套餐 +export const createTenantPackageTypeApi = (data: TenantPackageVO) => { + return request.post({ url: '/system/tenant-package/create', data }) +} + +// 修改租户套餐 +export const updateTenantPackageTypeApi = (data: TenantPackageVO) => { + return request.put({ url: '/system/tenant-package/update', data }) +} + +// 删除租户套餐 +export const deleteTenantPackageTypeApi = (id: number) => { + return request.delete({ url: '/system/tenant-package/delete?id=' + id }) +} +// 获取租户套餐精简信息列表 +export const getTenantPackageList = () => { + return request.get({ url: '/system/tenant-package/get-simple-list' }) +} diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts new file mode 100644 index 00000000..e505921d --- /dev/null +++ b/src/api/system/user/index.ts @@ -0,0 +1,91 @@ +import request from '@/config/axios' + +export interface UserVO { + id: number + username: string + nickname: string + deptId: number + postIds: string[] + email: string + mobile: string + sex: number + avatar: string + loginIp: string + status: number + remark: string + loginDate: Date + createTime: Date +} + +export interface UserPageReqVO extends PageParam { + deptId?: number + username?: string + mobile?: string + status?: number + createTime?: Date[] +} + +export interface UserExportReqVO { + code?: string + name?: string + status?: number + createTime?: Date[] +} + +// 查询用户管理列表 +export const getUserPageApi = (params: UserPageReqVO) => { + return request.get({ url: '/system/user/page', params }) +} + +// 查询用户详情 +export const getUserApi = (id: number) => { + return request.get({ url: '/system/user/get?id=' + id }) +} + +// 新增用户 +export const createUserApi = (data: UserVO) => { + return request.post({ url: '/system/user/create', data }) +} + +// 修改用户 +export const updateUserApi = (data: UserVO) => { + return request.put({ url: '/system/user/update', data }) +} + +// 删除用户 +export const deleteUserApi = (id: number) => { + return request.delete({ url: '/system/user/delete?id=' + id }) +} + +// 导出用户 +export const exportUserApi = (params: UserExportReqVO) => { + return request.download({ url: '/system/user/export', params }) +} + +// 下载用户导入模板 +export const importUserTemplateApi = () => { + return request.download({ url: '/system/user/get-import-template' }) +} + +// 用户密码重置 +export const resetUserPwdApi = (id: number, password: string) => { + const data = { + id, + password + } + return request.put({ url: '/system/user/update-password', data: data }) +} + +// 用户状态修改 +export const updateUserStatusApi = (id: number, status: number) => { + const data = { + id, + status + } + return request.put({ url: '/system/user/update-status', data: data }) +} + +// 获取用户精简信息列表 +export const getListSimpleUsersApi = () => { + return request.get({ url: '/system/user/list-all-simple' }) +} diff --git a/src/api/system/user/profile.ts b/src/api/system/user/profile.ts new file mode 100644 index 00000000..e78424cc --- /dev/null +++ b/src/api/system/user/profile.ts @@ -0,0 +1,77 @@ +import request from '@/config/axios' + +export interface ProfileDept { + id: number + name: string +} +export interface ProfileRole { + id: number + name: string +} +export interface ProfilePost { + id: number + name: string +} +export interface SocialUser { + id: number + type: number + openid: string + token: string + rawTokenInfo: string + nickname: string + avatar: string + rawUserInfo: string + code: string + state: string +} +export interface ProfileVO { + id: number + username: string + nickname: string + dept: ProfileDept + roles: ProfileRole[] + posts: ProfilePost[] + socialUsers: SocialUser[] + email: string + mobile: string + sex: number + avatar: string + status: number + remark: string + loginIp: string + loginDate: Date + createTime: Date +} + +export interface UserProfileUpdateReqVO { + nickname: string + email: string + mobile: string + sex: number +} + +// 查询用户个人信息 +export const getUserProfileApi = () => { + return request.get({ url: '/system/user/profile/get' }) +} + +// 修改用户个人信息 +export const updateUserProfileApi = (data: UserProfileUpdateReqVO) => { + return request.put({ url: '/system/user/profile/update', data }) +} + +// 用户密码重置 +export const updateUserPwdApi = (oldPassword: string, newPassword: string) => { + return request.put({ + url: '/system/user/profile/update-password', + data: { + oldPassword: oldPassword, + newPassword: newPassword + } + }) +} + +// 用户头像上传 +export const uploadAvatarApi = (data) => { + return request.upload({ url: '/system/user/profile/update-avatar', data: data }) +} diff --git a/src/api/system/user/socialUser.ts b/src/api/system/user/socialUser.ts new file mode 100644 index 00000000..79f4d402 --- /dev/null +++ b/src/api/system/user/socialUser.ts @@ -0,0 +1,31 @@ +import request from '@/config/axios' + +// 社交绑定,使用 code 授权码 +export const socialBind = (type, code, state) => { + return request.post({ + url: '/system/social-user/bind', + data: { + type, + code, + state + } + }) +} + +// 取消社交绑定 +export const socialUnbind = (type, openid) => { + return request.delete({ + url: '/system/social-user/unbind', + data: { + type, + openid + } + }) +} + +// 社交授权的跳转 +export const socialAuthRedirect = (type, redirectUri) => { + return request.get({ + url: '/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri + }) +} diff --git a/src/assets/imgs/avatar.gif b/src/assets/imgs/avatar.gif new file mode 100644 index 0000000000000000000000000000000000000000..fdbd32c675f85af4ed57021ac0638a21a3c6cad3 GIT binary patch literal 6334 zcmd_tc|cQFz6bD|n?QDwkc0#TNdjn8Kv1-Z)+S*KSS<=wq-p_?L8l8^YSlNjA@?SP zkU&^uQ)0pzWRX=+DJJYFYCuFlTiIk2D>&L}rxG$$9JAR-LhrN_UcMBnKUyqQ#v!fFg@4U*x23O{ju@-hnE_2a&q=w?aX>G z9^QLHCX=l_{JCF()HOP)p`pPqJF}#uB)s=#adGkSKQkaeQ8BY2J7DJPv=pSTT@#wL5EgaDPSFKy6!cR#tDdAr+RZ?hUgS@!Nbug1z}c zzH40FkO)%@>=!6A6o3GnP2Z-Ff&c)$C<4R~ERiP266Hw>WpautH7z~kRAyFoPVVV5 zdFrzoZGJ&vQE`c`^jukaMdkUb>YCcR`f?iaLK>4q7h8iM4tptuU?2e$1Bh==?g5>c zz1=sHNWCt^Uh6waj2;5^ngMq|3Ex8na6K2NW!O#wQE#}hAS3ro%^KRC+Wov75MZO5 zQ8xL~)reX=4!x$8xB6or!9CT>{jt?Sl9R0LFKHp-eIBWHwRxL{C|y_6ZS4+LQC5v# zi$TzL8%KG_a#WlrhRF9oCFHY@WQ1mb9irtP;a|&^MTu?`qk9?`^zdE|-NHQRAZP4U zYmA?*BF4MzniXf;B1*)oqZ$BIAV$B#S{aXx_8G7{BaLBGU+{ z1Mhb9s%SMlq#>DB|2%RV*dA}j$IkYo8Ykjh#0vZ|CTpTF(WBk!?s_q2PDC~c-H`bY z=eAf%E5dxkQU0gCy2Si`Lfdo#?LoEjyl~*p(X!c{1t0!W~?rlKZ z2gRxc8xjV?4IzMbEotrdtS8&uTr>`OyJSoZf72|Xw#$8dIz63(^@yDmesW=E@^oy+ zim5Yk0|%$_j!l+MssHq2j*#`HRw%&P8n6T$0q*Nsp-i;`0N7NkL}>yFAd{umPS~U@ zg%YNkIz!0J&R0rV5LFG5O#LE)VjQYeTb@)1;HywNwbO;{Lb@7NrpD{D3bAc0oSM*m zRauJfQrGv%ay##UL&K2Nr52=$&yF@s)ppg+&1SXL5*HvQm2mli_=g`Ur8IX8FYq+y660Ir(VErRi_H8Cy`>#K$H`J31^2DoMQDnDf=ZQI zSY)yWBiQXNyOOS-9MQGtc>wb8CxjY}l{p?*d5;s;9vEn~{28ifFkU1z%Yc8`d*sQ} zEii`1)2Ipr<3%yqxMNP?s9vz^qX$^B%QT$me3_|n?b$zp=i3Tm-Po9}Ili@J4#r(0 zJja2&3WY{a>*w!(m|<}&qOb!auz%zp%lh^Z2kREO>EpJ-%TIkd9jk`jM4}guxAw2% z9Dezdh4uJ#7~<$QvYG1kVBUYp6~^8CY(*?+jwHZka z(u*pA3N^0`Us<)#up`D;)d3#MTz?liOipVqIdZMQgS$SKG#E zLRbcC?aE=x$jNDVi!A{xyQ|8ND3FWgBOwlYRN^;7*-g5%{gP|o(lc~eZONvY_99Ox>ieYh{1rDQ4rdodoMu~@XMd?{W`0E)n(f-Ha_#$m!xz1!c2i1${WwaKop6HMgb=z-;> zi2V->Y8FvBBZsL5^Rrz`Jsj&4*?H&2f8o}K(RBk(t_EkS$GViw|lW~OA((#4A zz`4EgR~_%MpG9cwH*Mn^+30}W!ww;UjzwB6L{%;mwMmIPr_SF`VUgb)GbG?l1?Ye+ zu*8(vhzR-Weue-UAWc#vBq9^#N>g5&DXNt8B!9dZR1t}3r(xP=2&Acz*+md>vqqc= zm7h0dg{&zhpGQ(JDT(JxT9fep=gF5Y!<)&4N?OZJnB;#;as3W7bXP&_l5QT>n{t&E zeRp~)sd_UJ-?iNKxy8>GwtunU--^P9s(wg!WkY(VTWkv?J$M6iVo6nmJYk7X(nYi;&dK@iObK2bqM;I6yo_B2!wFhdO5T4vINJVZ&#*NR!JCLnOEfW^DLOfH1;NI zE{YjL!^z*lb=_r%?X5E6XbNLC0ryCbon5l_`HSa)9xpjbiee&Xp+KWTV0QI9-@YHE zJdSXFSM<(wrw9lZ9vkXAV1}1va)Qb|O?Mn;FRBWO;e~U)Jk{bG1hg;;P*csQpdGq> z++fR_?3M>}X-4k`I497fC>T0$KA96Z56pU&!Zw=#ZTQSZyHNi#c+`K>j07-%rLTA4 z;cvSjW=gQqBq^4R2SI$YDhYRC(dhEkca#FzrR1)TshG)JBxMw!L=JZ zvD5aM1@d|DGh3wFB;0^G?&Z!iCa&7kSgu+4Tvg_alY54;MO~SzcJA?@JylW8g%72f z-7D7bsRLRsn335K*=0w6i0g@*gYoZsdmy-%c4I_V&P}#O6Du%yS3k>;ee-P@X9Afv z^7Zcq_V*^>8Jl?`o(J!(A*8EI5#=IQ&&+roE#{OQM4tKB!cmq;p(T31Cr z=m7YhI1O(>(D9z2B9kIm(=JK?_khRvoH6#Qcu1wGvQ!XLBbS<)p_Y%>mAN~!>#jvV zf)&RBo_IeBn7nax^5oLX(u!2bFIZ6@xIARVt-L@gN;13B+x6gke>iwGOi15ZX~OgAU>y1V%oC|v=dDW zl8TB-01^nf5G$>#vujHj5CKck;+k@sfr1Pw8mA?8%DYPvjV{`PKDfW6G@}Er&AN;J zPB)f8cG9Lkn9`+Z;IUf$<0-Gveq-cKIhg@?Gt>p_Ri%Uo7czVNU z_L!|9rw$I(MQPSw@*4bOruh=aA1JgL`8|KYD$diWvgCnYwnwCc)?(yM@`ga?C#PQ9 z{802SCY#^-&E|zUrMp+!5;T22K7mO?_;y?H16V>2$TduA_aq-1tMm3Ri#kKh4sSJmBwQc#z*pme1x5G zPpB$%XC0s8&CMUJ7%R1^P}X6;jHD1K&U?DN?q8CkS7f|@j62a^Dt`fJ&}v|{ZICzj zT|(d4;19AHn$OIRkOL_=uLmMa;9%5uh)vm|mXmsSU6>SRAvo-p0X$}+MCQ(uyqwXG zaXxjP&OmP(TsMx8o~njW4#Ihmx-mKuGTZX$@BaE{!j`@gwoD3$e;0P5GMOTNW9-6Q zz+`L~d=2WH#;Gwom##roXz+%tTx>fFry*RurYysEYZ`7T61oP# z;kzbc*PDo~9lJPP-(COcVOD!R@yRPs2|p>8bz_P@tYjWTKEQH_cDBy^s#XCmvD#?$ zQomgi9}k$14Kkx5TnJV`NRw~>)l(VH+vc!=)kp*pzY_cM@`U4T7KS1c@!0-moxXi; z^yh(kOuktv^?(oRP@RUltY$t^HSCs>wi)zx*frWFCDnkyH6y5PO8ZU;uKGHJ#TzBo{g4Z85W-m}_FLaE8GHP?K7}%C zGB)`>Onq!J_SSLQQwjMe+k?}_2pOK=;hJYzNi+w-?LnI zNQLR}<>9_`J*qIer}+l+nMXHUCV>f^doOI`v5E~kJaojCOWQ4K78q^XuD;H(oCY7H=X(|Oq~1SX`!E*Kn`PGfRR-W zCL-)RlzPkrI}2&~(TuiwC#-woGppMXpRToRw%$MFuYXXnp8T5$Ffo#yZf7G+R@;@0BhXe!y5NHtZ~1?`s^!LGhVy8<*lo6CRh6# zZkSx%r|21g28R{IPAP8aN=-ULR&?+5WKuPbi0@o(`;)~B3)`P9_IW_6<^i1@$3dh!1f)?P1HzJ_(`Z?K-o-rfo&{|0NW z3Dy?EdqaZg-(cd$1x^3ScIXcrz@HGirP$xd5 zHNUoxLL39<-VNIkYWI&^|f| zhjp3F$3==X)Lo6SyZPhyS-X;GwA3mAaN1>8d1EXW!*K{n^>M;Cx`@Cpdf(3^umtAp zutHsh2WsstagO8=7*vPpUJry-%^&e7SJBamP0y`iGsZS=#-;#z=nhCz9Q@w9b`#UB zV!SRB-2iGS6n@^tTJeRQ!K^{I(dsHB`^g|fF21okRt{$I^+gCPlXi@`O*lcwl tSeX}8JPlhsJH^s%`^fr8K)RRk_D-Z(;fYh+bq~SM_Ebo|rD1@Le+4VAC8PiV literal 0 HcmV?d00001 diff --git a/src/assets/imgs/avatar.jpg b/src/assets/imgs/avatar.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d46a70a46430e31744420865138cc7eedb8b77e1 GIT binary patch literal 6264 zcmbuDWmMGNx5t0O&^aUB-GkEIDM;r~(j~)yH1YsLHwXesHv>qAlqeFC(jYM)4I&{S zWpMHN-*xZ0ukYUH)p>E&=dAr*`<(r~UASEXAX@60>Hr7?0HC`BZkK^5fZ#3&2?&Xa z3CSR&5Hd=Nd-o_PS*U66l0}G_gZXX=!NmA^_{CtlauO19x)$0GwJnhL_Q-_DgqD_5 z(Es;<+g<>Q58MDZU=SOC1qFejpxb_c9ssbgz;}@U8z3wkFg7mU?Gk|h_X+@TAqs#X zia80nZ-z*uFcD zP)Y!!L(=9e+rcumPh+2YXE(J-QejRVpO0I!gpo~}BLMlcHz=;JP-D~&6C7Ho@}#Q6+@<03;Kb8wmX_-jgx(e>OVud(P*XF^Vl16sN3 zL9VSB`M_g%o1P)BrQX9$svj~GgHrVO^-|)7rKENg&rc-;oC!k)8e~5)HYqtZ2DASDJ8VEC2{_$OcaQB$S8L5D^GQ? z+s$*j6EqD70RKm}|D$LS78v_qHDf{U_Uv61dt8A`-ZyYSRaJk{$HGGBBjlXX+YC8V zKwC6zGPAqf`cyz$pIyrm(T+CDOVQ!GC`}aSwxBW1ad%G@#B25L_H>uJj_SdwyELe? z%Fe*gzZu@k_u1M3aj&S$SqNz`-S2$GASYKgNd~J_^|UX_DG1h-kMkwvwcS#nMa04V7ZUszQk&4N$!hAgCTqpAQhQTk8ON*f0ofdV2=AQy@IIWQN=Pws^DCr*gw;EVeJmQO)u{x?i}GaVh_sL#~;X+7GB*sV+YkAKOH z1g^{+L!LhAjVPHKFZ{%Zl7;fP!+=RU0=KZEB1ma_T2b`3fZ#yb9jF2X0D-ZvvGBpT z*tl5WJ1!7#2MWcx$0`I-!X;-DHZZh%$`YQ&E-0d0{DFeQJ6%-8$i8tZqKVR{uM@jzyiT0o z7c1-kPHi_&7&CHY^{doMimEv}v39dY#&Lgi=@(n{fNjE>B(JpcOwknSb2+tX++3cg z32t~n^3u%`AtMKa_^FzDw{tyaj}mT_SWwV&0ibLN*!>>|Owa%LbKvT4stf0)h_`1JuTz zwF(^utK=XF|Ct&@jJM8u8IOb7jQc*V9HTkqI4CC2!JgUU0{6MLFg9yAHpz3dDZx!@ z%o`8A+h+QIWx*?}R6xxZ68zM4BDx4){0`?SOX0o-f_=pGF+*6D2_s#3Xv5In#4PWP zm^HIklb(YU7j;~7_apAK8Th|gPZ1_+9E4lGMe)LS?_tSUeYcu}830qZ-I%ywSJLBQCSZ*o5 zCo0nC;jboG#KEqAdOUJ=+8r_QQkfjYgT#Y^(;!X6g@~ z_AifVbfl{4(J4zV*gr{36(ao(#RMe|fy1^js-iSUeI6-0np|F+Dw&E=9G_ecKU^vA zWN}f?U8B~?+h<|4H%<+0JW1%?Uve+}TEc~Fp?(8cl~Ve?ztZD{ z{~Twy!MMdpLAL%yl|zp!)gkn3(ZRQbSoH%#jIxSu*UBhKF_KY*!St}JmGuYDa}dV@ z_oh&%3MK5Gv1|}-V*SUam&DxL^F$R6P_Yf^%0k_9LI(d(DHl zPM&QTmtZpFK!M%pmuP3^N0zjRnhP%sU!^J~xher`D*M--%wWo?s)K_BH}(j((H1w? z7Z*GPd{)eY%@U4bUG2<~q*Uc((k`5%BAC@?DR(L0zLv)#<0>7~;bCt|0bO7%Ianpn zDn!%zLBCUU+O+z^aS_=Pt+MKzoWSkmc^{qLsu%`6;^r1aV{6ZqkAcYR77qj>|qEPS9${R0!P!M8cHKDuKPmO5kT`Ds^pNeal!XZCTLU=7b z{t^W-SpZzaE=Oecj8B7_iQm%=1)|lV(V(*nM^=yN!W*P~prKXZr*1bYLCYGHz^*?%h;PzP7OMlZ4Qx z{1%wAwuzq9MLM?!Mov+J?XDQ(a>oiy-+x zA5mNxTAM+pK^nSlVjC!DnfL{tR3swA>jL(F3!ReXLiLOu(mk)1Ad}H`k zDvyCLitK~}eGA+p|MqP!|JQHE(p$h(&Y~nosuYpdE>*XXA_t3$HF`8MP?;7g$AtFT z>28_cZ~Zc6)KBIYJ-A!6ezqZLJ0&`AJUW~N-q~jDs>2y!A+bVs>7Em=xlUaQHOtgA z)_5}rgeQT{prumS(l!aZ9s5J4+i-OWokXFjcY=$+q2NBgNtz7!HKOs$6>q+a$j}x8 z0|<95@%!M{&R^n|q_h5kYKUA9Y7M*SMm(G6<~Ls}3dg@m+*lS}DJj;lW>r*&VtU8t}8EzuB{!;QTLJr)#bnPeko9@_>%zJS|VkP zq(@GhDofDSCE1DSSyHNOve6_Tsc)O;jy!WK4{C5AfYY#(MCD_wj;l7OhwS&Owz;OQ z+Av;ow)s^N=-F9Ggp)!MI8FhVfZd9(Xa_%RD3J~3ahc1ri>JYDXehfQdP2SdhX6zX z06!G{F*%?|ff9Uo`jcl9VbGXN<*o?c9%Qcl!`HD!-*kV}U_C0F#y85{Nsrl#h%Ny0 zGhdHH-|}^l3G5c=HPMhdy0rPtsXd8UuT=QQhu-{%>ElrAU^o9n`5$7#;`4Lwj$|$k ze>h$|+(tcL=ESL?%JG zB%dfqN0c6Y{fo;V%L4S2J|Xw6vghC7DJdXikfoOoh5Z4nW!a5_)5XG&Jjql|qKKS|h#jfwPynLJ0 zBOycm3`flAZyQCqTfhJlF+<%mk4!n4g5%jj9OIo>Cr?ftGlPeo#N|c#7JhEmd z`*~xLs-WF2tr3RF9Z4y)J;@b>0iOk8DKtUaiRQ|+r4*)Ynp((I%| z6GaeKR9BXVY(8(q^c;0<6%VonVwDK@?IMeXA8xq@Gcn9G;@$#KYDGYlNZ?Z9+#(rJ z{`sQUY@6Y51Lq*;;-wYd(C_F0lm<*`M(e~&Octe=J*S6=E>mCK)cZ4l6~1}49`nAc zqVi>Keg?C8G^{SIKfT!5yNCo`Ap&)g_1B0rKJ7Focn>aKTVsHkx`_JMX{3tj$u>at zI0SBi$RkQF5%Pnj@VTx}*IL*ReCd#}IepcO#$zDEDdF=sdQab9>X{W|0q|qoCly&* zkrz3ljI^UWbrCq$Sal5;xgXQ{kL=wJWiAMvHHi4wlFVzedxwrHzxLmKU{l?0foUp! zxs?pYcFW}F(iw0GI%Y^X`%^z1YcYW+WS4A;aeK0y4$9x`n(=-AWoz<%9~9WzM3?tf zj8V%)_)lfghSRHxQx>3V&6H;~!$G~2k*)vyL+9h* zF6tK8_*R@WV+r44X30-hd~o-uDVBfaW4`imz^iwyWd5xC*;sP8E_^wJiksZA2vHjN z(CcFN<1gh-98!wbr?ZBtJBdz6GcqjloM9Rz&?okNLYl{Jqr7DcD~r*66j?E82c+Dv zRP0|fKL;#R`73fS-iECsC9$!Z+WMf)OGS(!6lwWBLZ5D;DR17`1pgDTscFN>6RZMW zIDEKJt(k8`A*6gX<>`7&e+#%B$D}~!7w)&!#?;>e4TY6BG8#Lqa1JL=OEVb(lY&hgHWmo}y^l9rQ@Ia0(-S;V1<%xVLlB}X8-sNxJojf$ zmiR_6G!Fs-?|u#=7(|?!Wprj1WZMv^t^#Mo{+YX41afAvNKDTy4qiJ#XO=Dwsu-*` z4Cq|_=(+NizBhTbH|&-)wGpvEpEMElu6=K6YU(XqYU(#nZ&KMM2{@=jDWN}zMHThr zGM5myaXMubt-3?=qw2uD`RkyMKOPa+ysoID^4sW=lUbRI15*91B2 zo6JX@O}Y1=6{<}TV}obhs!^(S1I1>71e{dFL_8$L8>!SV6_|+{nL|xI|rrLlSfjLJ4vr?#bx-ADLXnY zgilc@@O6GI!CNlI4s3moX%|TAUTsLKDDDg-)F{@VF+QmbF@Gg0^hGGjNuP~Jbxdt+ zd__@chTr1@HQmc4p09MYbO zez$-+ME&*W8Y&h_W&^6ePb80sWE(HDAh^ zi4~Lruc+lyf@zd+`#r0mg>i9E1YaGvm^(YcAKZ}u!yy2)i(^i{M->%1R<$YCVFrlXzFW1YXmrAb z)KGK^FX!mv^4#>o<|N&HW%_2uUyNKAuMMnSznr=&F${X=!u$>Fe=V55CBE}-!H_!> zMv2A14qNysIh$cP4!ekQ+J6kBzZGs_q2k1)JRjdyJZfZsyvu>XDZ}BE5g+WSz)jH! z{;YURbaD4rY^gw4mRqpF4Skk_XHWHujU|&Mcx!hSuHl)9T@*@G0!lNbZELl9Eukai zbh-8=6Z2KyBTXZ1-}`fraU%Ux)$%yF+9gRskBo9GIqpgGF<5AS>jg8XM;hx+BK&r! zE;rJg*Y;rabcZV?9Sw-Hcu8E@-LpjYEWy&GXL~VkR^a(MJgtdG{ojJ?4>5}t-j+P^+(<;A4L=tRZB~w^*Ys`;%i8B)xY+a}hH{7crjc+B=^6+Oo zH*Y&-{z2Uk)}URh-CaU3r=g>zo-K#=y=+}n#QG_*Ta{ap?J75@#D9$knHK|BvqUI^ z_46Fh)wAfUx!fAAsP2QLMmc}Qr)mFM_MdRY_n8)#DArG~RSr>3R@QnToH%m3_+J|q BLq7lj literal 0 HcmV?d00001 diff --git a/src/assets/imgs/logo.png b/src/assets/imgs/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7e1043f21e57ead95f41354f0f1b7d1166c07ebd GIT binary patch literal 2801 zcmVnrv1e78#*(UE<&8o^M%`WKWi8RNU9x8xIf@5EC97AK25|x~s5yR75o( z9o*XDz_-CH8W_^c)V;8@N-iqEvbh=+87UYQ=-}ftDJ>ftCg0fI+0fLztgx1flgh!z zv7)A}oufV{C8?gMP(4A&!_81MEylaTxTvhQq^Kty9jBR|U`tO!Ehal8Afu0uzm$8W zcVJaRM=c;En~|Kkk9Lhn_viH2};Ryj2A>FdqP(agurZdqGtNIY9TIY2Tq zJS;9YDjw+R#_*~rYsz{$O_w~&aA!g+{;aB{GNYp8r?WmsBeQB+V!Ohz_3Bq1+P zJuWXMDXEp0n~jo`go=-Xh_iHsiG74)UuauINlZ69H!(I#B_KR17SYDZ%)i6Mw6>y} zq=k2TeRX+$Xl`#_V{uMDLOnfULpVe@DN;Ej4Fd({>kA-q< zcxYy6d|*|DU{PgONGCBzWmhp>GA1=7CBVYL->SL9tFn2Gr>v2ouaTCugpkaAi>HNl zLp@SiNKRf%FTszUo^66gS#+0qbEsW%N=R6VXHG*lNw&Vvp|a0meVtWri8oMeZ8|GT zKN7e7mvI092sTMXK~zY`MU@3x8(9>F??i}=ggEgKZwL`2NCPYcg0{H3TXDDI&{Aj% z)OG7y*WGQmzV5sKXwQW9*>{pjGCA-4a_*g(ASP2YcS(MwT}vvn6t;?{)uWA9zdvVB zwhoog8L9Qgt8d@Pg&Km*#%P%EMA6J>G}2BIO0wHhc*}k8fa7xG)q4_|@nrc)ne0g8 z)yB(`G=va4WYEwG!6pc$k{qeLWt*G*Fp&GE@q52RHZbx_axp|+JL=d(Yyd+DV~Vmx zw1UGRN;cWqInowy>lp5P+25Eak!{|%Q71ck?d`QAj)yo1Z06IO6?6__`l7*Rv$I4# zjjb(yc&Keoc)9=HxsrzV4-);6{exZK{2WRXn;NLMOtzd{or05tNu?>~F+u{$s+&b#7vrf-OQ0x8I z`sHgQIEC;~*N#c7^K48dlHNUa zN~Drzs}QCQDL#u^<1fbA+{12Lsqok_e_iVJSwAJ*`uVja^#+c?TzxG9vLOX#8#3@^ zOKIiI=KYHH9h=*I>APd+jHw4_Q*;qqKd(z3b|QAju~>pZiv_VkW8+y3^Ofy>;?e#4 zgEsfjz>)7Hc4)s(;P~MO|JooZY=rhauNSeid~8Cwe5J^5Y_I&<=hL`t!_yLnjH;^_ z=20aePyNz~Apk5I3~9BUog{`vu0(5ml^yYJ*y61M&dSP4b#*mEAd^U@-rvyBvkcB2 znVHWrF=BaOhB;*G&}cN>nw?Z#HL${2;cz(OWLe72Z#NK*GuN(Quk1v}Bg1Vz}v?j=>O%1&k1& zfLikKeHM;oi$p2}iV6`*Z5DHRM?z?`NHS|crq}5NIt23zh3F=;X7qYpjzG7U@HvPf=Ew#;cWy6@jV;|#sj@?gl2G8^%vWey1p*yq z)Ke5xic9RW5=tk4Jb{3-_Vo6trKP1iT^3A=NRm_%(JJ3Jnhy7bTBo02oIVZXGVD}Q zQBDaZ7KouUO5D49^JDnZ?VDXvDXa@zlX%59JIo% z0EZIWuRs0xacr!sO9Vuc0T{pmA55|66Ib&T`R$RE$>lLZ1Q88)%? z=3=lb=vfRYN+ki)&aC_*N6j$i6#en+YvT$>#(1U0|30HHNv0?&Stg?-w!2lHJ6*wS zTG1*AHe3ZAh9WgXFJmO1&;9mW0fYMe&%e%7Mi~V$vJ!i-eXfc?!NtWYXg@QPB-knp zxoeBmY6dJyk^0-(9C%Y~mlf*;I{0Cr*ozx9MD$`X=*bQ#N*t8ZoT8eV8nD!`8dR}P z4Q~NZzyPMYiQvtys$dmTLNg>>3FvxFQH`3BBM_+d<3@+vD9C~HK%kfDWp<;{SZCkN zBC5I;??y90(ZY#>VR);$CPydG8y&T)`*Ve+yxa*#Zf>pv$|Vws!%>%}QQfV&OX3K1 zY6+EyiLvqc+M1lR`U!cI@U`20%xumRM#$yz2+;P^j%1))S_Qp(3(+$zp$IR_X5*Cx z^;v!Gs=;YGsu%@j-4GG6e}BZ@P9jMu^}roQ>RBj6TC#HY@vZ{l*z{Tb_{5|k%IUoP zCg1FgGRUEtuBVl?rt(p*6wBTx#Sy8zwObA^B@VxGeyqWG??neix;EW-cb>SSQu17_9hFS@laN9Y)Or+X-zOA`3+mdldZEd@v0=&;Iw8us zhfN9tdK3x;T?Ca#0k0uEw_esdCMOL}*M_im73M7}&zg{snwoMj{4&HO0y2@mTQWLoJuZLtHLxe1h9@!JKa}MCJDu6dXDP4oIoibFaDZ6%Ym+ zmxbU19AL4yG^}@%`T2$@Q%_G1C5sB%sh)2T$`1}WF)q-(D%)w7IH8su5Ob=|PUOWzi zCnkau3t5S1Z_Wb&SVa&V^1PXLTl|N%*4EP2*4Dwn>4wNF+q^J(C7RRI)0>+iB_qJ& zL;tWgEf3ypKi)0t@-m|_th>A0N0K9h1APq*rMDN#4u>B;93FT=&l!T!mX^4 \ No newline at end of file diff --git a/src/assets/svgs/404.svg b/src/assets/svgs/404.svg new file mode 100644 index 00000000..5244d8d4 --- /dev/null +++ b/src/assets/svgs/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svgs/500.svg b/src/assets/svgs/500.svg new file mode 100644 index 00000000..9c020927 --- /dev/null +++ b/src/assets/svgs/500.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svgs/icon.svg b/src/assets/svgs/icon.svg new file mode 100644 index 00000000..7024becf --- /dev/null +++ b/src/assets/svgs/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svgs/login-bg.svg b/src/assets/svgs/login-bg.svg new file mode 100644 index 00000000..bbe06c16 --- /dev/null +++ b/src/assets/svgs/login-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svgs/login-box-bg.svg b/src/assets/svgs/login-box-bg.svg new file mode 100644 index 00000000..ab100403 --- /dev/null +++ b/src/assets/svgs/login-box-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svgs/message.svg b/src/assets/svgs/message.svg new file mode 100644 index 00000000..14ca8172 --- /dev/null +++ b/src/assets/svgs/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svgs/money.svg b/src/assets/svgs/money.svg new file mode 100644 index 00000000..c1580de1 --- /dev/null +++ b/src/assets/svgs/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svgs/peoples.svg b/src/assets/svgs/peoples.svg new file mode 100644 index 00000000..aab852e5 --- /dev/null +++ b/src/assets/svgs/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svgs/shopping.svg b/src/assets/svgs/shopping.svg new file mode 100644 index 00000000..f395bc7f --- /dev/null +++ b/src/assets/svgs/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Backtop/index.ts b/src/components/Backtop/index.ts new file mode 100644 index 00000000..96de88d6 --- /dev/null +++ b/src/components/Backtop/index.ts @@ -0,0 +1,3 @@ +import Backtop from './src/Backtop.vue' + +export { Backtop } diff --git a/src/components/Backtop/src/Backtop.vue b/src/components/Backtop/src/Backtop.vue new file mode 100644 index 00000000..f5c2fc44 --- /dev/null +++ b/src/components/Backtop/src/Backtop.vue @@ -0,0 +1,15 @@ + + + diff --git a/src/components/ConfigGlobal/index.ts b/src/components/ConfigGlobal/index.ts new file mode 100644 index 00000000..dda2462c --- /dev/null +++ b/src/components/ConfigGlobal/index.ts @@ -0,0 +1,3 @@ +import ConfigGlobal from './src/ConfigGlobal.vue' + +export { ConfigGlobal } diff --git a/src/components/ConfigGlobal/src/ConfigGlobal.vue b/src/components/ConfigGlobal/src/ConfigGlobal.vue new file mode 100644 index 00000000..10a0a9f2 --- /dev/null +++ b/src/components/ConfigGlobal/src/ConfigGlobal.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/components/ContentDetailWrap/index.ts b/src/components/ContentDetailWrap/index.ts new file mode 100644 index 00000000..1871cac7 --- /dev/null +++ b/src/components/ContentDetailWrap/index.ts @@ -0,0 +1,3 @@ +import ContentDetailWrap from './src/ContentDetailWrap.vue' + +export { ContentDetailWrap } diff --git a/src/components/ContentDetailWrap/src/ContentDetailWrap.vue b/src/components/ContentDetailWrap/src/ContentDetailWrap.vue new file mode 100644 index 00000000..697ea18a --- /dev/null +++ b/src/components/ContentDetailWrap/src/ContentDetailWrap.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/components/ContentWrap/index.ts b/src/components/ContentWrap/index.ts new file mode 100644 index 00000000..8c22cc83 --- /dev/null +++ b/src/components/ContentWrap/index.ts @@ -0,0 +1,3 @@ +import ContentWrap from './src/ContentWrap.vue' + +export { ContentWrap } diff --git a/src/components/ContentWrap/src/ContentWrap.vue b/src/components/ContentWrap/src/ContentWrap.vue new file mode 100644 index 00000000..51e50410 --- /dev/null +++ b/src/components/ContentWrap/src/ContentWrap.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/components/CountTo/index.ts b/src/components/CountTo/index.ts new file mode 100644 index 00000000..2119f023 --- /dev/null +++ b/src/components/CountTo/index.ts @@ -0,0 +1,3 @@ +import CountTo from './src/CountTo.vue' + +export { CountTo } diff --git a/src/components/CountTo/src/CountTo.vue b/src/components/CountTo/src/CountTo.vue new file mode 100644 index 00000000..8d2f1ade --- /dev/null +++ b/src/components/CountTo/src/CountTo.vue @@ -0,0 +1,180 @@ + + + diff --git a/src/components/Crontab/index.ts b/src/components/Crontab/index.ts new file mode 100644 index 00000000..6beeef86 --- /dev/null +++ b/src/components/Crontab/index.ts @@ -0,0 +1,2 @@ +import Crontab from './src/Crontab.vue' +export { Crontab } diff --git a/src/components/Crontab/src/Crontab.vue b/src/components/Crontab/src/Crontab.vue new file mode 100644 index 00000000..fe33bd5f --- /dev/null +++ b/src/components/Crontab/src/Crontab.vue @@ -0,0 +1,998 @@ + + + + diff --git a/src/components/Cropper/index.ts b/src/components/Cropper/index.ts new file mode 100644 index 00000000..8fcc6183 --- /dev/null +++ b/src/components/Cropper/index.ts @@ -0,0 +1,4 @@ +import CropperImage from './src/Cropper.vue' +import CropperAvatar from './src/CropperAvatar.vue' + +export { CropperImage, CropperAvatar } diff --git a/src/components/Cropper/src/CopperModal.vue b/src/components/Cropper/src/CopperModal.vue new file mode 100644 index 00000000..2365e5ec --- /dev/null +++ b/src/components/Cropper/src/CopperModal.vue @@ -0,0 +1,254 @@ + + + diff --git a/src/components/Cropper/src/Cropper.vue b/src/components/Cropper/src/Cropper.vue new file mode 100644 index 00000000..459c97fa --- /dev/null +++ b/src/components/Cropper/src/Cropper.vue @@ -0,0 +1,181 @@ + + + diff --git a/src/components/Cropper/src/CropperAvatar.vue b/src/components/Cropper/src/CropperAvatar.vue new file mode 100644 index 00000000..dc4c7475 --- /dev/null +++ b/src/components/Cropper/src/CropperAvatar.vue @@ -0,0 +1,135 @@ + + + diff --git a/src/components/Cropper/src/types.ts b/src/components/Cropper/src/types.ts new file mode 100644 index 00000000..bcad3b45 --- /dev/null +++ b/src/components/Cropper/src/types.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/Descriptions/index.ts b/src/components/Descriptions/index.ts new file mode 100644 index 00000000..91b0eb41 --- /dev/null +++ b/src/components/Descriptions/index.ts @@ -0,0 +1,3 @@ +import Descriptions from './src/Descriptions.vue' + +export { Descriptions } diff --git a/src/components/Descriptions/src/Descriptions.vue b/src/components/Descriptions/src/Descriptions.vue new file mode 100644 index 00000000..d6cea87a --- /dev/null +++ b/src/components/Descriptions/src/Descriptions.vue @@ -0,0 +1,155 @@ + + + + + diff --git a/src/components/Dialog/index.ts b/src/components/Dialog/index.ts new file mode 100644 index 00000000..1655dadc --- /dev/null +++ b/src/components/Dialog/index.ts @@ -0,0 +1,3 @@ +import Dialog from './src/Dialog.vue' + +export { Dialog } diff --git a/src/components/Dialog/src/Dialog.vue b/src/components/Dialog/src/Dialog.vue new file mode 100644 index 00000000..98336fdf --- /dev/null +++ b/src/components/Dialog/src/Dialog.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/src/components/DictTag/index.ts b/src/components/DictTag/index.ts new file mode 100644 index 00000000..4db27420 --- /dev/null +++ b/src/components/DictTag/index.ts @@ -0,0 +1,3 @@ +import DictTag from './src/DictTag.vue' + +export { DictTag } diff --git a/src/components/DictTag/src/DictTag.vue b/src/components/DictTag/src/DictTag.vue new file mode 100644 index 00000000..f70d52bf --- /dev/null +++ b/src/components/DictTag/src/DictTag.vue @@ -0,0 +1,56 @@ + diff --git a/src/components/Echart/index.ts b/src/components/Echart/index.ts new file mode 100644 index 00000000..48220921 --- /dev/null +++ b/src/components/Echart/index.ts @@ -0,0 +1,3 @@ +import Echart from './src/Echart.vue' + +export { Echart } diff --git a/src/components/Echart/src/Echart.vue b/src/components/Echart/src/Echart.vue new file mode 100644 index 00000000..41fde039 --- /dev/null +++ b/src/components/Echart/src/Echart.vue @@ -0,0 +1,113 @@ + + + diff --git a/src/components/Editor/index.ts b/src/components/Editor/index.ts new file mode 100644 index 00000000..3fbf0a9c --- /dev/null +++ b/src/components/Editor/index.ts @@ -0,0 +1,8 @@ +import Editor from './src/Editor.vue' +import { IDomEditor } from '@wangeditor/editor' + +export interface EditorExpose { + getEditorRef: () => Promise +} + +export { Editor } diff --git a/src/components/Editor/src/Editor.vue b/src/components/Editor/src/Editor.vue new file mode 100644 index 00000000..85b849fb --- /dev/null +++ b/src/components/Editor/src/Editor.vue @@ -0,0 +1,200 @@ + + + + + diff --git a/src/components/Error/index.ts b/src/components/Error/index.ts new file mode 100644 index 00000000..a52c6f97 --- /dev/null +++ b/src/components/Error/index.ts @@ -0,0 +1,3 @@ +import Error from './src/Error.vue' + +export { Error } diff --git a/src/components/Error/src/Error.vue b/src/components/Error/src/Error.vue new file mode 100644 index 00000000..a979bc01 --- /dev/null +++ b/src/components/Error/src/Error.vue @@ -0,0 +1,56 @@ + + + diff --git a/src/components/Form/index.ts b/src/components/Form/index.ts new file mode 100644 index 00000000..484c7a22 --- /dev/null +++ b/src/components/Form/index.ts @@ -0,0 +1,15 @@ +import Form from './src/Form.vue' +import { ElForm } from 'element-plus' +import { FormSchema, FormSetPropsType } from '@/types/form' + +export interface FormExpose { + setValues: (data: Recordable) => void + setProps: (props: Recordable) => void + delSchema: (field: string) => void + addSchema: (formSchema: FormSchema, index?: number) => void + setSchema: (schemaProps: FormSetPropsType[]) => void + formModel: Recordable + getElFormRef: () => ComponentRef +} + +export { Form } diff --git a/src/components/Form/src/Form.vue b/src/components/Form/src/Form.vue new file mode 100644 index 00000000..5e5120c3 --- /dev/null +++ b/src/components/Form/src/Form.vue @@ -0,0 +1,302 @@ + + + diff --git a/src/components/Form/src/componentMap.ts b/src/components/Form/src/componentMap.ts new file mode 100644 index 00000000..5af9b40d --- /dev/null +++ b/src/components/Form/src/componentMap.ts @@ -0,0 +1,55 @@ +import type { Component } from 'vue' +import { + ElCascader, + ElCheckboxGroup, + ElColorPicker, + ElDatePicker, + ElInput, + ElInputNumber, + ElRadioGroup, + ElRate, + ElSelect, + ElSelectV2, + ElTreeSelect, + ElSlider, + ElSwitch, + ElTimePicker, + ElTimeSelect, + ElTransfer, + ElAutocomplete, + ElDivider +} from 'element-plus' +import { InputPassword } from '@/components/InputPassword' +import { Editor } from '@/components/Editor' +import { UploadImg, UploadImgs, UploadFile } from '@/components/UploadFile' +import { ComponentName } from '@/types/components' + +const componentMap: Recordable = { + Radio: ElRadioGroup, + Checkbox: ElCheckboxGroup, + CheckboxButton: ElCheckboxGroup, + Input: ElInput, + Autocomplete: ElAutocomplete, + InputNumber: ElInputNumber, + Select: ElSelect, + Cascader: ElCascader, + Switch: ElSwitch, + Slider: ElSlider, + TimePicker: ElTimePicker, + DatePicker: ElDatePicker, + Rate: ElRate, + ColorPicker: ElColorPicker, + Transfer: ElTransfer, + Divider: ElDivider, + TimeSelect: ElTimeSelect, + SelectV2: ElSelectV2, + TreeSelect: ElTreeSelect, + RadioButton: ElRadioGroup, + InputPassword: InputPassword, + Editor: Editor, + UploadImg: UploadImg, + UploadImgs: UploadImgs, + UploadFile: UploadFile +} + +export { componentMap } diff --git a/src/components/Form/src/components/useRenderCheckbox.tsx b/src/components/Form/src/components/useRenderCheckbox.tsx new file mode 100644 index 00000000..e1518395 --- /dev/null +++ b/src/components/Form/src/components/useRenderCheckbox.tsx @@ -0,0 +1,26 @@ +import { FormSchema } from '@/types/form' +import { ElCheckbox, ElCheckboxButton } from 'element-plus' +import { defineComponent } from 'vue' + +export const useRenderCheckbox = () => { + const renderCheckboxOptions = (item: FormSchema) => { + // 如果有别名,就取别名 + const labelAlias = item?.componentProps?.optionsAlias?.labelField + const valueAlias = item?.componentProps?.optionsAlias?.valueField + const Com = (item.component === 'Checkbox' ? ElCheckbox : ElCheckboxButton) as ReturnType< + typeof defineComponent + > + return item?.componentProps?.options?.map((option) => { + const { ...other } = option + return ( + + {option[labelAlias || 'label']} + + ) + }) + } + + return { + renderCheckboxOptions + } +} diff --git a/src/components/Form/src/components/useRenderRadio.tsx b/src/components/Form/src/components/useRenderRadio.tsx new file mode 100644 index 00000000..d1005ca5 --- /dev/null +++ b/src/components/Form/src/components/useRenderRadio.tsx @@ -0,0 +1,26 @@ +import { FormSchema } from '@/types/form' +import { ElRadio, ElRadioButton } from 'element-plus' +import { defineComponent } from 'vue' + +export const useRenderRadio = () => { + const renderRadioOptions = (item: FormSchema) => { + // 如果有别名,就取别名 + const labelAlias = item?.componentProps?.optionsAlias?.labelField + const valueAlias = item?.componentProps?.optionsAlias?.valueField + const Com = (item.component === 'Radio' ? ElRadio : ElRadioButton) as ReturnType< + typeof defineComponent + > + return item?.componentProps?.options?.map((option) => { + const { ...other } = option + return ( + + {option[labelAlias || 'label']} + + ) + }) + } + + return { + renderRadioOptions + } +} diff --git a/src/components/Form/src/components/useRenderSelect.tsx b/src/components/Form/src/components/useRenderSelect.tsx new file mode 100644 index 00000000..59b72e68 --- /dev/null +++ b/src/components/Form/src/components/useRenderSelect.tsx @@ -0,0 +1,57 @@ +import { FormSchema } from '@/types/form' +import { ComponentOptions } from '@/types/components' +import { ElOption, ElOptionGroup } from 'element-plus' +import { getSlot } from '@/utils/tsxHelper' +import { Slots } from 'vue' + +export const useRenderSelect = (slots: Slots) => { + // 渲染 select options + const renderSelectOptions = (item: FormSchema) => { + // 如果有别名,就取别名 + const labelAlias = item?.componentProps?.optionsAlias?.labelField + return item?.componentProps?.options?.map((option) => { + if (option?.options?.length) { + return ( + + {() => { + return option?.options?.map((v) => { + return renderSelectOptionItem(item, v) + }) + }} + + ) + } else { + return renderSelectOptionItem(item, option) + } + }) + } + + // 渲染 select option item + const renderSelectOptionItem = (item: FormSchema, option: ComponentOptions) => { + // 如果有别名,就取别名 + const labelAlias = item?.componentProps?.optionsAlias?.labelField + const valueAlias = item?.componentProps?.optionsAlias?.valueField + + const { label, value, ...other } = option + + return ( + + {{ + default: () => + // option 插槽名规则,{field}-option + item?.componentProps?.optionsSlot + ? getSlot(slots, `${item.field}-option`, { item: option }) + : undefined + }} + + ) + } + + return { + renderSelectOptions + } +} diff --git a/src/components/Form/src/helper.ts b/src/components/Form/src/helper.ts new file mode 100644 index 00000000..e2549cbd --- /dev/null +++ b/src/components/Form/src/helper.ts @@ -0,0 +1,148 @@ +import type { Slots } from 'vue' +import { getSlot } from '@/utils/tsxHelper' +import { PlaceholderMoel } from './types' +import { FormSchema } from '@/types/form' +import { ColProps } from '@/types/components' + +/** + * + * @param schema 对应组件数据 + * @returns 返回提示信息对象 + * @description 用于自动设置placeholder + */ +export const setTextPlaceholder = (schema: FormSchema): PlaceholderMoel => { + const { t } = useI18n() + const textMap = ['Input', 'Autocomplete', 'InputNumber', 'InputPassword'] + const selectMap = ['Select', 'SelectV2', 'TimePicker', 'DatePicker', 'TimeSelect', 'TimeSelect'] + if (textMap.includes(schema?.component as string)) { + return { + placeholder: t('common.inputText') + } + } + if (selectMap.includes(schema?.component as string)) { + // 一些范围选择器 + const twoTextMap = ['datetimerange', 'daterange', 'monthrange', 'datetimerange', 'daterange'] + if ( + twoTextMap.includes( + (schema?.componentProps?.type || schema?.componentProps?.isRange) as string + ) + ) { + return { + startPlaceholder: t('common.startTimeText'), + endPlaceholder: t('common.endTimeText'), + rangeSeparator: '-' + } + } else { + return { + placeholder: t('common.selectText') + } + } + } + return {} +} + +/** + * + * @param col 内置栅格 + * @returns 返回栅格属性 + * @description 合并传入进来的栅格属性 + */ +export const setGridProp = (col: ColProps = {}): ColProps => { + const colProps: ColProps = { + // 如果有span,代表用户优先级更高,所以不需要默认栅格 + ...(col.span + ? {} + : { + xs: 24, + sm: 12, + md: 12, + lg: 12, + xl: 12 + }), + ...col + } + return colProps +} + +/** + * + * @param item 传入的组件属性 + * @returns 默认添加 clearable 属性 + */ +export const setComponentProps = (item: FormSchema): Recordable => { + const notNeedClearable = ['ColorPicker'] + const componentProps: Recordable = notNeedClearable.includes(item.component as string) + ? { ...item.componentProps } + : { + clearable: true, + ...item.componentProps + } + // 需要删除额外的属性 + delete componentProps?.slots + return componentProps +} + +/** + * + * @param slots 插槽 + * @param slotsProps 插槽属性 + * @param field 字段名 + */ +export const setItemComponentSlots = ( + slots: Slots, + slotsProps: Recordable = {}, + field: string +): Recordable => { + const slotObj: Recordable = {} + for (const key in slotsProps) { + if (slotsProps[key]) { + // 由于组件有可能重复,需要有一个唯一的前缀 + slotObj[key] = (data: Recordable) => { + return getSlot(slots, `${field}-${key}`, data) + } + } + } + return slotObj +} + +/** + * + * @param schema Form表单结构化数组 + * @param formModel FormMoel + * @returns FormMoel + * @description 生成对应的formModel + */ +export const initModel = (schema: FormSchema[], formModel: Recordable) => { + const model: Recordable = { ...formModel } + schema.map((v) => { + // 如果是hidden,就删除对应的值 + if (v.hidden) { + delete model[v.field] + } else if (v.component && v.component !== 'Divider') { + const hasField = Reflect.has(model, v.field) + // 如果先前已经有值存在,则不进行重新赋值,而是采用现有的值 + model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : '' + } + }) + return model +} + +/** + * @param slots 插槽 + * @param field 字段名 + * @returns 返回FormIiem插槽 + */ +export const setFormItemSlots = (slots: Slots, field: string): Recordable => { + const slotObj: Recordable = {} + if (slots[`${field}-error`]) { + slotObj['error'] = (data: Recordable) => { + return getSlot(slots, `${field}-error`, data) + } + } + if (slots[`${field}-label`]) { + slotObj['label'] = (data: Recordable) => { + return getSlot(slots, `${field}-label`, data) + } + } + return slotObj +} diff --git a/src/components/Form/src/types.ts b/src/components/Form/src/types.ts new file mode 100644 index 00000000..92a49d85 --- /dev/null +++ b/src/components/Form/src/types.ts @@ -0,0 +1,17 @@ +import { FormSchema } from '@/types/form' + +export interface PlaceholderMoel { + placeholder?: string + startPlaceholder?: string + endPlaceholder?: string + rangeSeparator?: string +} + +export type FormProps = { + schema?: FormSchema[] + isCol?: boolean + model?: Recordable + autoSetPlaceholder?: boolean + isCustom?: boolean + labelWidth?: string | number +} & Recordable diff --git a/src/components/Highlight/index.ts b/src/components/Highlight/index.ts new file mode 100644 index 00000000..3e2d9ed6 --- /dev/null +++ b/src/components/Highlight/index.ts @@ -0,0 +1,3 @@ +import Highlight from './src/Highlight.vue' + +export { Highlight } diff --git a/src/components/Highlight/src/Highlight.vue b/src/components/Highlight/src/Highlight.vue new file mode 100644 index 00000000..ef923a9a --- /dev/null +++ b/src/components/Highlight/src/Highlight.vue @@ -0,0 +1,65 @@ + diff --git a/src/components/IFrame/index.ts b/src/components/IFrame/index.ts new file mode 100644 index 00000000..9f8cf24a --- /dev/null +++ b/src/components/IFrame/index.ts @@ -0,0 +1,3 @@ +import IFrame from './src/IFrame.vue' + +export { IFrame } diff --git a/src/components/IFrame/src/IFrame.vue b/src/components/IFrame/src/IFrame.vue new file mode 100644 index 00000000..522da3cc --- /dev/null +++ b/src/components/IFrame/src/IFrame.vue @@ -0,0 +1,30 @@ + + diff --git a/src/components/Icon/index.ts b/src/components/Icon/index.ts new file mode 100644 index 00000000..33d1de38 --- /dev/null +++ b/src/components/Icon/index.ts @@ -0,0 +1,4 @@ +import Icon from './src/Icon.vue' +import IconSelect from './src/IconSelect.vue' + +export { Icon, IconSelect } diff --git a/src/components/Icon/src/Icon.vue b/src/components/Icon/src/Icon.vue new file mode 100644 index 00000000..bd94fcd4 --- /dev/null +++ b/src/components/Icon/src/Icon.vue @@ -0,0 +1,83 @@ + + + diff --git a/src/components/Icon/src/IconSelect.vue b/src/components/Icon/src/IconSelect.vue new file mode 100644 index 00000000..97a3e384 --- /dev/null +++ b/src/components/Icon/src/IconSelect.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/src/components/Icon/src/data.ts b/src/components/Icon/src/data.ts new file mode 100644 index 00000000..2a4ed5a3 --- /dev/null +++ b/src/components/Icon/src/data.ts @@ -0,0 +1,1961 @@ +export const IconJson = { + 'ep:': [ + 'add-location', + 'aim', + 'alarm-clock', + 'apple', + 'arrow-down', + 'arrow-down-bold', + 'arrow-left', + 'arrow-left-bold', + 'arrow-right', + 'arrow-right-bold', + 'arrow-up', + 'arrow-up-bold', + 'avatar', + 'back', + 'baseball', + 'basketball', + 'bell', + 'bell-filled', + 'bicycle', + 'bottom', + 'bottom-left', + 'bottom-right', + 'bowl', + 'box', + 'briefcase', + 'brush', + 'brush-filled', + 'burger', + 'calendar', + 'camera', + 'camera-filled', + 'caret-bottom', + 'caret-left', + 'caret-right', + 'caret-top', + 'cellphone', + 'chat-dot-round', + 'chat-dot-square', + 'chat-line-round', + 'chat-line-square', + 'chat-round', + 'chat-square', + 'check', + 'checked', + 'cherry', + 'chicken', + 'circle-check', + 'circle-check-filled', + 'circle-close', + 'circle-close-filled', + 'circle-plus', + 'circle-plus-filled', + 'clock', + 'close', + 'close-bold', + 'cloudy', + 'coffee', + 'coffee-cup', + 'coin', + 'cold-drink', + 'collection', + 'collection-tag', + 'comment', + 'compass', + 'connection', + 'coordinate', + 'copy-document', + 'cpu', + 'credit-card', + 'crop', + 'd-arrow-left', + 'd-arrow-right', + 'd-caret', + 'data-analysis', + 'data-board', + 'data-line', + 'delete', + 'delete-filled', + 'delete-location', + 'dessert', + 'discount', + 'dish', + 'dish-dot', + 'document', + 'document-add', + 'document-checked', + 'document-copy', + 'document-delete', + 'document-remove', + 'download', + 'drizzling', + 'edit', + 'edit-pen', + 'eleme', + 'eleme-filled', + 'expand', + 'failed', + 'female', + 'files', + 'film', + 'filter', + 'finished', + 'first-aid-kit', + 'flag', + 'fold', + 'folder', + 'folder-add', + 'folder-checked', + 'folder-delete', + 'folder-opened', + 'folder-remove', + 'food', + 'football', + 'fork-spoon', + 'fries', + 'full-screen', + 'goblet', + 'goblet-full', + 'goblet-square', + 'goblet-square-full', + 'goods', + 'goods-filled', + 'grape', + 'grid', + 'guide', + 'headset', + 'help', + 'help-filled', + 'histogram', + 'home-filled', + 'hot-water', + 'house', + 'ice-cream', + 'ice-cream-round', + 'ice-cream-square', + 'ice-drink', + 'ice-tea', + 'info-filled', + 'iphone', + 'key', + 'knife-fork', + 'lightning', + 'link', + 'list', + 'loading', + 'location', + 'location-filled', + 'location-information', + 'lock', + 'lollipop', + 'magic-stick', + 'magnet', + 'male', + 'management', + 'map-location', + 'medal', + 'menu', + 'message', + 'message-box', + 'mic', + 'microphone', + 'milk-tea', + 'minus', + 'money', + 'monitor', + 'moon', + 'moon-night', + 'more', + 'more-filled', + 'mostly-cloudy', + 'mouse', + 'mug', + 'mute', + 'mute-notification', + 'no-smoking', + 'notebook', + 'notification', + 'odometer', + 'office-building', + 'open', + 'operation', + 'opportunity', + 'orange', + 'paperclip', + 'partly-cloudy', + 'pear', + 'phone', + 'phone-filled', + 'picture', + 'picture-filled', + 'picture-rounded', + 'pie-chart', + 'place', + 'platform', + 'plus', + 'pointer', + 'position', + 'postcard', + 'pouring', + 'present', + 'price-tag', + 'printer', + 'promotion', + 'question-filled', + 'rank', + 'reading', + 'reading-lamp', + 'refresh', + 'refresh-left', + 'refresh-right', + 'refrigerator', + 'remove', + 'remove-filled', + 'right', + 'scale-to-original', + 'school', + 'scissor', + 'search', + 'select', + 'sell', + 'semi-select', + 'service', + 'set-up', + 'setting', + 'share', + 'ship', + 'shop', + 'shopping-bag', + 'shopping-cart', + 'shopping-cart-full', + 'smoking', + 'soccer', + 'sold-out', + 'sort', + 'sort-down', + 'sort-up', + 'stamp', + 'star', + 'star-filled', + 'stopwatch', + 'success-filled', + 'sugar', + 'suitcase', + 'sunny', + 'sunrise', + 'sunset', + 'switch', + 'switch-button', + 'takeaway-box', + 'ticket', + 'tickets', + 'timer', + 'toilet-paper', + 'tools', + 'top', + 'top-left', + 'top-right', + 'trend-charts', + 'trophy', + 'turn-off', + 'umbrella', + 'unlock', + 'upload', + 'upload-filled', + 'user', + 'user-filled', + 'van', + 'video-camera', + 'video-camera-filled', + 'video-pause', + 'video-play', + 'view', + 'wallet', + 'wallet-filled', + 'warning', + 'warning-filled', + 'watch', + 'watermelon', + 'wind-power', + 'zoom-in', + 'zoom-out' + ], + 'fa:': [ + '500px', + 'address-book', + 'address-book-o', + 'address-card', + 'address-card-o', + 'adjust', + 'adn', + 'align-center', + 'align-justify', + 'align-left', + 'amazon', + 'ambulance', + 'american-sign-language-interpreting', + 'anchor', + 'android', + 'angellist', + 'angle-double-left', + 'angle-double-up', + 'angle-down', + 'angle-left', + 'angle-up', + 'apple', + 'archive', + 'area-chart', + 'arrow-circle-left', + 'arrow-circle-o-left', + 'arrow-circle-o-up', + 'arrow-circle-up', + 'arrow-left', + 'arrow-up', + 'arrows', + 'arrows-alt', + 'arrows-h', + 'arrows-v', + 'assistive-listening-systems', + 'asterisk', + 'at', + 'audio-description', + 'automobile', + 'backward', + 'balance-scale', + 'ban', + 'bandcamp', + 'bank', + 'bar-chart', + 'barcode', + 'bars', + 'bath', + 'battery', + 'battery-0', + 'battery-1', + 'battery-2', + 'battery-3', + 'bed', + 'beer', + 'behance', + 'behance-square', + 'bell', + 'bell-o', + 'bell-slash', + 'bell-slash-o', + 'bicycle', + 'binoculars', + 'birthday-cake', + 'bitbucket', + 'bitbucket-square', + 'bitcoin', + 'black-tie', + 'blind', + 'bluetooth', + 'bluetooth-b', + 'bold', + 'bolt', + 'bomb', + 'book', + 'bookmark', + 'bookmark-o', + 'braille', + 'briefcase', + 'bug', + 'building', + 'building-o', + 'bullhorn', + 'bullseye', + 'bus', + 'buysellads', + 'cab', + 'calculator', + 'calendar', + 'calendar-check-o', + 'calendar-minus-o', + 'calendar-o', + 'calendar-plus-o', + 'calendar-times-o', + 'camera', + 'camera-retro', + 'caret-down', + 'caret-left', + 'caret-square-o-left', + 'caret-square-o-up', + 'caret-up', + 'cart-arrow-down', + 'cart-plus', + 'cc', + 'cc-amex', + 'cc-diners-club', + 'cc-discover', + 'cc-jcb', + 'cc-mastercard', + 'cc-paypal', + 'cc-stripe', + 'cc-visa', + 'certificate', + 'chain', + 'chain-broken', + 'check', + 'check-circle', + 'check-circle-o', + 'check-square', + 'check-square-o', + 'chevron-circle-left', + 'chevron-circle-up', + 'chevron-down', + 'chevron-left', + 'chevron-up', + 'child', + 'chrome', + 'circle', + 'circle-o', + 'circle-o-notch', + 'circle-thin', + 'clipboard', + 'clock-o', + 'clone', + 'close', + 'cloud', + 'cloud-download', + 'cloud-upload', + 'cny', + 'code', + 'code-fork', + 'codepen', + 'codiepie', + 'coffee', + 'cog', + 'cogs', + 'columns', + 'comment', + 'comment-o', + 'commenting', + 'commenting-o', + 'comments', + 'comments-o', + 'compass', + 'compress', + 'connectdevelop', + 'contao', + 'copy', + 'copyright', + 'creative-commons', + 'credit-card', + 'credit-card-alt', + 'crop', + 'crosshairs', + 'css3', + 'cube', + 'cubes', + 'cut', + 'cutlery', + 'dashboard', + 'dashcube', + 'database', + 'deaf', + 'dedent', + 'delicious', + 'desktop', + 'deviantart', + 'diamond', + 'digg', + 'dollar', + 'dot-circle-o', + 'download', + 'dribbble', + 'drivers-license', + 'drivers-license-o', + 'dropbox', + 'drupal', + 'edge', + 'edit', + 'eercast', + 'eject', + 'ellipsis-h', + 'ellipsis-v', + 'empire', + 'envelope', + 'envelope-o', + 'envelope-open', + 'envelope-open-o', + 'envelope-square', + 'envira', + 'eraser', + 'etsy', + 'eur', + 'exchange', + 'exclamation', + 'exclamation-circle', + 'exclamation-triangle', + 'expand', + 'expeditedssl', + 'external-link', + 'external-link-square', + 'eye', + 'eye-slash', + 'eyedropper', + 'fa', + 'facebook', + 'facebook-official', + 'facebook-square', + 'fast-backward', + 'fax', + 'feed', + 'female', + 'fighter-jet', + 'file', + 'file-archive-o', + 'file-audio-o', + 'file-code-o', + 'file-excel-o', + 'file-image-o', + 'file-movie-o', + 'file-o', + 'file-pdf-o', + 'file-powerpoint-o', + 'file-text', + 'file-text-o', + 'file-word-o', + 'film', + 'filter', + 'fire', + 'fire-extinguisher', + 'firefox', + 'first-order', + 'flag', + 'flag-checkered', + 'flag-o', + 'flask', + 'flickr', + 'floppy-o', + 'folder', + 'folder-o', + 'folder-open', + 'folder-open-o', + 'font', + 'fonticons', + 'fort-awesome', + 'forumbee', + 'foursquare', + 'free-code-camp', + 'frown-o', + 'futbol-o', + 'gamepad', + 'gavel', + 'gbp', + 'genderless', + 'get-pocket', + 'gg', + 'gg-circle', + 'gift', + 'git', + 'git-square', + 'github', + 'github-alt', + 'github-square', + 'gitlab', + 'gittip', + 'glass', + 'glide', + 'glide-g', + 'globe', + 'google', + 'google-plus', + 'google-plus-circle', + 'google-plus-square', + 'google-wallet', + 'graduation-cap', + 'grav', + 'group', + 'h-square', + 'hacker-news', + 'hand-grab-o', + 'hand-lizard-o', + 'hand-o-left', + 'hand-o-up', + 'hand-paper-o', + 'hand-peace-o', + 'hand-pointer-o', + 'hand-scissors-o', + 'hand-spock-o', + 'handshake-o', + 'hashtag', + 'hdd-o', + 'header', + 'headphones', + 'heart', + 'heart-o', + 'heartbeat', + 'history', + 'home', + 'hospital-o', + 'hourglass', + 'hourglass-1', + 'hourglass-2', + 'hourglass-3', + 'hourglass-o', + 'houzz', + 'html5', + 'i-cursor', + 'id-badge', + 'ils', + 'image', + 'imdb', + 'inbox', + 'indent', + 'industry', + 'info', + 'info-circle', + 'inr', + 'instagram', + 'internet-explorer', + 'intersex', + 'ioxhost', + 'italic', + 'joomla', + 'jsfiddle', + 'key', + 'keyboard-o', + 'krw', + 'language', + 'laptop', + 'lastfm', + 'lastfm-square', + 'leaf', + 'leanpub', + 'lemon-o', + 'level-up', + 'life-bouy', + 'lightbulb-o', + 'line-chart', + 'linkedin', + 'linkedin-square', + 'linode', + 'linux', + 'list', + 'list-alt', + 'list-ol', + 'list-ul', + 'location-arrow', + 'lock', + 'long-arrow-left', + 'long-arrow-up', + 'low-vision', + 'magic', + 'magnet', + 'mail-forward', + 'mail-reply', + 'mail-reply-all', + 'male', + 'map', + 'map-marker', + 'map-o', + 'map-pin', + 'map-signs', + 'mars', + 'mars-double', + 'mars-stroke', + 'mars-stroke-h', + 'mars-stroke-v', + 'maxcdn', + 'meanpath', + 'medium', + 'medkit', + 'meetup', + 'meh-o', + 'mercury', + 'microchip', + 'microphone', + 'microphone-slash', + 'minus', + 'minus-circle', + 'minus-square', + 'minus-square-o', + 'mixcloud', + 'mobile', + 'modx', + 'money', + 'moon-o', + 'motorcycle', + 'mouse-pointer', + 'music', + 'neuter', + 'newspaper-o', + 'object-group', + 'object-ungroup', + 'odnoklassniki', + 'odnoklassniki-square', + 'opencart', + 'openid', + 'opera', + 'optin-monster', + 'pagelines', + 'paint-brush', + 'paper-plane', + 'paper-plane-o', + 'paperclip', + 'paragraph', + 'pause', + 'pause-circle', + 'pause-circle-o', + 'paw', + 'paypal', + 'pencil', + 'pencil-square', + 'percent', + 'phone', + 'phone-square', + 'pie-chart', + 'pied-piper', + 'pied-piper-alt', + 'pied-piper-pp', + 'pinterest', + 'pinterest-p', + 'pinterest-square', + 'plane', + 'play', + 'play-circle', + 'play-circle-o', + 'plug', + 'plus', + 'plus-circle', + 'plus-square', + 'plus-square-o', + 'podcast', + 'power-off', + 'print', + 'product-hunt', + 'puzzle-piece', + 'qq', + 'qrcode', + 'question', + 'question-circle', + 'question-circle-o', + 'quora', + 'quote-left', + 'quote-right', + 'ra', + 'random', + 'ravelry', + 'recycle', + 'reddit', + 'reddit-alien', + 'reddit-square', + 'refresh', + 'registered', + 'renren', + 'repeat', + 'retweet', + 'road', + 'rocket', + 'rotate-left', + 'rouble', + 'rss-square', + 'safari', + 'scribd', + 'search', + 'search-minus', + 'search-plus', + 'sellsy', + 'server', + 'share-alt', + 'share-alt-square', + 'share-square', + 'share-square-o', + 'shield', + 'ship', + 'shirtsinbulk', + 'shopping-bag', + 'shopping-basket', + 'shopping-cart', + 'shower', + 'sign-in', + 'sign-language', + 'sign-out', + 'signal', + 'simplybuilt', + 'sitemap', + 'skyatlas', + 'skype', + 'slack', + 'sliders', + 'slideshare', + 'smile-o', + 'snapchat', + 'snapchat-ghost', + 'snapchat-square', + 'snowflake-o', + 'sort', + 'sort-alpha-asc', + 'sort-alpha-desc', + 'sort-amount-asc', + 'sort-amount-desc', + 'sort-asc', + 'sort-numeric-asc', + 'sort-numeric-desc', + 'soundcloud', + 'space-shuttle', + 'spinner', + 'spoon', + 'spotify', + 'square', + 'square-o', + 'stack-exchange', + 'stack-overflow', + 'star', + 'star-half', + 'star-half-empty', + 'star-o', + 'steam', + 'steam-square', + 'step-backward', + 'stethoscope', + 'sticky-note', + 'sticky-note-o', + 'stop', + 'stop-circle', + 'stop-circle-o', + 'street-view', + 'strikethrough', + 'stumbleupon', + 'stumbleupon-circle', + 'subscript', + 'subway', + 'suitcase', + 'sun-o', + 'superpowers', + 'superscript', + 'table', + 'tablet', + 'tag', + 'tags', + 'tasks', + 'telegram', + 'television', + 'tencent-weibo', + 'terminal', + 'text-height', + 'text-width', + 'th', + 'th-large', + 'th-list', + 'themeisle', + 'thermometer', + 'thermometer-0', + 'thermometer-1', + 'thermometer-2', + 'thermometer-3', + 'thumb-tack', + 'thumbs-down', + 'thumbs-o-up', + 'thumbs-up', + 'ticket', + 'times-circle', + 'times-circle-o', + 'times-rectangle', + 'times-rectangle-o', + 'tint', + 'toggle-off', + 'toggle-on', + 'trademark', + 'train', + 'transgender-alt', + 'trash', + 'trash-o', + 'tree', + 'trello', + 'tripadvisor', + 'trophy', + 'truck', + 'try', + 'tty', + 'tumblr', + 'tumblr-square', + 'twitch', + 'twitter', + 'twitter-square', + 'umbrella', + 'underline', + 'universal-access', + 'unlock', + 'unlock-alt', + 'upload', + 'usb', + 'user', + 'user-circle', + 'user-circle-o', + 'user-md', + 'user-o', + 'user-plus', + 'user-secret', + 'user-times', + 'venus', + 'venus-double', + 'venus-mars', + 'viacoin', + 'viadeo', + 'viadeo-square', + 'video-camera', + 'vimeo', + 'vimeo-square', + 'vine', + 'vk', + 'volume-control-phone', + 'volume-down', + 'volume-off', + 'volume-up', + 'wechat', + 'weibo', + 'whatsapp', + 'wheelchair', + 'wheelchair-alt', + 'wifi', + 'wikipedia-w', + 'window-maximize', + 'window-minimize', + 'window-restore', + 'windows', + 'wordpress', + 'wpbeginner', + 'wpexplorer', + 'wpforms', + 'wrench', + 'xing', + 'xing-square', + 'y-combinator', + 'yahoo', + 'yelp', + 'yoast', + 'youtube', + 'youtube-play', + 'youtube-square' + ], + 'fa-solid:': [ + 'abacus', + 'ad', + 'address-book', + 'address-card', + 'adjust', + 'air-freshener', + 'align-center', + 'align-justify', + 'align-left', + 'align-right', + 'allergies', + 'ambulance', + 'american-sign-language-interpreting', + 'anchor', + 'angle-double-down', + 'angle-double-left', + 'angle-double-right', + 'angle-double-up', + 'angle-down', + 'angle-left', + 'angle-right', + 'angle-up', + 'angry', + 'ankh', + 'apple-alt', + 'archive', + 'archway', + 'arrow-alt-circle-down', + 'arrow-alt-circle-left', + 'arrow-alt-circle-right', + 'arrow-alt-circle-up', + 'arrow-circle-down', + 'arrow-circle-left', + 'arrow-circle-right', + 'arrow-circle-up', + 'arrow-down', + 'arrow-left', + 'arrow-right', + 'arrow-up', + 'arrows-alt', + 'arrows-alt-h', + 'arrows-alt-v', + 'assistive-listening-systems', + 'asterisk', + 'at', + 'atlas', + 'atom', + 'audio-description', + 'award', + 'baby', + 'baby-carriage', + 'backspace', + 'backward', + 'bacon', + 'bacteria', + 'bacterium', + 'bahai', + 'balance-scale', + 'balance-scale-left', + 'balance-scale-right', + 'ban', + 'band-aid', + 'barcode', + 'bars', + 'baseball-ball', + 'basketball-ball', + 'bath', + 'battery-empty', + 'battery-full', + 'battery-half', + 'battery-quarter', + 'battery-three-quarters', + 'bed', + 'beer', + 'bell', + 'bell-slash', + 'bezier-curve', + 'bible', + 'bicycle', + 'biking', + 'binoculars', + 'biohazard', + 'birthday-cake', + 'blender', + 'blender-phone', + 'blind', + 'blog', + 'bold', + 'bolt', + 'bomb', + 'bone', + 'bong', + 'book', + 'book-dead', + 'book-medical', + 'book-open', + 'book-reader', + 'bookmark', + 'border-all', + 'border-none', + 'border-style', + 'bowling-ball', + 'box', + 'box-open', + 'box-tissue', + 'boxes', + 'braille', + 'brain', + 'bread-slice', + 'briefcase', + 'briefcase-medical', + 'broadcast-tower', + 'broom', + 'brush', + 'bug', + 'building', + 'bullhorn', + 'bullseye', + 'burn', + 'bus', + 'bus-alt', + 'business-time', + 'calculator', + 'calculator-alt', + 'calendar', + 'calendar-alt', + 'calendar-check', + 'calendar-day', + 'calendar-minus', + 'calendar-plus', + 'calendar-times', + 'calendar-week', + 'camera', + 'camera-retro', + 'campground', + 'candy-cane', + 'cannabis', + 'capsules', + 'car', + 'car-alt', + 'car-battery', + 'car-crash', + 'car-side', + 'caravan', + 'caret-down', + 'caret-left', + 'caret-right', + 'caret-square-down', + 'caret-square-left', + 'caret-square-right', + 'caret-square-up', + 'caret-up', + 'carrot', + 'cart-arrow-down', + 'cart-plus', + 'cash-register', + 'cat', + 'certificate', + 'chair', + 'chalkboard', + 'chalkboard-teacher', + 'charging-station', + 'chart-area', + 'chart-bar', + 'chart-line', + 'chart-pie', + 'check', + 'check-circle', + 'check-double', + 'check-square', + 'cheese', + 'chess', + 'chess-bishop', + 'chess-board', + 'chess-king', + 'chess-knight', + 'chess-pawn', + 'chess-queen', + 'chess-rook', + 'chevron-circle-down', + 'chevron-circle-left', + 'chevron-circle-right', + 'chevron-circle-up', + 'chevron-down', + 'chevron-left', + 'chevron-right', + 'chevron-up', + 'child', + 'church', + 'circle', + 'circle-notch', + 'city', + 'clinic-medical', + 'clipboard', + 'clipboard-check', + 'clipboard-list', + 'clock', + 'clone', + 'closed-captioning', + 'cloud', + 'cloud-download-alt', + 'cloud-meatball', + 'cloud-moon', + 'cloud-moon-rain', + 'cloud-rain', + 'cloud-showers-heavy', + 'cloud-sun', + 'cloud-sun-rain', + 'cloud-upload-alt', + 'cocktail', + 'code', + 'code-branch', + 'coffee', + 'cog', + 'cogs', + 'coins', + 'columns', + 'comment', + 'comment-alt', + 'comment-dollar', + 'comment-dots', + 'comment-medical', + 'comment-slash', + 'comments', + 'comments-dollar', + 'compact-disc', + 'compass', + 'compress', + 'compress-alt', + 'compress-arrows-alt', + 'concierge-bell', + 'cookie', + 'cookie-bite', + 'copy', + 'copyright', + 'couch', + 'credit-card', + 'crop', + 'crop-alt', + 'cross', + 'crosshairs', + 'crow', + 'crown', + 'crutch', + 'cube', + 'cubes', + 'cut', + 'database', + 'deaf', + 'democrat', + 'desktop', + 'dharmachakra', + 'diagnoses', + 'dice', + 'dice-d20', + 'dice-d6', + 'dice-five', + 'dice-four', + 'dice-one', + 'dice-six', + 'dice-three', + 'dice-two', + 'digital-tachograph', + 'directions', + 'disease', + 'divide', + 'dizzy', + 'dna', + 'dog', + 'dollar-sign', + 'dolly', + 'dolly-flatbed', + 'donate', + 'door-closed', + 'door-open', + 'dot-circle', + 'dove', + 'download', + 'drafting-compass', + 'dragon', + 'draw-polygon', + 'drum', + 'drum-steelpan', + 'drumstick-bite', + 'dumbbell', + 'dumpster', + 'dumpster-fire', + 'dungeon', + 'edit', + 'egg', + 'eject', + 'ellipsis-h', + 'ellipsis-v', + 'empty-set', + 'envelope', + 'envelope-open', + 'envelope-open-text', + 'envelope-square', + 'equals', + 'eraser', + 'ethernet', + 'euro-sign', + 'exchange-alt', + 'exclamation', + 'exclamation-circle', + 'exclamation-triangle', + 'expand', + 'expand-alt', + 'expand-arrows-alt', + 'external-link-alt', + 'external-link-square-alt', + 'eye', + 'eye-dropper', + 'eye-slash', + 'fan', + 'fast-backward', + 'fast-forward', + 'faucet', + 'fax', + 'feather', + 'feather-alt', + 'female', + 'fighter-jet', + 'file', + 'file-alt', + 'file-archive', + 'file-audio', + 'file-code', + 'file-contract', + 'file-csv', + 'file-download', + 'file-excel', + 'file-export', + 'file-image', + 'file-import', + 'file-invoice', + 'file-invoice-dollar', + 'file-medical', + 'file-medical-alt', + 'file-pdf', + 'file-powerpoint', + 'file-prescription', + 'file-signature', + 'file-upload', + 'file-video', + 'file-word', + 'fill', + 'fill-drip', + 'film', + 'filter', + 'fingerprint', + 'fire', + 'fire-alt', + 'fire-extinguisher', + 'first-aid', + 'fish', + 'fist-raised', + 'flag', + 'flag-checkered', + 'flag-usa', + 'flask', + 'flushed', + 'folder', + 'folder-minus', + 'folder-open', + 'folder-plus', + 'font', + 'football-ball', + 'forward', + 'frog', + 'frown', + 'frown-open', + 'function', + 'funnel-dollar', + 'futbol', + 'gamepad', + 'gas-pump', + 'gavel', + 'gem', + 'genderless', + 'ghost', + 'gift', + 'gifts', + 'glass-cheers', + 'glass-martini', + 'glass-martini-alt', + 'glass-whiskey', + 'glasses', + 'globe', + 'globe-africa', + 'globe-americas', + 'globe-asia', + 'globe-europe', + 'golf-ball', + 'gopuram', + 'graduation-cap', + 'greater-than', + 'greater-than-equal', + 'grimace', + 'grin', + 'grin-alt', + 'grin-beam', + 'grin-beam-sweat', + 'grin-hearts', + 'grin-squint', + 'grin-squint-tears', + 'grin-stars', + 'grin-tears', + 'grin-tongue', + 'grin-tongue-squint', + 'grin-tongue-wink', + 'grin-wink', + 'grip-horizontal', + 'grip-lines', + 'grip-lines-vertical', + 'grip-vertical', + 'guitar', + 'h-square', + 'hamburger', + 'hammer', + 'hamsa', + 'hand-holding', + 'hand-holding-heart', + 'hand-holding-medical', + 'hand-holding-usd', + 'hand-holding-water', + 'hand-lizard', + 'hand-middle-finger', + 'hand-paper', + 'hand-peace', + 'hand-point-down', + 'hand-point-left', + 'hand-point-right', + 'hand-point-up', + 'hand-pointer', + 'hand-rock', + 'hand-scissors', + 'hand-sparkles', + 'hand-spock', + 'hands', + 'hands-helping', + 'hands-wash', + 'handshake', + 'handshake-alt-slash', + 'handshake-slash', + 'hanukiah', + 'hard-hat', + 'hashtag', + 'hat-cowboy', + 'hat-cowboy-side', + 'hat-wizard', + 'hdd', + 'head-side-cough', + 'head-side-cough-slash', + 'head-side-mask', + 'head-side-virus', + 'heading', + 'headphones', + 'headphones-alt', + 'headset', + 'heart', + 'heart-broken', + 'heartbeat', + 'helicopter', + 'highlighter', + 'hiking', + 'hippo', + 'history', + 'hockey-puck', + 'holly-berry', + 'home', + 'horse', + 'horse-head', + 'hospital', + 'hospital-alt', + 'hospital-symbol', + 'hospital-user', + 'hot-tub', + 'hotdog', + 'hotel', + 'hourglass', + 'hourglass-end', + 'hourglass-half', + 'hourglass-start', + 'house-damage', + 'house-user', + 'hryvnia', + 'i-cursor', + 'ice-cream', + 'icicles', + 'icons', + 'id-badge', + 'id-card', + 'id-card-alt', + 'igloo', + 'image', + 'images', + 'inbox', + 'indent', + 'industry', + 'infinity', + 'info', + 'info-circle', + 'integral', + 'intersection', + 'italic', + 'jedi', + 'joint', + 'journal-whills', + 'kaaba', + 'key', + 'keyboard', + 'khanda', + 'kiss', + 'kiss-beam', + 'kiss-wink-heart', + 'kiwi-bird', + 'lambda', + 'landmark', + 'language', + 'laptop', + 'laptop-code', + 'laptop-house', + 'laptop-medical', + 'laugh', + 'laugh-beam', + 'laugh-squint', + 'laugh-wink', + 'layer-group', + 'leaf', + 'lemon', + 'less-than', + 'less-than-equal', + 'level-down-alt', + 'level-up-alt', + 'life-ring', + 'lightbulb', + 'link', + 'lira-sign', + 'list', + 'list-alt', + 'list-ol', + 'list-ul', + 'location-arrow', + 'lock', + 'lock-open', + 'long-arrow-alt-down', + 'long-arrow-alt-left', + 'long-arrow-alt-right', + 'long-arrow-alt-up', + 'low-vision', + 'luggage-cart', + 'lungs', + 'lungs-virus', + 'magic', + 'magnet', + 'mail-bulk', + 'male', + 'map', + 'map-marked', + 'map-marked-alt', + 'map-marker', + 'map-marker-alt', + 'map-pin', + 'map-signs', + 'marker', + 'mars', + 'mars-double', + 'mars-stroke', + 'mars-stroke-h', + 'mars-stroke-v', + 'mask', + 'medal', + 'medkit', + 'meh', + 'meh-blank', + 'meh-rolling-eyes', + 'memory', + 'menorah', + 'mercury', + 'meteor', + 'microchip', + 'microphone', + 'microphone-alt', + 'microphone-alt-slash', + 'microphone-slash', + 'microscope', + 'minus', + 'minus-circle', + 'minus-square', + 'mitten', + 'mobile', + 'mobile-alt', + 'money-bill', + 'money-bill-alt', + 'money-bill-wave', + 'money-bill-wave-alt', + 'money-check', + 'money-check-alt', + 'monument', + 'moon', + 'mortar-pestle', + 'mosque', + 'motorcycle', + 'mountain', + 'mouse', + 'mouse-pointer', + 'mug-hot', + 'music', + 'network-wired', + 'neuter', + 'newspaper', + 'not-equal', + 'notes-medical', + 'object-group', + 'object-ungroup', + 'oil-can', + 'om', + 'omega', + 'otter', + 'outdent', + 'pager', + 'paint-brush', + 'paint-roller', + 'palette', + 'pallet', + 'paper-plane', + 'paperclip', + 'parachute-box', + 'paragraph', + 'parking', + 'passport', + 'pastafarianism', + 'paste', + 'pause', + 'pause-circle', + 'paw', + 'peace', + 'pen', + 'pen-alt', + 'pen-fancy', + 'pen-nib', + 'pen-square', + 'pencil-alt', + 'pencil-ruler', + 'people-arrows', + 'people-carry', + 'pepper-hot', + 'percent', + 'percentage', + 'person-booth', + 'phone', + 'phone-alt', + 'phone-slash', + 'phone-square', + 'phone-square-alt', + 'phone-volume', + 'photo-video', + 'pi', + 'piggy-bank', + 'pills', + 'pizza-slice', + 'place-of-worship', + 'plane', + 'plane-arrival', + 'plane-departure', + 'plane-slash', + 'play', + 'play-circle', + 'plug', + 'plus', + 'plus-circle', + 'plus-square', + 'podcast', + 'poll', + 'poll-h', + 'poo', + 'poo-storm', + 'poop', + 'portrait', + 'pound-sign', + 'power-off', + 'pray', + 'praying-hands', + 'prescription', + 'prescription-bottle', + 'prescription-bottle-alt', + 'print', + 'procedures', + 'project-diagram', + 'pump-medical', + 'pump-soap', + 'puzzle-piece', + 'qrcode', + 'question', + 'question-circle', + 'quidditch', + 'quote-left', + 'quote-right', + 'quran', + 'radiation', + 'radiation-alt', + 'rainbow', + 'random', + 'receipt', + 'record-vinyl', + 'recycle', + 'redo', + 'redo-alt', + 'registered', + 'remove-format', + 'reply', + 'reply-all', + 'republican', + 'restroom', + 'retweet', + 'ribbon', + 'ring', + 'road', + 'robot', + 'rocket', + 'route', + 'rss', + 'rss-square', + 'ruble-sign', + 'ruler', + 'ruler-combined', + 'ruler-horizontal', + 'ruler-vertical', + 'running', + 'rupee-sign', + 'sad-cry', + 'sad-tear', + 'satellite', + 'satellite-dish', + 'save', + 'school', + 'screwdriver', + 'scroll', + 'sd-card', + 'search', + 'search-dollar', + 'search-location', + 'search-minus', + 'search-plus', + 'seedling', + 'server', + 'shapes', + 'share', + 'share-alt', + 'share-alt-square', + 'share-square', + 'shekel-sign', + 'shield-alt', + 'shield-virus', + 'ship', + 'shipping-fast', + 'shoe-prints', + 'shopping-bag', + 'shopping-basket', + 'shopping-cart', + 'shower', + 'shuttle-van', + 'sigma', + 'sign', + 'sign-in-alt', + 'sign-language', + 'sign-out-alt', + 'signal', + 'signal-alt', + 'signal-alt-slash', + 'signal-slash', + 'signature', + 'sim-card', + 'sink', + 'sitemap', + 'skating', + 'skiing', + 'skiing-nordic', + 'skull', + 'skull-crossbones', + 'slash', + 'sleigh', + 'sliders-h', + 'smile', + 'smile-beam', + 'smile-wink', + 'smog', + 'smoking', + 'smoking-ban', + 'sms', + 'snowboarding', + 'snowflake', + 'snowman', + 'snowplow', + 'soap', + 'socks', + 'solar-panel', + 'sort', + 'sort-alpha-down', + 'sort-alpha-down-alt', + 'sort-alpha-up', + 'sort-alpha-up-alt', + 'sort-amount-down', + 'sort-amount-down-alt', + 'sort-amount-up', + 'sort-amount-up-alt', + 'sort-down', + 'sort-numeric-down', + 'sort-numeric-down-alt', + 'sort-numeric-up', + 'sort-numeric-up-alt', + 'sort-up', + 'spa', + 'space-shuttle', + 'spell-check', + 'spider', + 'spinner', + 'splotch', + 'spray-can', + 'square', + 'square-full', + 'square-root', + 'square-root-alt', + 'stamp', + 'star', + 'star-and-crescent', + 'star-half', + 'star-half-alt', + 'star-of-david', + 'star-of-life', + 'step-backward', + 'step-forward', + 'stethoscope', + 'sticky-note', + 'stop', + 'stop-circle', + 'stopwatch', + 'stopwatch-20', + 'store', + 'store-alt', + 'store-alt-slash', + 'store-slash', + 'stream', + 'street-view', + 'strikethrough', + 'stroopwafel', + 'subscript', + 'subway', + 'suitcase', + 'suitcase-rolling', + 'sun', + 'superscript', + 'surprise', + 'swatchbook', + 'swimmer', + 'swimming-pool', + 'synagogue', + 'sync', + 'sync-alt', + 'syringe', + 'table', + 'table-tennis', + 'tablet', + 'tablet-alt', + 'tablets', + 'tachometer-alt', + 'tag', + 'tags', + 'tally', + 'tape', + 'tasks', + 'taxi', + 'teeth', + 'teeth-open', + 'temperature-high', + 'temperature-low', + 'tenge', + 'terminal', + 'text-height', + 'text-width', + 'th', + 'th-large', + 'th-list', + 'theater-masks', + 'thermometer', + 'thermometer-empty', + 'thermometer-full', + 'thermometer-half', + 'thermometer-quarter', + 'thermometer-three-quarters', + 'theta', + 'thumbs-down', + 'thumbs-up', + 'thumbtack', + 'ticket-alt', + 'tilde', + 'times', + 'times-circle', + 'tint', + 'tint-slash', + 'tired', + 'toggle-off', + 'toggle-on', + 'toilet', + 'toilet-paper', + 'toilet-paper-slash', + 'toolbox', + 'tools', + 'tooth', + 'torah', + 'torii-gate', + 'tractor', + 'trademark', + 'traffic-light', + 'trailer', + 'train', + 'tram', + 'transgender', + 'transgender-alt', + 'trash', + 'trash-alt', + 'trash-restore', + 'trash-restore-alt', + 'tree', + 'trophy', + 'truck', + 'truck-loading', + 'truck-monster', + 'truck-moving', + 'truck-pickup', + 'tshirt', + 'tty', + 'tv', + 'umbrella', + 'umbrella-beach', + 'underline', + 'undo', + 'undo-alt', + 'union', + 'universal-access', + 'university', + 'unlink', + 'unlock', + 'unlock-alt', + 'upload', + 'user', + 'user-alt', + 'user-alt-slash', + 'user-astronaut', + 'user-check', + 'user-circle', + 'user-clock', + 'user-cog', + 'user-edit', + 'user-friends', + 'user-graduate', + 'user-injured', + 'user-lock', + 'user-md', + 'user-minus', + 'user-ninja', + 'user-nurse', + 'user-plus', + 'user-secret', + 'user-shield', + 'user-slash', + 'user-tag', + 'user-tie', + 'user-times', + 'users', + 'users-cog', + 'users-slash', + 'utensil-spoon', + 'utensils', + 'value-absolute', + 'vector-square', + 'venus', + 'venus-double', + 'venus-mars', + 'vest', + 'vest-patches', + 'vial', + 'vials', + 'video', + 'video-slash', + 'vihara', + 'virus', + 'virus-slash', + 'viruses', + 'voicemail', + 'volleyball-ball', + 'volume', + 'volume-down', + 'volume-mute', + 'volume-off', + 'volume-slash', + 'volume-up', + 'vote-yea', + 'vr-cardboard', + 'walking', + 'wallet', + 'warehouse', + 'water', + 'wave-square', + 'weight', + 'weight-hanging', + 'wheelchair', + 'wifi', + 'wifi-slash', + 'wind', + 'window-close', + 'window-maximize', + 'window-minimize', + 'window-restore', + 'wine-bottle', + 'wine-glass', + 'wine-glass-alt', + 'won-sign', + 'wrench', + 'x-ray', + 'yen-sign', + 'yin-yang' + ] +} diff --git a/src/components/ImageViewer/index.ts b/src/components/ImageViewer/index.ts new file mode 100644 index 00000000..38681356 --- /dev/null +++ b/src/components/ImageViewer/index.ts @@ -0,0 +1,33 @@ +import ImageViewer from './src/ImageViewer.vue' +import { isClient } from '@/utils/is' +import { createVNode, render, VNode } from 'vue' +import { ImageViewerProps } from './src/types' + +let instance: Nullable = null + +export function createImageViewer(options: ImageViewerProps) { + if (!isClient) return + const { + urlList, + initialIndex = 0, + infinite = true, + hideOnClickModal = false, + appendToBody = false, + zIndex = 2000, + show = true + } = options + + const propsData: Partial = {} + const container = document.createElement('div') + propsData.urlList = urlList + propsData.initialIndex = initialIndex + propsData.infinite = infinite + propsData.hideOnClickModal = hideOnClickModal + propsData.appendToBody = appendToBody + propsData.zIndex = zIndex + propsData.show = show + + document.body.appendChild(container) + instance = createVNode(ImageViewer, propsData) + render(instance, container) +} diff --git a/src/components/ImageViewer/src/ImageViewer.vue b/src/components/ImageViewer/src/ImageViewer.vue new file mode 100644 index 00000000..a2ddc072 --- /dev/null +++ b/src/components/ImageViewer/src/ImageViewer.vue @@ -0,0 +1,33 @@ + + + diff --git a/src/components/ImageViewer/src/types.ts b/src/components/ImageViewer/src/types.ts new file mode 100644 index 00000000..1932d74d --- /dev/null +++ b/src/components/ImageViewer/src/types.ts @@ -0,0 +1,9 @@ +export interface ImageViewerProps { + urlList?: string[] + zIndex?: number + initialIndex?: number + infinite?: boolean + hideOnClickModal?: boolean + appendToBody?: boolean + show?: boolean +} diff --git a/src/components/Infotip/index.ts b/src/components/Infotip/index.ts new file mode 100644 index 00000000..413fa5f4 --- /dev/null +++ b/src/components/Infotip/index.ts @@ -0,0 +1,3 @@ +import Infotip from './src/Infotip.vue' + +export { Infotip } diff --git a/src/components/Infotip/src/Infotip.vue b/src/components/Infotip/src/Infotip.vue new file mode 100644 index 00000000..a03f34e2 --- /dev/null +++ b/src/components/Infotip/src/Infotip.vue @@ -0,0 +1,52 @@ + + + diff --git a/src/components/InputPassword/index.ts b/src/components/InputPassword/index.ts new file mode 100644 index 00000000..1dcc38e9 --- /dev/null +++ b/src/components/InputPassword/index.ts @@ -0,0 +1,3 @@ +import InputPassword from './src/InputPassword.vue' + +export { InputPassword } diff --git a/src/components/InputPassword/src/InputPassword.vue b/src/components/InputPassword/src/InputPassword.vue new file mode 100644 index 00000000..9e82cd41 --- /dev/null +++ b/src/components/InputPassword/src/InputPassword.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/src/components/Qrcode/index.ts b/src/components/Qrcode/index.ts new file mode 100644 index 00000000..ce461612 --- /dev/null +++ b/src/components/Qrcode/index.ts @@ -0,0 +1,3 @@ +import Qrcode from './src/Qrcode.vue' + +export { Qrcode } diff --git a/src/components/Qrcode/src/Qrcode.vue b/src/components/Qrcode/src/Qrcode.vue new file mode 100644 index 00000000..0ef59ba7 --- /dev/null +++ b/src/components/Qrcode/src/Qrcode.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/src/components/Search/index.ts b/src/components/Search/index.ts new file mode 100644 index 00000000..fcc6f163 --- /dev/null +++ b/src/components/Search/index.ts @@ -0,0 +1,3 @@ +import Search from './src/Search.vue' + +export { Search } diff --git a/src/components/Search/src/Search.vue b/src/components/Search/src/Search.vue new file mode 100644 index 00000000..ffff5681 --- /dev/null +++ b/src/components/Search/src/Search.vue @@ -0,0 +1,144 @@ + + + diff --git a/src/components/Sticky/index.ts b/src/components/Sticky/index.ts new file mode 100644 index 00000000..5e1de45e --- /dev/null +++ b/src/components/Sticky/index.ts @@ -0,0 +1,3 @@ +import Sticky from './src/Sticky.vue' + +export { Sticky } diff --git a/src/components/Sticky/src/Sticky.vue b/src/components/Sticky/src/Sticky.vue new file mode 100644 index 00000000..5d668059 --- /dev/null +++ b/src/components/Sticky/src/Sticky.vue @@ -0,0 +1,140 @@ + + diff --git a/src/components/Table/index.ts b/src/components/Table/index.ts new file mode 100644 index 00000000..689f64a8 --- /dev/null +++ b/src/components/Table/index.ts @@ -0,0 +1,12 @@ +import Table from './src/Table.vue' +import { ElTable } from 'element-plus' +import { TableSetPropsType } from '@/types/table' + +export interface TableExpose { + setProps: (props: Recordable) => void + setColumn: (columnProps: TableSetPropsType[]) => void + selections: Recordable[] + elTableRef: ComponentRef +} + +export { Table } diff --git a/src/components/Table/src/Table.vue b/src/components/Table/src/Table.vue new file mode 100644 index 00000000..66969c99 --- /dev/null +++ b/src/components/Table/src/Table.vue @@ -0,0 +1,307 @@ + + diff --git a/src/components/Table/src/helper.ts b/src/components/Table/src/helper.ts new file mode 100644 index 00000000..d8b34a8a --- /dev/null +++ b/src/components/Table/src/helper.ts @@ -0,0 +1,8 @@ +export const setIndex = (reserveIndex: boolean, index: number, size: number, current: number) => { + const newIndex = index + 1 + if (reserveIndex) { + return size * (current - 1) + newIndex + } else { + return newIndex + } +} diff --git a/src/components/Table/src/types.ts b/src/components/Table/src/types.ts new file mode 100644 index 00000000..1c7ff765 --- /dev/null +++ b/src/components/Table/src/types.ts @@ -0,0 +1,26 @@ +import { Pagination, TableColumn } from '@/types/table' + +export type TableProps = { + pageSize?: number + currentPage?: number + // 是否多选 + selection?: boolean + // 是否所有的超出隐藏,优先级低于schema中的showOverflowTooltip, + showOverflowTooltip?: boolean + // 表头 + columns?: TableColumn[] + // 是否展示分页 + pagination?: Pagination | undefined + // 仅对 type=selection 的列有效,类型为 Boolean,为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key) + reserveSelection?: boolean + // 加载状态 + loading?: boolean + // 是否叠加索引 + reserveIndex?: boolean + // 对齐方式 + align?: 'left' | 'center' | 'right' + // 表头对齐方式 + headerAlign?: 'left' | 'center' | 'right' + data?: Recordable + expand?: boolean +} & Recordable diff --git a/src/components/Tooltip/index.ts b/src/components/Tooltip/index.ts new file mode 100644 index 00000000..ab66ddff --- /dev/null +++ b/src/components/Tooltip/index.ts @@ -0,0 +1,3 @@ +import Tooltip from './src/Tooltip.vue' + +export { Tooltip } diff --git a/src/components/Tooltip/src/Tooltip.vue b/src/components/Tooltip/src/Tooltip.vue new file mode 100644 index 00000000..cad1cd29 --- /dev/null +++ b/src/components/Tooltip/src/Tooltip.vue @@ -0,0 +1,14 @@ + + diff --git a/src/components/UploadFile/index.ts b/src/components/UploadFile/index.ts new file mode 100644 index 00000000..97c1d665 --- /dev/null +++ b/src/components/UploadFile/index.ts @@ -0,0 +1,5 @@ +import UploadImg from './src/UploadImg.vue' +import UploadImgs from './src/UploadImgs.vue' +import UploadFile from './src/UploadFile.vue' + +export { UploadImg, UploadImgs, UploadFile } diff --git a/src/components/UploadFile/src/UploadFile.vue b/src/components/UploadFile/src/UploadFile.vue new file mode 100644 index 00000000..d02ad826 --- /dev/null +++ b/src/components/UploadFile/src/UploadFile.vue @@ -0,0 +1,164 @@ + + + diff --git a/src/components/UploadFile/src/UploadImg.vue b/src/components/UploadFile/src/UploadImg.vue new file mode 100644 index 00000000..a350528c --- /dev/null +++ b/src/components/UploadFile/src/UploadImg.vue @@ -0,0 +1,251 @@ + + + + diff --git a/src/components/UploadFile/src/UploadImgs.vue b/src/components/UploadFile/src/UploadImgs.vue new file mode 100644 index 00000000..82e5030c --- /dev/null +++ b/src/components/UploadFile/src/UploadImgs.vue @@ -0,0 +1,277 @@ + + + + diff --git a/src/components/Verifition/index.ts b/src/components/Verifition/index.ts new file mode 100644 index 00000000..bcfe6d94 --- /dev/null +++ b/src/components/Verifition/index.ts @@ -0,0 +1,3 @@ +import Verify from './src/Verify.vue' + +export { Verify } diff --git a/src/components/Verifition/src/Verify.vue b/src/components/Verifition/src/Verify.vue new file mode 100644 index 00000000..6bfc353e --- /dev/null +++ b/src/components/Verifition/src/Verify.vue @@ -0,0 +1,443 @@ + + + diff --git a/src/components/Verifition/src/Verify/VerifyPoints.vue b/src/components/Verifition/src/Verify/VerifyPoints.vue new file mode 100644 index 00000000..8eeb83f6 --- /dev/null +++ b/src/components/Verifition/src/Verify/VerifyPoints.vue @@ -0,0 +1,250 @@ + + diff --git a/src/components/Verifition/src/Verify/VerifySlide.vue b/src/components/Verifition/src/Verify/VerifySlide.vue new file mode 100644 index 00000000..6b6108df --- /dev/null +++ b/src/components/Verifition/src/Verify/VerifySlide.vue @@ -0,0 +1,376 @@ + + diff --git a/src/components/Verifition/src/Verify/index.ts b/src/components/Verifition/src/Verify/index.ts new file mode 100644 index 00000000..0daa63a5 --- /dev/null +++ b/src/components/Verifition/src/Verify/index.ts @@ -0,0 +1,4 @@ +import VerifySlide from './VerifySlide.vue' +import VerifyPoints from './VerifyPoints.vue' + +export { VerifySlide, VerifyPoints } diff --git a/src/components/Verifition/src/utils/ase.ts b/src/components/Verifition/src/utils/ase.ts new file mode 100644 index 00000000..d2e6b988 --- /dev/null +++ b/src/components/Verifition/src/utils/ase.ts @@ -0,0 +1,14 @@ +import CryptoJS from 'crypto-js' +/** + * @word 要加密的内容 + * @keyWord String 服务器随机返回的关键字 + * */ +export function aesEncrypt(word, keyWord = 'XwKsGlMcdPMEhR1B') { + const key = CryptoJS.enc.Utf8.parse(keyWord) + const srcs = CryptoJS.enc.Utf8.parse(word) + const encrypted = CryptoJS.AES.encrypt(srcs, key, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }) + return encrypted.toString() +} diff --git a/src/components/Verifition/src/utils/util.ts b/src/components/Verifition/src/utils/util.ts new file mode 100644 index 00000000..15c16270 --- /dev/null +++ b/src/components/Verifition/src/utils/util.ts @@ -0,0 +1,97 @@ +export function resetSize(vm) { + let img_width, img_height, bar_width, bar_height //图片的宽度、高度,移动条的宽度、高度 + const EmployeeWindow = window as any + const parentWidth = vm.$el.parentNode.offsetWidth || EmployeeWindow.offsetWidth + const parentHeight = vm.$el.parentNode.offsetHeight || EmployeeWindow.offsetHeight + if (vm.imgSize.width.indexOf('%') != -1) { + img_width = (parseInt(vm.imgSize.width) / 100) * parentWidth + 'px' + } else { + img_width = vm.imgSize.width + } + + if (vm.imgSize.height.indexOf('%') != -1) { + img_height = (parseInt(vm.imgSize.height) / 100) * parentHeight + 'px' + } else { + img_height = vm.imgSize.height + } + + if (vm.barSize.width.indexOf('%') != -1) { + bar_width = (parseInt(vm.barSize.width) / 100) * parentWidth + 'px' + } else { + bar_width = vm.barSize.width + } + + if (vm.barSize.height.indexOf('%') != -1) { + bar_height = (parseInt(vm.barSize.height) / 100) * parentHeight + 'px' + } else { + bar_height = vm.barSize.height + } + + return { imgWidth: img_width, imgHeight: img_height, barWidth: bar_width, barHeight: bar_height } +} + +export const _code_chars = [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z' +] +export const _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0'] +export const _code_color2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC'] diff --git a/src/components/XButton/index.ts b/src/components/XButton/index.ts new file mode 100644 index 00000000..be0f0d4f --- /dev/null +++ b/src/components/XButton/index.ts @@ -0,0 +1,4 @@ +import XButton from './src/XButton.vue' +import XTextButton from './src/XTextButton.vue' + +export { XButton, XTextButton } diff --git a/src/components/XButton/src/XButton.vue b/src/components/XButton/src/XButton.vue new file mode 100644 index 00000000..3ef71230 --- /dev/null +++ b/src/components/XButton/src/XButton.vue @@ -0,0 +1,47 @@ + + + + diff --git a/src/components/XButton/src/XTextButton.vue b/src/components/XButton/src/XTextButton.vue new file mode 100644 index 00000000..a256a985 --- /dev/null +++ b/src/components/XButton/src/XTextButton.vue @@ -0,0 +1,46 @@ + + + + diff --git a/src/components/XModal/index.ts b/src/components/XModal/index.ts new file mode 100644 index 00000000..65a72bd7 --- /dev/null +++ b/src/components/XModal/index.ts @@ -0,0 +1,3 @@ +import XModal from './src/XModal.vue' + +export { XModal } diff --git a/src/components/XModal/src/XModal.vue b/src/components/XModal/src/XModal.vue new file mode 100644 index 00000000..b69c95ba --- /dev/null +++ b/src/components/XModal/src/XModal.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/components/XTable/index.ts b/src/components/XTable/index.ts new file mode 100644 index 00000000..4abe968e --- /dev/null +++ b/src/components/XTable/index.ts @@ -0,0 +1,3 @@ +import XTable from './src/XTable.vue' + +export { XTable } diff --git a/src/components/XTable/src/XTable.vue b/src/components/XTable/src/XTable.vue new file mode 100644 index 00000000..34f91ce0 --- /dev/null +++ b/src/components/XTable/src/XTable.vue @@ -0,0 +1,419 @@ + + + diff --git a/src/components/XTable/src/style/dark.scss b/src/components/XTable/src/style/dark.scss new file mode 100644 index 00000000..d981b041 --- /dev/null +++ b/src/components/XTable/src/style/dark.scss @@ -0,0 +1,81 @@ +// 修改样式变量 +//@import 'vxe-table/styles/variable.scss'; + +/*font*/ +$vxe-font-color: #e5e7eb; +// $vxe-font-size: 14px !default; +// $vxe-font-size-medium: 16px !default; +// $vxe-font-size-small: 14px !default; +// $vxe-font-size-mini: 12px !default; + +/*color*/ +$vxe-primary-color: #409eff !default; +$vxe-success-color: #67c23a !default; +$vxe-info-color: #909399 !default; +$vxe-warning-color: #e6a23c !default; +$vxe-danger-color: #f56c6c !default; +$vxe-disabled-color: #bfbfbf !default; +$vxe-primary-disabled-color: #c0c4cc !default; + +/*loading*/ +$vxe-loading-color: $vxe-primary-color !default; +$vxe-loading-background-color: #1d1e1f !default; +$vxe-loading-z-index: 999 !default; + +/*icon*/ +$vxe-icon-font-family: Verdana, Arial, Tahoma !default; +$vxe-icon-background-color: #e5e7eb !default; + +/*toolbar*/ +$vxe-toolbar-background-color: #1d1e1f !default; +$vxe-toolbar-button-border: #dcdfe6 !default; +$vxe-toolbar-custom-active-background-color: #d9dadb !default; +$vxe-toolbar-panel-background-color: #e5e7eb !default; + +$vxe-table-font-color: #e5e7eb; +$vxe-table-header-background-color: #1d1e1f; +$vxe-table-body-background-color: #141414; +$vxe-table-row-striped-background-color: #1d1d1d; +$vxe-table-row-hover-background-color: #1d1e1f; +$vxe-table-row-hover-striped-background-color: #1e1e1e; +$vxe-table-footer-background-color: #1d1e1f; +$vxe-table-row-current-background-color: #302d2d; +$vxe-table-column-current-background-color: #302d2d; +$vxe-table-column-hover-background-color: #302d2d; +$vxe-table-row-hover-current-background-color: #302d2d; +$vxe-table-row-checkbox-checked-background-color: #3e3c37 !default; +$vxe-table-row-hover-checkbox-checked-background-color: #615a4a !default; +$vxe-table-menu-background-color: #1d1e1f; +$vxe-table-border-width: 1px !default; +$vxe-table-border-color: #4c4d4f !default; +$vxe-table-fixed-left-scrolling-box-shadow: 8px 0px 10px -5px rgba(0, 0, 0, 0.12) !default; +$vxe-table-fixed-right-scrolling-box-shadow: -8px 0px 10px -5px rgba(0, 0, 0, 0.12) !default; + +$vxe-form-background-color: #141414; + +/*pager*/ +$vxe-pager-background-color: #1d1e1f !default; +$vxe-pager-perfect-background-color: #262727 !default; +$vxe-pager-perfect-button-background-color: #a7a3a3 !default; + +$vxe-input-background-color: #141414; +$vxe-input-border-color: #4c4d4f !default; + +$vxe-select-option-hover-background-color: #262626 !default; +$vxe-select-panel-background-color: #141414 !default; +$vxe-select-empty-color: #262626 !default; +$vxe-optgroup-title-color: #909399 !default; + +/*button*/ +$vxe-button-default-background-color: #262626; +$vxe-button-dropdown-panel-background-color: #141414; + +/*modal*/ +$vxe-modal-header-background-color: #141414; +$vxe-modal-body-background-color: #141414; +$vxe-modal-border-color: #3b3b3b; + +/*pulldown*/ +$vxe-pulldown-panel-background-color: #262626 !default; + +@import 'vxe-table/styles/index.scss'; diff --git a/src/components/XTable/src/style/index.scss b/src/components/XTable/src/style/index.scss new file mode 100644 index 00000000..e37af37a --- /dev/null +++ b/src/components/XTable/src/style/index.scss @@ -0,0 +1,6 @@ +// @import 'vxe-table/styles/variable.scss'; +// @import 'vxe-table/styles/modules.scss'; +// @import './theme/light.scss'; +i { + border-color: initial; +} diff --git a/src/components/XTable/src/style/light.scss b/src/components/XTable/src/style/light.scss new file mode 100644 index 00000000..f2f1309f --- /dev/null +++ b/src/components/XTable/src/style/light.scss @@ -0,0 +1,16 @@ +// 修改样式变量 +// /*font*/ +// $vxe-font-size: 12px !default; +// $vxe-font-size-medium: 16px !default; +// $vxe-font-size-small: 14px !default; +// $vxe-font-size-mini: 12px !default; +/*color*/ +$vxe-primary-color: #409eff !default; +$vxe-success-color: #67c23a !default; +$vxe-info-color: #909399 !default; +$vxe-warning-color: #e6a23c !default; +$vxe-danger-color: #f56c6c !default; +$vxe-disabled-color: #bfbfbf !default; +$vxe-primary-disabled-color: #c0c4cc !default; + +@import 'vxe-table/styles/index.scss'; diff --git a/src/components/XTable/src/type.ts b/src/components/XTable/src/type.ts new file mode 100644 index 00000000..5e5251e4 --- /dev/null +++ b/src/components/XTable/src/type.ts @@ -0,0 +1,26 @@ +import { CrudSchema } from '@/hooks/web/useCrudSchemas' +import type { VxeGridProps, VxeGridPropTypes, VxeTablePropTypes } from 'vxe-table' + +export type XTableProps = VxeGridProps & { + allSchemas?: CrudSchema + height?: number // 高度 默认730 + topActionSlots?: boolean // 是否开启表格内顶部操作栏插槽 + treeConfig?: VxeTablePropTypes.TreeConfig // 树形表单配置 + isList?: boolean // 是否不带分页的list + getListApi?: Function // 获取列表接口 + getAllListApi?: Function // 获取全部数据接口 用于 vxe 导出 + deleteApi?: Function // 删除接口 + deleteListApi?: Function // 批量删除接口 + exportListApi?: Function // 导出接口 + exportName?: string // 导出文件夹名称 + params?: any // 其他查询参数 + pagination?: boolean | VxeGridPropTypes.PagerConfig // 分页配置参数 + toolBar?: boolean | VxeGridPropTypes.ToolbarConfig // 右侧工具栏配置参数 +} +export type XColumns = VxeGridPropTypes.Columns + +export type VxeTableColumn = { + field: string + title?: string + children?: VxeTableColumn[] +} & Recordable diff --git a/src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue b/src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue new file mode 100644 index 00000000..1f471f3e --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue @@ -0,0 +1,698 @@ + + + diff --git a/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue b/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue new file mode 100644 index 00000000..a206c45b --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue @@ -0,0 +1,595 @@ + + + + + diff --git a/src/components/bpmnProcessDesigner/package/designer/index.ts b/src/components/bpmnProcessDesigner/package/designer/index.ts new file mode 100644 index 00000000..85228468 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/index.ts @@ -0,0 +1,8 @@ +import MyProcessDesigner from './ProcessDesigner.vue' + +MyProcessDesigner.install = function (Vue) { + Vue.component(MyProcessDesigner.name, MyProcessDesigner) +} + +// 流程图的设计器,可编辑 +export default MyProcessDesigner diff --git a/src/components/bpmnProcessDesigner/package/designer/index2.ts b/src/components/bpmnProcessDesigner/package/designer/index2.ts new file mode 100644 index 00000000..ebe8ca78 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/index2.ts @@ -0,0 +1,8 @@ +import MyProcessViewer from './ProcessViewer.vue' + +MyProcessViewer.install = function (Vue) { + Vue.component(MyProcessViewer.name, MyProcessViewer) +} + +// 流程图的查看器,不可编辑 +export default MyProcessViewer diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/contentPadProvider.js b/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/contentPadProvider.js new file mode 100644 index 00000000..87834931 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/contentPadProvider.js @@ -0,0 +1,423 @@ +import { assign, forEach, isArray } from 'min-dash' + +import { is } from 'bpmn-js/lib/util/ModelUtil' + +import { isExpanded, isEventSubProcess } from 'bpmn-js/lib/util/DiUtil' + +import { isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil' + +import { getChildLanes } from 'bpmn-js/lib/features/modeling/util/LaneUtil' + +import { hasPrimaryModifier } from 'diagram-js/lib/util/Mouse' + +/** + * A provider for BPMN 2.0 elements context pad + */ +export default function ContextPadProvider( + config, + injector, + eventBus, + contextPad, + modeling, + elementFactory, + connect, + create, + popupMenu, + canvas, + rules, + translate +) { + config = config || {} + + contextPad.registerProvider(this) + + this._contextPad = contextPad + + this._modeling = modeling + + this._elementFactory = elementFactory + this._connect = connect + this._create = create + this._popupMenu = popupMenu + this._canvas = canvas + this._rules = rules + this._translate = translate + + if (config.autoPlace !== false) { + this._autoPlace = injector.get('autoPlace', false) + } + + eventBus.on('create.end', 250, function (event) { + const context = event.context, + shape = context.shape + + if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) { + return + } + + const entries = contextPad.getEntries(shape) + + if (entries.replace) { + entries.replace.action.click(event, shape) + } + }) +} + +ContextPadProvider.$inject = [ + 'config.contextPad', + 'injector', + 'eventBus', + 'contextPad', + 'modeling', + 'elementFactory', + 'connect', + 'create', + 'popupMenu', + 'canvas', + 'rules', + 'translate', + 'elementRegistry' +] + +ContextPadProvider.prototype.getContextPadEntries = function (element) { + const contextPad = this._contextPad, + modeling = this._modeling, + elementFactory = this._elementFactory, + connect = this._connect, + create = this._create, + popupMenu = this._popupMenu, + canvas = this._canvas, + rules = this._rules, + autoPlace = this._autoPlace, + translate = this._translate + + const actions = {} + + if (element.type === 'label') { + return actions + } + + const businessObject = element.businessObject + + function startConnect(event, element) { + connect.start(event, element) + } + + function removeElement() { + modeling.removeElements([element]) + } + + function getReplaceMenuPosition(element) { + const Y_OFFSET = 5 + + const diagramContainer = canvas.getContainer(), + pad = contextPad.getPad(element).html + + const diagramRect = diagramContainer.getBoundingClientRect(), + padRect = pad.getBoundingClientRect() + + const top = padRect.top - diagramRect.top + const left = padRect.left - diagramRect.left + + const pos = { + x: left, + y: top + padRect.height + Y_OFFSET + } + + return pos + } + + /** + * Create an append action + * + * @param {string} type + * @param {string} className + * @param {string} [title] + * @param {Object} [options] + * + * @return {Object} descriptor + */ + function appendAction(type, className, title, options) { + if (typeof title !== 'string') { + options = title + title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') }) + } + + function appendStart(event, element) { + const shape = elementFactory.createShape(assign({ type: type }, options)) + create.start(event, shape, { + source: element + }) + } + + const append = autoPlace + ? function (event, element) { + const shape = elementFactory.createShape(assign({ type: type }, options)) + + autoPlace.append(element, shape) + } + : appendStart + + return { + group: 'model', + className: className, + title: title, + action: { + dragstart: appendStart, + click: append + } + } + } + + function splitLaneHandler(count) { + return function (event, element) { + // actual split + modeling.splitLane(element, count) + + // refresh context pad after split to + // get rid of split icons + contextPad.open(element, true) + } + } + + if (isAny(businessObject, ['bpmn:Lane', 'bpmn:Participant']) && isExpanded(businessObject)) { + const childLanes = getChildLanes(element) + + assign(actions, { + 'lane-insert-above': { + group: 'lane-insert-above', + className: 'bpmn-icon-lane-insert-above', + title: translate('Add Lane above'), + action: { + click: function (event, element) { + modeling.addLane(element, 'top') + } + } + } + }) + + if (childLanes.length < 2) { + if (element.height >= 120) { + assign(actions, { + 'lane-divide-two': { + group: 'lane-divide', + className: 'bpmn-icon-lane-divide-two', + title: translate('Divide into two Lanes'), + action: { + click: splitLaneHandler(2) + } + } + }) + } + + if (element.height >= 180) { + assign(actions, { + 'lane-divide-three': { + group: 'lane-divide', + className: 'bpmn-icon-lane-divide-three', + title: translate('Divide into three Lanes'), + action: { + click: splitLaneHandler(3) + } + } + }) + } + } + + assign(actions, { + 'lane-insert-below': { + group: 'lane-insert-below', + className: 'bpmn-icon-lane-insert-below', + title: translate('Add Lane below'), + action: { + click: function (event, element) { + modeling.addLane(element, 'bottom') + } + } + } + }) + } + + if (is(businessObject, 'bpmn:FlowNode')) { + if (is(businessObject, 'bpmn:EventBasedGateway')) { + assign(actions, { + 'append.receive-task': appendAction( + 'bpmn:ReceiveTask', + 'bpmn-icon-receive-task', + translate('Append ReceiveTask') + ), + 'append.message-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-message', + translate('Append MessageIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:MessageEventDefinition' } + ), + 'append.timer-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-timer', + translate('Append TimerIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:TimerEventDefinition' } + ), + 'append.condition-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-condition', + translate('Append ConditionIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:ConditionalEventDefinition' } + ), + 'append.signal-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-signal', + translate('Append SignalIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:SignalEventDefinition' } + ) + }) + } else if ( + isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition') + ) { + assign(actions, { + 'append.compensation-activity': appendAction( + 'bpmn:Task', + 'bpmn-icon-task', + translate('Append compensation activity'), + { + isForCompensation: true + } + ) + }) + } else if ( + !is(businessObject, 'bpmn:EndEvent') && + !businessObject.isForCompensation && + !isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') && + !isEventSubProcess(businessObject) + ) { + assign(actions, { + 'append.end-event': appendAction( + 'bpmn:EndEvent', + 'bpmn-icon-end-event-none', + translate('Append EndEvent') + ), + 'append.gateway': appendAction( + 'bpmn:ExclusiveGateway', + 'bpmn-icon-gateway-none', + translate('Append Gateway') + ), + 'append.append-task': appendAction( + 'bpmn:UserTask', + 'bpmn-icon-user-task', + translate('Append Task') + ), + 'append.intermediate-event': appendAction( + 'bpmn:IntermediateThrowEvent', + 'bpmn-icon-intermediate-event-none', + translate('Append Intermediate/Boundary Event') + ) + }) + } + } + + if (!popupMenu.isEmpty(element, 'bpmn-replace')) { + // Replace menu entry + assign(actions, { + replace: { + group: 'edit', + className: 'bpmn-icon-screw-wrench', + title: '修改类型', + action: { + click: function (event, element) { + const position = assign(getReplaceMenuPosition(element), { + cursor: { x: event.x, y: event.y } + }) + + popupMenu.open(element, 'bpmn-replace', position) + } + } + } + }) + } + + if ( + isAny(businessObject, [ + 'bpmn:FlowNode', + 'bpmn:InteractionNode', + 'bpmn:DataObjectReference', + 'bpmn:DataStoreReference' + ]) + ) { + assign(actions, { + 'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation'), + + connect: { + group: 'connect', + className: 'bpmn-icon-connection-multi', + title: translate( + 'Connect using ' + + (businessObject.isForCompensation ? '' : 'Sequence/MessageFlow or ') + + 'Association' + ), + action: { + click: startConnect, + dragstart: startConnect + } + } + }) + } + + if (isAny(businessObject, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) { + assign(actions, { + connect: { + group: 'connect', + className: 'bpmn-icon-connection-multi', + title: translate('Connect using DataInputAssociation'), + action: { + click: startConnect, + dragstart: startConnect + } + } + }) + } + + if (is(businessObject, 'bpmn:Group')) { + assign(actions, { + 'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation') + }) + } + + // delete element entry, only show if allowed by rules + let deleteAllowed = rules.allowed('elements.delete', { elements: [element] }) + + if (isArray(deleteAllowed)) { + // was the element returned as a deletion candidate? + deleteAllowed = deleteAllowed[0] === element + } + + if (deleteAllowed) { + assign(actions, { + delete: { + group: 'edit', + className: 'bpmn-icon-trash', + title: translate('Remove'), + action: { + click: removeElement + } + } + }) + } + + return actions +} + +// helpers ///////// + +function isEventType(eventBo, type, definition) { + const isType = eventBo.$instanceOf(type) + let isDefinition = false + + const definitions = eventBo.eventDefinitions || [] + forEach(definitions, function (def) { + if (def.$type === definition) { + isDefinition = true + } + }) + + return isType && isDefinition +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/index.js b/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/index.js new file mode 100644 index 00000000..80009efc --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/index.js @@ -0,0 +1,6 @@ +import CustomContextPadProvider from './contentPadProvider' + +export default { + __init__: ['contextPadProvider'], + contextPadProvider: ['type', CustomContextPadProvider] +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/defaultEmpty.js b/src/components/bpmnProcessDesigner/package/designer/plugins/defaultEmpty.js new file mode 100644 index 00000000..f3bc894f --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/defaultEmpty.js @@ -0,0 +1,24 @@ +export default (key, name, type) => { + if (!type) type = 'camunda' + const TYPE_TARGET = { + activiti: 'http://activiti.org/bpmn', + camunda: 'http://bpmn.io/schema/bpmn', + flowable: 'http://flowable.org/bpmn' + } + return ` + + + + + + + +` +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/activitiDescriptor.json b/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/activitiDescriptor.json new file mode 100644 index 00000000..db5e4901 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/activitiDescriptor.json @@ -0,0 +1,994 @@ +{ + "name": "Activiti", + "uri": "http://activiti.org/bpmn", + "prefix": "activiti", + "xml": { + "tagAlias": "lowerCase" + }, + "associations": [], + "types": [ + { + "name": "Definitions", + "isAbstract": true, + "extends": ["bpmn:Definitions"], + "properties": [ + { + "name": "diagramRelationId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "InOutBinding", + "superClass": ["Element"], + "isAbstract": true, + "properties": [ + { + "name": "source", + "isAttr": true, + "type": "String" + }, + { + "name": "sourceExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "target", + "isAttr": true, + "type": "String" + }, + { + "name": "businessKey", + "isAttr": true, + "type": "String" + }, + { + "name": "local", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "variables", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "In", + "superClass": ["InOutBinding"], + "meta": { + "allowedIn": ["bpmn:CallActivity"] + } + }, + { + "name": "Out", + "superClass": ["InOutBinding"], + "meta": { + "allowedIn": ["bpmn:CallActivity"] + } + }, + { + "name": "AsyncCapable", + "isAbstract": true, + "extends": ["bpmn:Activity", "bpmn:Gateway", "bpmn:Event"], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncBefore", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncAfter", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "exclusive", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "JobPriorized", + "isAbstract": true, + "extends": ["bpmn:Process", "activiti:AsyncCapable"], + "properties": [ + { + "name": "jobPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "SignalEventDefinition", + "isAbstract": true, + "extends": ["bpmn:SignalEventDefinition"], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + } + ] + }, + { + "name": "ErrorEventDefinition", + "isAbstract": true, + "extends": ["bpmn:ErrorEventDefinition"], + "properties": [ + { + "name": "errorCodeVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "errorMessageVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Error", + "isAbstract": true, + "extends": ["bpmn:Error"], + "properties": [ + { + "name": "activiti:errorMessage", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "PotentialStarter", + "superClass": ["Element"], + "properties": [ + { + "name": "resourceAssignmentExpression", + "type": "bpmn:ResourceAssignmentExpression" + } + ] + }, + { + "name": "FormSupported", + "isAbstract": true, + "extends": ["bpmn:StartEvent", "bpmn:UserTask"], + "properties": [ + { + "name": "formHandlerClass", + "isAttr": true, + "type": "String" + }, + { + "name": "formKey", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "TemplateSupported", + "isAbstract": true, + "extends": ["bpmn:Process", "bpmn:FlowElement"], + "properties": [ + { + "name": "modelerTemplate", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Initiator", + "isAbstract": true, + "extends": ["bpmn:StartEvent"], + "properties": [ + { + "name": "initiator", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ScriptTask", + "isAbstract": true, + "extends": ["bpmn:ScriptTask"], + "properties": [ + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Process", + "isAbstract": true, + "extends": ["bpmn:Process"], + "properties": [ + { + "name": "candidateStarterGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateStarterUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "versionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "historyTimeToLive", + "isAttr": true, + "type": "String" + }, + { + "name": "isStartableInTasklist", + "isAttr": true, + "type": "Boolean", + "default": true + }, + { + "name": "executionListener", + "isAbstract": true, + "type": "Expression" + } + ] + }, + { + "name": "EscalationEventDefinition", + "isAbstract": true, + "extends": ["bpmn:EscalationEventDefinition"], + "properties": [ + { + "name": "escalationCodeVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FormalExpression", + "isAbstract": true, + "extends": ["bpmn:FormalExpression"], + "properties": [ + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "multiinstance_type", + "superClass": ["Element"] + }, + { + "name": "multiinstance_condition", + "superClass": ["Element"] + }, + { + "name": "Assignable", + "extends": ["bpmn:UserTask"], + "properties": [ + { + "name": "assignee", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "dueDate", + "isAttr": true, + "type": "String" + }, + { + "name": "followUpDate", + "isAttr": true, + "type": "String" + }, + { + "name": "priority", + "isAttr": true, + "type": "String" + }, + { + "name": "multiinstance_condition", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "CallActivity", + "extends": ["bpmn:CallActivity"], + "properties": [ + { + "name": "calledElementBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "calledElementVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementVersionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "caseRef", + "isAttr": true, + "type": "String" + }, + { + "name": "caseBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "caseVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "caseTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingClass", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingDelegateExpression", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ServiceTaskLike", + "extends": [ + "bpmn:ServiceTask", + "bpmn:BusinessRuleTask", + "bpmn:SendTask", + "bpmn:MessageEventDefinition" + ], + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "DmnCapable", + "extends": ["bpmn:BusinessRuleTask"], + "properties": [ + { + "name": "decisionRef", + "isAttr": true, + "type": "String" + }, + { + "name": "decisionRefBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "decisionRefVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "mapDecisionResult", + "isAttr": true, + "type": "String", + "default": "resultList" + }, + { + "name": "decisionRefTenantId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ExternalCapable", + "extends": ["activiti:ServiceTaskLike"], + "properties": [ + { + "name": "type", + "isAttr": true, + "type": "String" + }, + { + "name": "topic", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "TaskPriorized", + "extends": ["bpmn:Process", "activiti:ExternalCapable"], + "properties": [ + { + "name": "taskPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Properties", + "superClass": ["Element"], + "meta": { + "allowedIn": ["*"] + }, + "properties": [ + { + "name": "values", + "type": "Property", + "isMany": true + } + ] + }, + { + "name": "Property", + "superClass": ["Element"], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "value", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "Connector", + "superClass": ["Element"], + "meta": { + "allowedIn": ["activiti:ServiceTaskLike"] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + } + ] + }, + { + "name": "InputOutput", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:FlowNode", "activiti:Connector"] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + }, + { + "name": "inputParameters", + "isMany": true, + "type": "InputParameter" + }, + { + "name": "outputParameters", + "isMany": true, + "type": "OutputParameter" + } + ] + }, + { + "name": "InputOutputParameter", + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "InputOutputParameterDefinition", + "isAbstract": true + }, + { + "name": "List", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "items", + "isMany": true, + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Map", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "entries", + "isMany": true, + "type": "Entry" + } + ] + }, + { + "name": "Entry", + "properties": [ + { + "name": "key", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Value", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "id", + "isAttr": true, + "type": "String" + }, + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Script", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "scriptFormat", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Field", + "superClass": ["Element"], + "meta": { + "allowedIn": [ + "activiti:ServiceTaskLike", + "activiti:ExecutionListener", + "activiti:TaskListener" + ] + }, + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "expression", + "type": "String" + }, + { + "name": "stringValue", + "isAttr": true, + "type": "String" + }, + { + "name": "string", + "type": "String" + } + ] + }, + { + "name": "InputParameter", + "superClass": ["InputOutputParameter"] + }, + { + "name": "OutputParameter", + "superClass": ["InputOutputParameter"] + }, + { + "name": "Collectable", + "isAbstract": true, + "extends": ["bpmn:MultiInstanceLoopCharacteristics"], + "superClass": ["activiti:AsyncCapable"], + "properties": [ + { + "name": "collection", + "isAttr": true, + "type": "String" + }, + { + "name": "elementVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FailedJobRetryTimeCycle", + "superClass": ["Element"], + "meta": { + "allowedIn": ["activiti:AsyncCapable", "bpmn:MultiInstanceLoopCharacteristics"] + }, + "properties": [ + { + "name": "body", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "ExecutionListener", + "superClass": ["Element"], + "meta": { + "allowedIn": [ + "bpmn:Task", + "bpmn:ServiceTask", + "bpmn:UserTask", + "bpmn:BusinessRuleTask", + "bpmn:ScriptTask", + "bpmn:ReceiveTask", + "bpmn:ManualTask", + "bpmn:ExclusiveGateway", + "bpmn:SequenceFlow", + "bpmn:ParallelGateway", + "bpmn:InclusiveGateway", + "bpmn:EventBasedGateway", + "bpmn:StartEvent", + "bpmn:IntermediateCatchEvent", + "bpmn:IntermediateThrowEvent", + "bpmn:EndEvent", + "bpmn:BoundaryEvent", + "bpmn:CallActivity", + "bpmn:SubProcess", + "bpmn:Process" + ] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + } + ] + }, + { + "name": "TaskListener", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:UserTask"] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + } + ] + }, + { + "name": "FormProperty", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "required", + "type": "String", + "isAttr": true + }, + { + "name": "readable", + "type": "String", + "isAttr": true + }, + { + "name": "writable", + "type": "String", + "isAttr": true + }, + { + "name": "variable", + "type": "String", + "isAttr": true + }, + { + "name": "expression", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "default", + "type": "String", + "isAttr": true + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "FormProperty", + "superClass": ["Element"], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "label", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "defaultValue", + "type": "String", + "isAttr": true + }, + { + "name": "properties", + "type": "Properties" + }, + { + "name": "validation", + "type": "Validation" + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "Validation", + "superClass": ["Element"], + "properties": [ + { + "name": "constraints", + "type": "Constraint", + "isMany": true + } + ] + }, + { + "name": "Constraint", + "superClass": ["Element"], + "properties": [ + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "config", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "ConditionalEventDefinition", + "isAbstract": true, + "extends": ["bpmn:ConditionalEventDefinition"], + "properties": [ + { + "name": "variableName", + "isAttr": true, + "type": "String" + }, + { + "name": "variableEvent", + "isAttr": true, + "type": "String" + } + ] + } + ], + "emumerations": [] +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/camundaDescriptor.json b/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/camundaDescriptor.json new file mode 100644 index 00000000..79b86bca --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/camundaDescriptor.json @@ -0,0 +1,1010 @@ +{ + "name": "Camunda", + "uri": "http://camunda.org/schema/1.0/bpmn", + "prefix": "camunda", + "xml": { + "tagAlias": "lowerCase" + }, + "associations": [], + "types": [ + { + "name": "Definitions", + "isAbstract": true, + "extends": ["bpmn:Definitions"], + "properties": [ + { + "name": "diagramRelationId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "InOutBinding", + "superClass": ["Element"], + "isAbstract": true, + "properties": [ + { + "name": "source", + "isAttr": true, + "type": "String" + }, + { + "name": "sourceExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "target", + "isAttr": true, + "type": "String" + }, + { + "name": "businessKey", + "isAttr": true, + "type": "String" + }, + { + "name": "local", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "variables", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "In", + "superClass": ["InOutBinding"], + "meta": { + "allowedIn": ["bpmn:CallActivity", "bpmn:SignalEventDefinition"] + } + }, + { + "name": "Out", + "superClass": ["InOutBinding"], + "meta": { + "allowedIn": ["bpmn:CallActivity"] + } + }, + { + "name": "AsyncCapable", + "isAbstract": true, + "extends": ["bpmn:Activity", "bpmn:Gateway", "bpmn:Event"], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncBefore", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncAfter", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "exclusive", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "JobPriorized", + "isAbstract": true, + "extends": ["bpmn:Process", "camunda:AsyncCapable"], + "properties": [ + { + "name": "jobPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "SignalEventDefinition", + "isAbstract": true, + "extends": ["bpmn:SignalEventDefinition"], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + } + ] + }, + { + "name": "ErrorEventDefinition", + "isAbstract": true, + "extends": ["bpmn:ErrorEventDefinition"], + "properties": [ + { + "name": "errorCodeVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "errorMessageVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Error", + "isAbstract": true, + "extends": ["bpmn:Error"], + "properties": [ + { + "name": "camunda:errorMessage", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "PotentialStarter", + "superClass": ["Element"], + "properties": [ + { + "name": "resourceAssignmentExpression", + "type": "bpmn:ResourceAssignmentExpression" + } + ] + }, + { + "name": "FormSupported", + "isAbstract": true, + "extends": ["bpmn:StartEvent", "bpmn:UserTask"], + "properties": [ + { + "name": "formHandlerClass", + "isAttr": true, + "type": "String" + }, + { + "name": "formKey", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "TemplateSupported", + "isAbstract": true, + "extends": ["bpmn:Process", "bpmn:FlowElement"], + "properties": [ + { + "name": "modelerTemplate", + "isAttr": true, + "type": "String" + }, + { + "name": "modelerTemplateVersion", + "isAttr": true, + "type": "Integer" + } + ] + }, + { + "name": "Initiator", + "isAbstract": true, + "extends": ["bpmn:StartEvent"], + "properties": [ + { + "name": "initiator", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ScriptTask", + "isAbstract": true, + "extends": ["bpmn:ScriptTask"], + "properties": [ + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Process", + "isAbstract": true, + "extends": ["bpmn:Process"], + "properties": [ + { + "name": "candidateStarterGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateStarterUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "versionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "historyTimeToLive", + "isAttr": true, + "type": "String" + }, + { + "name": "isStartableInTasklist", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "EscalationEventDefinition", + "isAbstract": true, + "extends": ["bpmn:EscalationEventDefinition"], + "properties": [ + { + "name": "escalationCodeVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FormalExpression", + "isAbstract": true, + "extends": ["bpmn:FormalExpression"], + "properties": [ + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Assignable", + "extends": ["bpmn:UserTask"], + "properties": [ + { + "name": "assignee", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "dueDate", + "isAttr": true, + "type": "String" + }, + { + "name": "followUpDate", + "isAttr": true, + "type": "String" + }, + { + "name": "priority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "CallActivity", + "extends": ["bpmn:CallActivity"], + "properties": [ + { + "name": "calledElementBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "calledElementVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementVersionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "caseRef", + "isAttr": true, + "type": "String" + }, + { + "name": "caseBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "caseVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "caseTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingClass", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingDelegateExpression", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ServiceTaskLike", + "extends": [ + "bpmn:ServiceTask", + "bpmn:BusinessRuleTask", + "bpmn:SendTask", + "bpmn:MessageEventDefinition" + ], + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "DmnCapable", + "extends": ["bpmn:BusinessRuleTask"], + "properties": [ + { + "name": "decisionRef", + "isAttr": true, + "type": "String" + }, + { + "name": "decisionRefBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "decisionRefVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "mapDecisionResult", + "isAttr": true, + "type": "String", + "default": "resultList" + }, + { + "name": "decisionRefTenantId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ExternalCapable", + "extends": ["camunda:ServiceTaskLike"], + "properties": [ + { + "name": "type", + "isAttr": true, + "type": "String" + }, + { + "name": "topic", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "TaskPriorized", + "extends": ["bpmn:Process", "camunda:ExternalCapable"], + "properties": [ + { + "name": "taskPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Properties", + "superClass": ["Element"], + "meta": { + "allowedIn": ["*"] + }, + "properties": [ + { + "name": "values", + "type": "Property", + "isMany": true + } + ] + }, + { + "name": "Property", + "superClass": ["Element"], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "value", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "Connector", + "superClass": ["Element"], + "meta": { + "allowedIn": ["camunda:ServiceTaskLike"] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + } + ] + }, + { + "name": "InputOutput", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:FlowNode", "camunda:Connector"] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + }, + { + "name": "inputParameters", + "isMany": true, + "type": "InputParameter" + }, + { + "name": "outputParameters", + "isMany": true, + "type": "OutputParameter" + } + ] + }, + { + "name": "InputOutputParameter", + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "InputOutputParameterDefinition", + "isAbstract": true + }, + { + "name": "List", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "items", + "isMany": true, + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Map", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "entries", + "isMany": true, + "type": "Entry" + } + ] + }, + { + "name": "Entry", + "properties": [ + { + "name": "key", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Value", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "id", + "isAttr": true, + "type": "String" + }, + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Script", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "scriptFormat", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Field", + "superClass": ["Element"], + "meta": { + "allowedIn": [ + "camunda:ServiceTaskLike", + "camunda:ExecutionListener", + "camunda:TaskListener" + ] + }, + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "expression", + "type": "String" + }, + { + "name": "stringValue", + "isAttr": true, + "type": "String" + }, + { + "name": "string", + "type": "String" + } + ] + }, + { + "name": "InputParameter", + "superClass": ["InputOutputParameter"] + }, + { + "name": "OutputParameter", + "superClass": ["InputOutputParameter"] + }, + { + "name": "Collectable", + "isAbstract": true, + "extends": ["bpmn:MultiInstanceLoopCharacteristics"], + "superClass": ["camunda:AsyncCapable"], + "properties": [ + { + "name": "collection", + "isAttr": true, + "type": "String" + }, + { + "name": "elementVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FailedJobRetryTimeCycle", + "superClass": ["Element"], + "meta": { + "allowedIn": ["camunda:AsyncCapable", "bpmn:MultiInstanceLoopCharacteristics"] + }, + "properties": [ + { + "name": "body", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "ExecutionListener", + "superClass": ["Element"], + "meta": { + "allowedIn": [ + "bpmn:Task", + "bpmn:ServiceTask", + "bpmn:UserTask", + "bpmn:BusinessRuleTask", + "bpmn:ScriptTask", + "bpmn:ReceiveTask", + "bpmn:ManualTask", + "bpmn:ExclusiveGateway", + "bpmn:SequenceFlow", + "bpmn:ParallelGateway", + "bpmn:InclusiveGateway", + "bpmn:EventBasedGateway", + "bpmn:StartEvent", + "bpmn:IntermediateCatchEvent", + "bpmn:IntermediateThrowEvent", + "bpmn:EndEvent", + "bpmn:BoundaryEvent", + "bpmn:CallActivity", + "bpmn:SubProcess", + "bpmn:Process" + ] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + } + ] + }, + { + "name": "TaskListener", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:UserTask"] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + }, + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "eventDefinitions", + "type": "bpmn:TimerEventDefinition", + "isMany": true + } + ] + }, + { + "name": "FormProperty", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "required", + "type": "String", + "isAttr": true + }, + { + "name": "readable", + "type": "String", + "isAttr": true + }, + { + "name": "writable", + "type": "String", + "isAttr": true + }, + { + "name": "variable", + "type": "String", + "isAttr": true + }, + { + "name": "expression", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "default", + "type": "String", + "isAttr": true + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "FormData", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"] + }, + "properties": [ + { + "name": "fields", + "type": "FormField", + "isMany": true + }, + { + "name": "businessKey", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "FormField", + "superClass": ["Element"], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "label", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "defaultValue", + "type": "String", + "isAttr": true + }, + { + "name": "properties", + "type": "Properties" + }, + { + "name": "validation", + "type": "Validation" + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "Validation", + "superClass": ["Element"], + "properties": [ + { + "name": "constraints", + "type": "Constraint", + "isMany": true + } + ] + }, + { + "name": "Constraint", + "superClass": ["Element"], + "properties": [ + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "config", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "ConditionalEventDefinition", + "isAbstract": true, + "extends": ["bpmn:ConditionalEventDefinition"], + "properties": [ + { + "name": "variableName", + "isAttr": true, + "type": "String" + }, + { + "name": "variableEvents", + "isAttr": true, + "type": "String" + } + ] + } + ], + "emumerations": [] +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json b/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json new file mode 100644 index 00000000..7fe7ad14 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json @@ -0,0 +1,1207 @@ +{ + "name": "Flowable", + "uri": "http://flowable.org/bpmn", + "prefix": "flowable", + "xml": { + "tagAlias": "lowerCase" + }, + "associations": [], + "types": [ + { + "name": "InOutBinding", + "superClass": ["Element"], + "isAbstract": true, + "properties": [ + { + "name": "source", + "isAttr": true, + "type": "String" + }, + { + "name": "sourceExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "target", + "isAttr": true, + "type": "String" + }, + { + "name": "businessKey", + "isAttr": true, + "type": "String" + }, + { + "name": "local", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "variables", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "In", + "superClass": ["InOutBinding"], + "meta": { + "allowedIn": ["bpmn:CallActivity"] + } + }, + { + "name": "Out", + "superClass": ["InOutBinding"], + "meta": { + "allowedIn": ["bpmn:CallActivity"] + } + }, + { + "name": "AsyncCapable", + "isAbstract": true, + "extends": ["bpmn:Activity", "bpmn:Gateway", "bpmn:Event"], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncBefore", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncAfter", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "exclusive", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "JobPriorized", + "isAbstract": true, + "extends": ["bpmn:Process", "flowable:AsyncCapable"], + "properties": [ + { + "name": "jobPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "SignalEventDefinition", + "isAbstract": true, + "extends": ["bpmn:SignalEventDefinition"], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + } + ] + }, + { + "name": "ErrorEventDefinition", + "isAbstract": true, + "extends": ["bpmn:ErrorEventDefinition"], + "properties": [ + { + "name": "errorCodeVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "errorMessageVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Error", + "isAbstract": true, + "extends": ["bpmn:Error"], + "properties": [ + { + "name": "flowable:errorMessage", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "PotentialStarter", + "superClass": ["Element"], + "properties": [ + { + "name": "resourceAssignmentExpression", + "type": "bpmn:ResourceAssignmentExpression" + } + ] + }, + { + "name": "FormSupported", + "isAbstract": true, + "extends": ["bpmn:StartEvent", "bpmn:UserTask"], + "properties": [ + { + "name": "formHandlerClass", + "isAttr": true, + "type": "String" + }, + { + "name": "formKey", + "isAttr": true, + "type": "String" + }, + { + "name": "formType", + "isAttr": true, + "type": "String" + }, + { + "name": "formReadOnly", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "formInit", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "TemplateSupported", + "isAbstract": true, + "extends": ["bpmn:Process", "bpmn:FlowElement"], + "properties": [ + { + "name": "modelerTemplate", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Initiator", + "isAbstract": true, + "extends": ["bpmn:StartEvent"], + "properties": [ + { + "name": "initiator", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ScriptTask", + "isAbstract": true, + "extends": ["bpmn:ScriptTask"], + "properties": [ + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Process", + "isAbstract": true, + "extends": ["bpmn:Process"], + "properties": [ + { + "name": "candidateStarterGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateStarterUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "versionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "historyTimeToLive", + "isAttr": true, + "type": "String" + }, + { + "name": "isStartableInTasklist", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "EscalationEventDefinition", + "isAbstract": true, + "extends": ["bpmn:EscalationEventDefinition"], + "properties": [ + { + "name": "escalationCodeVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FormalExpression", + "isAbstract": true, + "extends": ["bpmn:FormalExpression"], + "properties": [ + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Assignable", + "extends": ["bpmn:UserTask"], + "properties": [ + { + "name": "assignee", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "dueDate", + "isAttr": true, + "type": "String" + }, + { + "name": "followUpDate", + "isAttr": true, + "type": "String" + }, + { + "name": "priority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Assignee", + "supperClass": "Element", + "meta": { + "allowedIn": ["*"] + }, + "properties": [ + { + "name": "label", + "type": "String", + "isAttr": true + }, + { + "name": "viewId", + "type": "Number", + "isAttr": true + } + ] + }, + { + "name": "CallActivity", + "extends": ["bpmn:CallActivity"], + "properties": [ + { + "name": "calledElementBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "calledElementVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementVersionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "caseRef", + "isAttr": true, + "type": "String" + }, + { + "name": "caseBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "caseVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "caseTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingClass", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingDelegateExpression", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ServiceTaskLike", + "extends": [ + "bpmn:ServiceTask", + "bpmn:BusinessRuleTask", + "bpmn:SendTask", + "bpmn:MessageEventDefinition" + ], + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "DmnCapable", + "extends": ["bpmn:BusinessRuleTask"], + "properties": [ + { + "name": "decisionRef", + "isAttr": true, + "type": "String" + }, + { + "name": "decisionRefBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "decisionRefVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "mapDecisionResult", + "isAttr": true, + "type": "String", + "default": "resultList" + }, + { + "name": "decisionRefTenantId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ExternalCapable", + "extends": ["flowable:ServiceTaskLike"], + "properties": [ + { + "name": "type", + "isAttr": true, + "type": "String" + }, + { + "name": "topic", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "TaskPriorized", + "extends": ["bpmn:Process", "flowable:ExternalCapable"], + "properties": [ + { + "name": "taskPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Properties", + "superClass": ["Element"], + "meta": { + "allowedIn": ["*"] + }, + "properties": [ + { + "name": "values", + "type": "Property", + "isMany": true + } + ] + }, + { + "name": "Property", + "superClass": ["Element"], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "value", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "Button", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:UserTask"] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "code", + "type": "String", + "isAttr": true + }, + { + "name": "isHide", + "type": "String", + "isAttr": true + }, + { + "name": "next", + "type": "String", + "isAttr": true + }, + { + "name": "sort", + "type": "Integer", + "isAttr": true + } + ] + }, + { + "name": "Assignee", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:UserTask"] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "value", + "type": "String", + "isAttr": true + }, + { + "name": "condition", + "type": "String", + "isAttr": true + }, + { + "name": "operationType", + "type": "String", + "isAttr": true + }, + { + "name": "sort", + "type": "Integer", + "isAttr": true + } + ] + }, + { + "name": "Connector", + "superClass": ["Element"], + "meta": { + "allowedIn": ["flowable:ServiceTaskLike"] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + } + ] + }, + { + "name": "InputOutput", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:FlowNode", "flowable:Connector"] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + }, + { + "name": "inputParameters", + "isMany": true, + "type": "InputParameter" + }, + { + "name": "outputParameters", + "isMany": true, + "type": "OutputParameter" + } + ] + }, + { + "name": "InputOutputParameter", + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "InputOutputParameterDefinition", + "isAbstract": true + }, + { + "name": "List", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "items", + "isMany": true, + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Map", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "entries", + "isMany": true, + "type": "Entry" + } + ] + }, + { + "name": "Entry", + "properties": [ + { + "name": "key", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Value", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "id", + "isAttr": true, + "type": "String" + }, + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Script", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "scriptFormat", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Field", + "superClass": ["Element"], + "meta": { + "allowedIn": [ + "flowable:ServiceTaskLike", + "flowable:ExecutionListener", + "flowable:TaskListener" + ] + }, + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "expression", + "type": "String" + }, + { + "name": "stringValue", + "isAttr": true, + "type": "String" + }, + { + "name": "string", + "type": "String" + } + ] + }, + { + "name": "ChildField", + "superClass": ["Element"], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "required", + "type": "String", + "isAttr": true + }, + { + "name": "readable", + "type": "String", + "isAttr": true + }, + { + "name": "writable", + "type": "String", + "isAttr": true + }, + { + "name": "variable", + "type": "String", + "isAttr": true + }, + { + "name": "expression", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "default", + "type": "String", + "isAttr": true + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "InputParameter", + "superClass": ["InputOutputParameter"] + }, + { + "name": "OutputParameter", + "superClass": ["InputOutputParameter"] + }, + { + "name": "Collectable", + "isAbstract": true, + "extends": ["bpmn:MultiInstanceLoopCharacteristics"], + "superClass": ["flowable:AsyncCapable"], + "properties": [ + { + "name": "collection", + "isAttr": true, + "type": "String" + }, + { + "name": "elementVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FailedJobRetryTimeCycle", + "superClass": ["Element"], + "meta": { + "allowedIn": ["flowable:AsyncCapable", "bpmn:MultiInstanceLoopCharacteristics"] + }, + "properties": [ + { + "name": "body", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "ExecutionListener", + "superClass": ["Element"], + "meta": { + "allowedIn": [ + "bpmn:Task", + "bpmn:ServiceTask", + "bpmn:UserTask", + "bpmn:BusinessRuleTask", + "bpmn:ScriptTask", + "bpmn:ReceiveTask", + "bpmn:ManualTask", + "bpmn:ExclusiveGateway", + "bpmn:SequenceFlow", + "bpmn:ParallelGateway", + "bpmn:InclusiveGateway", + "bpmn:EventBasedGateway", + "bpmn:StartEvent", + "bpmn:IntermediateCatchEvent", + "bpmn:IntermediateThrowEvent", + "bpmn:EndEvent", + "bpmn:BoundaryEvent", + "bpmn:CallActivity", + "bpmn:SubProcess", + "bpmn:Process" + ] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + } + ] + }, + { + "name": "TaskListener", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:UserTask"] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + } + ] + }, + { + "name": "FormProperty", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "required", + "type": "String", + "isAttr": true + }, + { + "name": "readable", + "type": "String", + "isAttr": true + }, + { + "name": "writable", + "type": "String", + "isAttr": true + }, + { + "name": "variable", + "type": "String", + "isAttr": true + }, + { + "name": "expression", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "default", + "type": "String", + "isAttr": true + }, + { + "name": "values", + "type": "Value", + "isMany": true + }, + { + "name": "children", + "type": "ChildField", + "isMany": true + }, + { + "name": "extensionElements", + "type": "bpmn:ExtensionElements", + "isMany": true + } + ] + }, + { + "name": "FormData", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"] + }, + "properties": [ + { + "name": "fields", + "type": "FormField", + "isMany": true + }, + { + "name": "businessKey", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "FormField", + "superClass": ["Element"], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "label", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "defaultValue", + "type": "String", + "isAttr": true + }, + { + "name": "properties", + "type": "Properties" + }, + { + "name": "validation", + "type": "Validation" + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "Validation", + "superClass": ["Element"], + "properties": [ + { + "name": "constraints", + "type": "Constraint", + "isMany": true + } + ] + }, + { + "name": "Constraint", + "superClass": ["Element"], + "properties": [ + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "config", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "ConditionalEventDefinition", + "isAbstract": true, + "extends": ["bpmn:ConditionalEventDefinition"], + "properties": [ + { + "name": "variableName", + "isAttr": true, + "type": "String" + }, + { + "name": "variableEvent", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Condition", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:SequenceFlow"] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "field", + "type": "String", + "isAttr": true + }, + { + "name": "compare", + "type": "String", + "isAttr": true + }, + { + "name": "value", + "type": "String", + "isAttr": true + }, + { + "name": "logic", + "type": "String", + "isAttr": true + }, + { + "name": "sort", + "type": "Integer", + "isAttr": true + } + ] + } + ], + "emumerations": [] +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/activitiExtension.js b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/activitiExtension.js new file mode 100644 index 00000000..56ef38aa --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/activitiExtension.js @@ -0,0 +1,83 @@ +'use strict' + +import { some } from 'min-dash' + +// const some = require('min-dash').some +// const some = some + +const ALLOWED_TYPES = { + FailedJobRetryTimeCycle: [ + 'bpmn:StartEvent', + 'bpmn:BoundaryEvent', + 'bpmn:IntermediateCatchEvent', + 'bpmn:Activity' + ], + Connector: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'], + Field: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'] +} + +function is(element, type) { + return element && typeof element.$instanceOf === 'function' && element.$instanceOf(type) +} + +function exists(element) { + return element && element.length +} + +function includesType(collection, type) { + return ( + exists(collection) && + some(collection, function (element) { + return is(element, type) + }) + ) +} + +function anyType(element, types) { + return some(types, function (type) { + return is(element, type) + }) +} + +function isAllowed(propName, propDescriptor, newElement) { + const name = propDescriptor.name, + types = ALLOWED_TYPES[name.replace(/activiti:/, '')] + + return name === propName && anyType(newElement, types) +} + +function ActivitiModdleExtension(eventBus) { + eventBus.on( + 'property.clone', + function (context) { + const newElement = context.newElement, + propDescriptor = context.propertyDescriptor + + this.canCloneProperty(newElement, propDescriptor) + }, + this + ) +} + +ActivitiModdleExtension.$inject = ['eventBus'] + +ActivitiModdleExtension.prototype.canCloneProperty = function (newElement, propDescriptor) { + if (isAllowed('activiti:FailedJobRetryTimeCycle', propDescriptor, newElement)) { + return ( + includesType(newElement.eventDefinitions, 'bpmn:TimerEventDefinition') || + includesType(newElement.eventDefinitions, 'bpmn:SignalEventDefinition') || + is(newElement.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics') + ) + } + + if (isAllowed('activiti:Connector', propDescriptor, newElement)) { + return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition') + } + + if (isAllowed('activiti:Field', propDescriptor, newElement)) { + return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition') + } +} + +// module.exports = ActivitiModdleExtension; +export default ActivitiModdleExtension diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/index.js b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/index.js new file mode 100644 index 00000000..c22ca345 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/index.js @@ -0,0 +1,11 @@ +/* + * @author igdianov + * address https://github.com/igdianov/activiti-bpmn-moddle + * */ + +import activitiExtension from './activitiExtension' + +export default { + __init__: ['ActivitiModdleExtension'], + ActivitiModdleExtension: ['type', activitiExtension] +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/extension.js b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/extension.js new file mode 100644 index 00000000..b8c37a57 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/extension.js @@ -0,0 +1,151 @@ +'use strict' + +import { isFunction, isObject, some } from 'min-dash' + +// const isFunction = isFunction, +// isObject = isObject, +// some = some +// const isFunction = require('min-dash').isFunction, +// isObject = require('min-dash').isObject, +// some = require('min-dash').some + +const WILDCARD = '*' + +function CamundaModdleExtension(eventBus) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this + + eventBus.on('moddleCopy.canCopyProperty', function (context) { + const property = context.property, + parent = context.parent + + return self.canCopyProperty(property, parent) + }) +} + +CamundaModdleExtension.$inject = ['eventBus'] + +/** + * Check wether to disallow copying property. + */ +CamundaModdleExtension.prototype.canCopyProperty = function (property, parent) { + // (1) check wether property is allowed in parent + if (isObject(property) && !isAllowedInParent(property, parent)) { + return false + } + + // (2) check more complex scenarios + + if (is(property, 'camunda:InputOutput') && !this.canHostInputOutput(parent)) { + return false + } + + if (isAny(property, ['camunda:Connector', 'camunda:Field']) && !this.canHostConnector(parent)) { + return false + } + + if (is(property, 'camunda:In') && !this.canHostIn(parent)) { + return false + } +} + +CamundaModdleExtension.prototype.canHostInputOutput = function (parent) { + // allowed in camunda:Connector + const connector = getParent(parent, 'camunda:Connector') + + if (connector) { + return true + } + + // special rules inside bpmn:FlowNode + const flowNode = getParent(parent, 'bpmn:FlowNode') + + if (!flowNode) { + return false + } + + if (isAny(flowNode, ['bpmn:StartEvent', 'bpmn:Gateway', 'bpmn:BoundaryEvent'])) { + return false + } + + return !(is(flowNode, 'bpmn:SubProcess') && flowNode.get('triggeredByEvent')) +} + +CamundaModdleExtension.prototype.canHostConnector = function (parent) { + const serviceTaskLike = getParent(parent, 'camunda:ServiceTaskLike') + + if (is(serviceTaskLike, 'bpmn:MessageEventDefinition')) { + // only allow on throw and end events + return getParent(parent, 'bpmn:IntermediateThrowEvent') || getParent(parent, 'bpmn:EndEvent') + } + + return true +} + +CamundaModdleExtension.prototype.canHostIn = function (parent) { + const callActivity = getParent(parent, 'bpmn:CallActivity') + + if (callActivity) { + return true + } + + const signalEventDefinition = getParent(parent, 'bpmn:SignalEventDefinition') + + if (signalEventDefinition) { + // only allow on throw and end events + return getParent(parent, 'bpmn:IntermediateThrowEvent') || getParent(parent, 'bpmn:EndEvent') + } + + return true +} + +// module.exports = CamundaModdleExtension; +export default CamundaModdleExtension + +// helpers ////////// + +function is(element, type) { + return element && isFunction(element.$instanceOf) && element.$instanceOf(type) +} + +function isAny(element, types) { + return some(types, function (t) { + return is(element, t) + }) +} + +function getParent(element, type) { + if (!type) { + return element.$parent + } + + if (is(element, type)) { + return element + } + + if (!element.$parent) { + return + } + + return getParent(element.$parent, type) +} + +function isAllowedInParent(property, parent) { + // (1) find property descriptor + const descriptor = property.$type && property.$model.getTypeDescriptor(property.$type) + + const allowedIn = descriptor && descriptor.meta && descriptor.meta.allowedIn + + if (!allowedIn || isWildcard(allowedIn)) { + return true + } + + // (2) check wether property has parent of allowed type + return some(allowedIn, function (type) { + return getParent(parent, type) + }) +} + +function isWildcard(allowedIn) { + return allowedIn.indexOf(WILDCARD) !== -1 +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/index.js b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/index.js new file mode 100644 index 00000000..1da1bc70 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/index.js @@ -0,0 +1,8 @@ +'use strict' + +import extension from './extension' + +export default { + __init__: ['camundaModdleExtension'], + camundaModdleExtension: ['type', extension] +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/flowableExtension.js b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/flowableExtension.js new file mode 100644 index 00000000..3dcea677 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/flowableExtension.js @@ -0,0 +1,83 @@ +'use strict' + +import { some } from 'min-dash' + +// const some = some +// const some = require('min-dash').some + +const ALLOWED_TYPES = { + FailedJobRetryTimeCycle: [ + 'bpmn:StartEvent', + 'bpmn:BoundaryEvent', + 'bpmn:IntermediateCatchEvent', + 'bpmn:Activity' + ], + Connector: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'], + Field: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'] +} + +function is(element, type) { + return element && typeof element.$instanceOf === 'function' && element.$instanceOf(type) +} + +function exists(element) { + return element && element.length +} + +function includesType(collection, type) { + return ( + exists(collection) && + some(collection, function (element) { + return is(element, type) + }) + ) +} + +function anyType(element, types) { + return some(types, function (type) { + return is(element, type) + }) +} + +function isAllowed(propName, propDescriptor, newElement) { + const name = propDescriptor.name, + types = ALLOWED_TYPES[name.replace(/flowable:/, '')] + + return name === propName && anyType(newElement, types) +} + +function FlowableModdleExtension(eventBus) { + eventBus.on( + 'property.clone', + function (context) { + const newElement = context.newElement, + propDescriptor = context.propertyDescriptor + + this.canCloneProperty(newElement, propDescriptor) + }, + this + ) +} + +FlowableModdleExtension.$inject = ['eventBus'] + +FlowableModdleExtension.prototype.canCloneProperty = function (newElement, propDescriptor) { + if (isAllowed('flowable:FailedJobRetryTimeCycle', propDescriptor, newElement)) { + return ( + includesType(newElement.eventDefinitions, 'bpmn:TimerEventDefinition') || + includesType(newElement.eventDefinitions, 'bpmn:SignalEventDefinition') || + is(newElement.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics') + ) + } + + if (isAllowed('flowable:Connector', propDescriptor, newElement)) { + return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition') + } + + if (isAllowed('flowable:Field', propDescriptor, newElement)) { + return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition') + } +} + +// module.exports = FlowableModdleExtension; +export default FlowableModdleExtension diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/index.js b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/index.js new file mode 100644 index 00000000..6d59b67f --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/index.js @@ -0,0 +1,10 @@ +/* + * @author igdianov + * address https://github.com/igdianov/activiti-bpmn-moddle + * */ +import flowableExtension from './flowableExtension' + +export default { + __init__: ['FlowableModdleExtension'], + FlowableModdleExtension: ['type', flowableExtension] +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js b/src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js new file mode 100644 index 00000000..5e2803b5 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js @@ -0,0 +1,221 @@ +import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider' +import { assign } from 'min-dash' + +export default function CustomPalette( + palette, + create, + elementFactory, + spaceTool, + lassoTool, + handTool, + globalConnect, + translate +) { + PaletteProvider.call( + this, + palette, + create, + elementFactory, + spaceTool, + lassoTool, + handTool, + globalConnect, + translate, + 2000 + ) +} + +const F = function () {} // 核心,利用空对象作为中介; +F.prototype = PaletteProvider.prototype // 核心,将父类的原型赋值给空对象F; + +// 利用中介函数重写原型链方法 +F.prototype.getPaletteEntries = function () { + const actions = {}, + create = this._create, + elementFactory = this._elementFactory, + spaceTool = this._spaceTool, + lassoTool = this._lassoTool, + handTool = this._handTool, + globalConnect = this._globalConnect, + translate = this._translate + + function createAction(type, group, className, title, options) { + function createListener(event) { + const shape = elementFactory.createShape(assign({ type: type }, options)) + + if (options) { + shape.businessObject.di.isExpanded = options.isExpanded + } + + create.start(event, shape) + } + + const shortType = type.replace(/^bpmn:/, '') + + return { + group: group, + className: className, + title: title || translate('Create {type}', { type: shortType }), + action: { + dragstart: createListener, + click: createListener + } + } + } + + function createSubprocess(event) { + const subProcess = elementFactory.createShape({ + type: 'bpmn:SubProcess', + x: 0, + y: 0, + isExpanded: true + }) + + const startEvent = elementFactory.createShape({ + type: 'bpmn:StartEvent', + x: 40, + y: 82, + parent: subProcess + }) + + create.start(event, [subProcess, startEvent], { + hints: { + autoSelect: [startEvent] + } + }) + } + + function createParticipant(event) { + create.start(event, elementFactory.createParticipantShape()) + } + + assign(actions, { + 'hand-tool': { + group: 'tools', + className: 'bpmn-icon-hand-tool', + title: '激活抓手工具', + // title: translate("Activate the hand tool"), + action: { + click: function (event) { + handTool.activateHand(event) + } + } + }, + 'lasso-tool': { + group: 'tools', + className: 'bpmn-icon-lasso-tool', + title: translate('Activate the lasso tool'), + action: { + click: function (event) { + lassoTool.activateSelection(event) + } + } + }, + 'space-tool': { + group: 'tools', + className: 'bpmn-icon-space-tool', + title: translate('Activate the create/remove space tool'), + action: { + click: function (event) { + spaceTool.activateSelection(event) + } + } + }, + 'global-connect-tool': { + group: 'tools', + className: 'bpmn-icon-connection-multi', + title: translate('Activate the global connect tool'), + action: { + click: function (event) { + globalConnect.toggle(event) + } + } + }, + 'tool-separator': { + group: 'tools', + separator: true + }, + 'create.start-event': createAction( + 'bpmn:StartEvent', + 'event', + 'bpmn-icon-start-event-none', + translate('Create StartEvent') + ), + 'create.intermediate-event': createAction( + 'bpmn:IntermediateThrowEvent', + 'event', + 'bpmn-icon-intermediate-event-none', + translate('Create Intermediate/Boundary Event') + ), + 'create.end-event': createAction( + 'bpmn:EndEvent', + 'event', + 'bpmn-icon-end-event-none', + translate('Create EndEvent') + ), + 'create.exclusive-gateway': createAction( + 'bpmn:ExclusiveGateway', + 'gateway', + 'bpmn-icon-gateway-none', + translate('Create Gateway') + ), + 'create.user-task': createAction( + 'bpmn:UserTask', + 'activity', + 'bpmn-icon-user-task', + translate('Create User Task') + ), + 'create.data-object': createAction( + 'bpmn:DataObjectReference', + 'data-object', + 'bpmn-icon-data-object', + translate('Create DataObjectReference') + ), + 'create.data-store': createAction( + 'bpmn:DataStoreReference', + 'data-store', + 'bpmn-icon-data-store', + translate('Create DataStoreReference') + ), + 'create.subprocess-expanded': { + group: 'activity', + className: 'bpmn-icon-subprocess-expanded', + title: translate('Create expanded SubProcess'), + action: { + dragstart: createSubprocess, + click: createSubprocess + } + }, + 'create.participant-expanded': { + group: 'collaboration', + className: 'bpmn-icon-participant', + title: translate('Create Pool/Participant'), + action: { + dragstart: createParticipant, + click: createParticipant + } + }, + 'create.group': createAction( + 'bpmn:Group', + 'artifact', + 'bpmn-icon-group', + translate('Create Group') + ) + }) + + return actions +} + +CustomPalette.$inject = [ + 'palette', + 'create', + 'elementFactory', + 'spaceTool', + 'lassoTool', + 'handTool', + 'globalConnect', + 'translate' +] + +CustomPalette.prototype = new F() // 核心,将 F的实例赋值给子类; +CustomPalette.prototype.constructor = CustomPalette // 修复子类CustomPalette的构造器指向,防止原型链的混乱; diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/palette/index.js b/src/components/bpmnProcessDesigner/package/designer/plugins/palette/index.js new file mode 100644 index 00000000..8e4f3ac9 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/palette/index.js @@ -0,0 +1,22 @@ +// import PaletteModule from "diagram-js/lib/features/palette"; +// import CreateModule from "diagram-js/lib/features/create"; +// import SpaceToolModule from "diagram-js/lib/features/space-tool"; +// import LassoToolModule from "diagram-js/lib/features/lasso-tool"; +// import HandToolModule from "diagram-js/lib/features/hand-tool"; +// import GlobalConnectModule from "diagram-js/lib/features/global-connect"; +// import translate from "diagram-js/lib/i18n/translate"; +// +// import PaletteProvider from "./paletteProvider"; +// +// export default { +// __depends__: [PaletteModule, CreateModule, SpaceToolModule, LassoToolModule, HandToolModule, GlobalConnectModule, translate], +// __init__: ["paletteProvider"], +// paletteProvider: ["type", PaletteProvider] +// }; + +import CustomPalette from './CustomPalette' + +export default { + __init__: ['paletteProvider'], + paletteProvider: ['type', CustomPalette] +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js b/src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js new file mode 100644 index 00000000..7098981c --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js @@ -0,0 +1,213 @@ +import { assign } from 'min-dash' + +/** + * A palette provider for BPMN 2.0 elements. + */ +export default function PaletteProvider( + palette, + create, + elementFactory, + spaceTool, + lassoTool, + handTool, + globalConnect, + translate +) { + this._palette = palette + this._create = create + this._elementFactory = elementFactory + this._spaceTool = spaceTool + this._lassoTool = lassoTool + this._handTool = handTool + this._globalConnect = globalConnect + this._translate = translate + + palette.registerProvider(this) +} + +PaletteProvider.$inject = [ + 'palette', + 'create', + 'elementFactory', + 'spaceTool', + 'lassoTool', + 'handTool', + 'globalConnect', + 'translate' +] + +PaletteProvider.prototype.getPaletteEntries = function () { + const actions = {}, + create = this._create, + elementFactory = this._elementFactory, + spaceTool = this._spaceTool, + lassoTool = this._lassoTool, + handTool = this._handTool, + globalConnect = this._globalConnect, + translate = this._translate + + function createAction(type, group, className, title, options) { + function createListener(event) { + const shape = elementFactory.createShape(assign({ type: type }, options)) + + if (options) { + shape.businessObject.di.isExpanded = options.isExpanded + } + + create.start(event, shape) + } + + const shortType = type.replace(/^bpmn:/, '') + + return { + group: group, + className: className, + title: title || translate('Create {type}', { type: shortType }), + action: { + dragstart: createListener, + click: createListener + } + } + } + + function createSubprocess(event) { + const subProcess = elementFactory.createShape({ + type: 'bpmn:SubProcess', + x: 0, + y: 0, + isExpanded: true + }) + + const startEvent = elementFactory.createShape({ + type: 'bpmn:StartEvent', + x: 40, + y: 82, + parent: subProcess + }) + + create.start(event, [subProcess, startEvent], { + hints: { + autoSelect: [startEvent] + } + }) + } + + function createParticipant(event) { + create.start(event, elementFactory.createParticipantShape()) + } + + assign(actions, { + 'hand-tool': { + group: 'tools', + className: 'bpmn-icon-hand-tool', + title: translate('Activate the hand tool'), + action: { + click: function (event) { + handTool.activateHand(event) + } + } + }, + 'lasso-tool': { + group: 'tools', + className: 'bpmn-icon-lasso-tool', + title: translate('Activate the lasso tool'), + action: { + click: function (event) { + lassoTool.activateSelection(event) + } + } + }, + 'space-tool': { + group: 'tools', + className: 'bpmn-icon-space-tool', + title: translate('Activate the create/remove space tool'), + action: { + click: function (event) { + spaceTool.activateSelection(event) + } + } + }, + 'global-connect-tool': { + group: 'tools', + className: 'bpmn-icon-connection-multi', + title: translate('Activate the global connect tool'), + action: { + click: function (event) { + globalConnect.toggle(event) + } + } + }, + 'tool-separator': { + group: 'tools', + separator: true + }, + 'create.start-event': createAction( + 'bpmn:StartEvent', + 'event', + 'bpmn-icon-start-event-none', + translate('Create StartEvent') + ), + 'create.intermediate-event': createAction( + 'bpmn:IntermediateThrowEvent', + 'event', + 'bpmn-icon-intermediate-event-none', + translate('Create Intermediate/Boundary Event') + ), + 'create.end-event': createAction( + 'bpmn:EndEvent', + 'event', + 'bpmn-icon-end-event-none', + translate('Create EndEvent') + ), + 'create.exclusive-gateway': createAction( + 'bpmn:ExclusiveGateway', + 'gateway', + 'bpmn-icon-gateway-none', + translate('Create Gateway') + ), + 'create.user-task': createAction( + 'bpmn:UserTask', + 'activity', + 'bpmn-icon-user-task', + translate('Create User Task') + ), + 'create.data-object': createAction( + 'bpmn:DataObjectReference', + 'data-object', + 'bpmn-icon-data-object', + translate('Create DataObjectReference') + ), + 'create.data-store': createAction( + 'bpmn:DataStoreReference', + 'data-store', + 'bpmn-icon-data-store', + translate('Create DataStoreReference') + ), + 'create.subprocess-expanded': { + group: 'activity', + className: 'bpmn-icon-subprocess-expanded', + title: translate('Create expanded SubProcess'), + action: { + dragstart: createSubprocess, + click: createSubprocess + } + }, + 'create.participant-expanded': { + group: 'collaboration', + className: 'bpmn-icon-participant', + title: translate('Create Pool/Participant'), + action: { + dragstart: createParticipant, + click: createParticipant + } + }, + 'create.group': createAction( + 'bpmn:Group', + 'artifact', + 'bpmn-icon-group', + translate('Create Group') + ) + }) + + return actions +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/translate/customTranslate.js b/src/components/bpmnProcessDesigner/package/designer/plugins/translate/customTranslate.js new file mode 100644 index 00000000..c1b99e12 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/translate/customTranslate.js @@ -0,0 +1,44 @@ +// import translations from "./zh"; +// +// export default function customTranslate(template, replacements) { +// replacements = replacements || {}; +// +// // Translate +// template = translations[template] || template; +// +// // Replace +// return template.replace(/{([^}]+)}/g, function(_, key) { +// let str = replacements[key]; +// if ( +// translations[replacements[key]] !== null && +// translations[replacements[key]] !== "undefined" +// ) { +// // eslint-disable-next-line no-mixed-spaces-and-tabs +// str = translations[replacements[key]]; +// // eslint-disable-next-line no-mixed-spaces-and-tabs +// } +// return str || "{" + key + "}"; +// }); +// } + +export default function customTranslate(translations) { + return function (template, replacements) { + replacements = replacements || {} + // Translate + template = translations[template] || template + + // Replace + return template.replace(/{([^}]+)}/g, function (_, key) { + let str = replacements[key] + if ( + translations[replacements[key]] !== null && + translations[replacements[key]] !== undefined + ) { + // eslint-disable-next-line no-mixed-spaces-and-tabs + str = translations[replacements[key]] + // eslint-disable-next-line no-mixed-spaces-and-tabs + } + return str || '{' + key + '}' + }) + } +} diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/translate/zh.js b/src/components/bpmnProcessDesigner/package/designer/plugins/translate/zh.js new file mode 100644 index 00000000..777db3e7 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/translate/zh.js @@ -0,0 +1,240 @@ +/** + * This is a sample file that should be replaced with the actual translation. + * + * Checkout https://github.com/bpmn-io/bpmn-js-i18n for a list of available + * translations and labels to translate. + */ +export default { + // 添加部分 + 'Append EndEvent': '追加结束事件', + 'Append Gateway': '追加网关', + 'Append Task': '追加任务', + 'Append Intermediate/Boundary Event': '追加中间抛出事件/边界事件', + + 'Activate the global connect tool': '激活全局连接工具', + 'Append {type}': '添加 {type}', + 'Add Lane above': '在上面添加道', + 'Divide into two Lanes': '分割成两个道', + 'Divide into three Lanes': '分割成三个道', + 'Add Lane below': '在下面添加道', + 'Append compensation activity': '追加补偿活动', + 'Change type': '修改类型', + 'Connect using Association': '使用关联连接', + 'Connect using Sequence/MessageFlow or Association': '使用顺序/消息流或者关联连接', + 'Connect using DataInputAssociation': '使用数据输入关联连接', + Remove: '移除', + 'Activate the hand tool': '激活抓手工具', + 'Activate the lasso tool': '激活套索工具', + 'Activate the create/remove space tool': '激活创建/删除空间工具', + 'Create expanded SubProcess': '创建扩展子过程', + 'Create IntermediateThrowEvent/BoundaryEvent': '创建中间抛出事件/边界事件', + 'Create Pool/Participant': '创建池/参与者', + 'Parallel Multi Instance': '并行多重事件', + 'Sequential Multi Instance': '时序多重事件', + DataObjectReference: '数据对象参考', + DataStoreReference: '数据存储参考', + Loop: '循环', + 'Ad-hoc': '即席', + 'Create {type}': '创建 {type}', + Task: '任务', + 'Send Task': '发送任务', + 'Receive Task': '接收任务', + 'User Task': '用户任务', + 'Manual Task': '手工任务', + 'Business Rule Task': '业务规则任务', + 'Service Task': '服务任务', + 'Script Task': '脚本任务', + 'Call Activity': '调用活动', + 'Sub Process (collapsed)': '子流程(折叠的)', + 'Sub Process (expanded)': '子流程(展开的)', + 'Start Event': '开始事件', + StartEvent: '开始事件', + 'Intermediate Throw Event': '中间事件', + 'End Event': '结束事件', + EndEvent: '结束事件', + 'Create StartEvent': '创建开始事件', + 'Create EndEvent': '创建结束事件', + 'Create Task': '创建任务', + 'Create User Task': '创建用户任务', + 'Create Gateway': '创建网关', + 'Create DataObjectReference': '创建数据对象', + 'Create DataStoreReference': '创建数据存储', + 'Create Group': '创建分组', + 'Create Intermediate/Boundary Event': '创建中间/边界事件', + 'Message Start Event': '消息开始事件', + 'Timer Start Event': '定时开始事件', + 'Conditional Start Event': '条件开始事件', + 'Signal Start Event': '信号开始事件', + 'Error Start Event': '错误开始事件', + 'Escalation Start Event': '升级开始事件', + 'Compensation Start Event': '补偿开始事件', + 'Message Start Event (non-interrupting)': '消息开始事件(非中断)', + 'Timer Start Event (non-interrupting)': '定时开始事件(非中断)', + 'Conditional Start Event (non-interrupting)': '条件开始事件(非中断)', + 'Signal Start Event (non-interrupting)': '信号开始事件(非中断)', + 'Escalation Start Event (non-interrupting)': '升级开始事件(非中断)', + 'Message Intermediate Catch Event': '消息中间捕获事件', + 'Message Intermediate Throw Event': '消息中间抛出事件', + 'Timer Intermediate Catch Event': '定时中间捕获事件', + 'Escalation Intermediate Throw Event': '升级中间抛出事件', + 'Conditional Intermediate Catch Event': '条件中间捕获事件', + 'Link Intermediate Catch Event': '链接中间捕获事件', + 'Link Intermediate Throw Event': '链接中间抛出事件', + 'Compensation Intermediate Throw Event': '补偿中间抛出事件', + 'Signal Intermediate Catch Event': '信号中间捕获事件', + 'Signal Intermediate Throw Event': '信号中间抛出事件', + 'Message End Event': '消息结束事件', + 'Escalation End Event': '定时结束事件', + 'Error End Event': '错误结束事件', + 'Cancel End Event': '取消结束事件', + 'Compensation End Event': '补偿结束事件', + 'Signal End Event': '信号结束事件', + 'Terminate End Event': '终止结束事件', + 'Message Boundary Event': '消息边界事件', + 'Message Boundary Event (non-interrupting)': '消息边界事件(非中断)', + 'Timer Boundary Event': '定时边界事件', + 'Timer Boundary Event (non-interrupting)': '定时边界事件(非中断)', + 'Escalation Boundary Event': '升级边界事件', + 'Escalation Boundary Event (non-interrupting)': '升级边界事件(非中断)', + 'Conditional Boundary Event': '条件边界事件', + 'Conditional Boundary Event (non-interrupting)': '条件边界事件(非中断)', + 'Error Boundary Event': '错误边界事件', + 'Cancel Boundary Event': '取消边界事件', + 'Signal Boundary Event': '信号边界事件', + 'Signal Boundary Event (non-interrupting)': '信号边界事件(非中断)', + 'Compensation Boundary Event': '补偿边界事件', + 'Exclusive Gateway': '互斥网关', + 'Parallel Gateway': '并行网关', + 'Inclusive Gateway': '相容网关', + 'Complex Gateway': '复杂网关', + 'Event based Gateway': '事件网关', + Transaction: '转运', + 'Sub Process': '子流程', + 'Event Sub Process': '事件子流程', + 'Collapsed Pool': '折叠池', + 'Expanded Pool': '展开池', + + // Errors + 'no parent for {element} in {parent}': '在{parent}里,{element}没有父类', + 'no shape type specified': '没有指定的形状类型', + 'flow elements must be children of pools/participants': '流元素必须是池/参与者的子类', + 'out of bounds release': 'out of bounds release', + 'more than {count} child lanes': '子道大于{count} ', + 'element required': '元素不能为空', + 'diagram not part of bpmn:Definitions': '流程图不符合bpmn规范', + 'no diagram to display': '没有可展示的流程图', + 'no process or collaboration to display': '没有可展示的流程/协作', + 'element {element} referenced by {referenced}#{property} not yet drawn': + '由{referenced}#{property}引用的{element}元素仍未绘制', + 'already rendered {element}': '{element} 已被渲染', + 'failed to import {element}': '导入{element}失败', + //属性面板的参数 + Id: '编号', + Name: '名称', + General: '常规', + Details: '详情', + 'Message Name': '消息名称', + Message: '消息', + Initiator: '创建者', + 'Asynchronous Continuations': '持续异步', + 'Asynchronous Before': '异步前', + 'Asynchronous After': '异步后', + 'Job Configuration': '工作配置', + Exclusive: '排除', + 'Job Priority': '工作优先级', + 'Retry Time Cycle': '重试时间周期', + Documentation: '文档', + 'Element Documentation': '元素文档', + 'History Configuration': '历史配置', + 'History Time To Live': '历史的生存时间', + Forms: '表单', + 'Form Key': '表单key', + 'Form Fields': '表单字段', + 'Business Key': '业务key', + 'Form Field': '表单字段', + ID: '编号', + Type: '类型', + Label: '名称', + 'Default Value': '默认值', + 'Default Flow': '默认流转路径', + 'Conditional Flow': '条件流转路径', + 'Sequence Flow': '普通流转路径', + Validation: '校验', + 'Add Constraint': '添加约束', + Config: '配置', + Properties: '属性', + 'Add Property': '添加属性', + Value: '值', + Listeners: '监听器', + 'Execution Listener': '执行监听', + 'Event Type': '事件类型', + 'Listener Type': '监听器类型', + 'Java Class': 'Java类', + Expression: '表达式', + 'Must provide a value': '必须提供一个值', + 'Delegate Expression': '代理表达式', + Script: '脚本', + 'Script Format': '脚本格式', + 'Script Type': '脚本类型', + 'Inline Script': '内联脚本', + 'External Script': '外部脚本', + Resource: '资源', + 'Field Injection': '字段注入', + Extensions: '扩展', + 'Input/Output': '输入/输出', + 'Input Parameters': '输入参数', + 'Output Parameters': '输出参数', + Parameters: '参数', + 'Output Parameter': '输出参数', + 'Timer Definition Type': '定时器定义类型', + 'Timer Definition': '定时器定义', + Date: '日期', + Duration: '持续', + Cycle: '循环', + Signal: '信号', + 'Signal Name': '信号名称', + Escalation: '升级', + Error: '错误', + 'Link Name': '链接名称', + Condition: '条件名称', + 'Variable Name': '变量名称', + 'Variable Event': '变量事件', + 'Specify more than one variable change event as a comma separated list.': + '多个变量事件以逗号隔开', + 'Wait for Completion': '等待完成', + 'Activity Ref': '活动参考', + 'Version Tag': '版本标签', + Executable: '可执行文件', + 'External Task Configuration': '扩展任务配置', + 'Task Priority': '任务优先级', + External: '外部', + Connector: '连接器', + 'Must configure Connector': '必须配置连接器', + 'Connector Id': '连接器编号', + Implementation: '实现方式', + 'Field Injections': '字段注入', + Fields: '字段', + 'Result Variable': '结果变量', + Topic: '主题', + 'Configure Connector': '配置连接器', + 'Input Parameter': '输入参数', + Assignee: '代理人', + 'Candidate Users': '候选用户', + 'Candidate Groups': '候选组', + 'Due Date': '到期时间', + 'Follow Up Date': '跟踪日期', + Priority: '优先级', + 'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': + '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00', + 'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': + '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00', + Variables: '变量', + 'Candidate Starter Configuration': '候选人起动器配置', + 'Candidate Starter Groups': '候选人起动器组', + 'This maps to the process definition key.': '这映射到流程定义键。', + 'Candidate Starter Users': '候选人起动器的用户', + 'Specify more than one user as a comma separated list.': '指定多个用户作为逗号分隔的列表。', + 'Tasklist Configuration': 'Tasklist配置', + Startable: '启动', + 'Specify more than one group as a comma separated list.': '指定多个组作为逗号分隔的列表。' +} diff --git a/src/components/bpmnProcessDesigner/package/index.ts b/src/components/bpmnProcessDesigner/package/index.ts new file mode 100644 index 00000000..23c77346 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/index.ts @@ -0,0 +1,37 @@ +import { App } from 'vue' +import MyProcessDesigner from './designer' +import MyProcessPenal from './penal' +import MyProcessViewer from './designer/index2' + +const components = [MyProcessDesigner, MyProcessPenal, MyProcessViewer] + +// const install = function (Vue) { +// components.forEach(component => { +// Vue.component(component.name, component) +// }) +// } + +// if (typeof window !== "undefined" && window.Vue) { +// install(window.Vue) +// } +// components.forEach(component => { +// Vue.component(component.name, component) +// }) +const componentss = { + install: (Vue: App): void => { + components.forEach((component) => { + Vue.component(component.name, component) + }) + } +} +// let version = "0.0.1" +export const MyPD = (app) => { + // export default { + // app.use(version) + // app.use(install) + // app.use(MyProcessDesigner) + // app.use(MyProcessPenal) + // app.use(MyProcessViewer) + // app.use(components) + app.use(componentss) +} diff --git a/src/components/bpmnProcessDesigner/package/palette/ProcessPalette.vue b/src/components/bpmnProcessDesigner/package/palette/ProcessPalette.vue new file mode 100644 index 00000000..f5272840 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/palette/ProcessPalette.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue b/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue new file mode 100644 index 00000000..03dec801 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue @@ -0,0 +1,205 @@ + + diff --git a/src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue b/src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue new file mode 100644 index 00000000..d9f178a0 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue @@ -0,0 +1,180 @@ + + diff --git a/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue b/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue new file mode 100644 index 00000000..ee4512e7 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue @@ -0,0 +1,185 @@ + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/form/ElementForm.vue b/src/components/bpmnProcessDesigner/package/penal/form/ElementForm.vue new file mode 100644 index 00000000..a1b51623 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/form/ElementForm.vue @@ -0,0 +1,444 @@ + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/index.js b/src/components/bpmnProcessDesigner/package/penal/index.js new file mode 100644 index 00000000..7fa56170 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/index.js @@ -0,0 +1,7 @@ +import MyPropertiesPanel from './PropertiesPanel.vue' + +MyPropertiesPanel.install = function (Vue) { + Vue.component(MyPropertiesPanel.name, MyPropertiesPanel) +} + +export default MyPropertiesPanel diff --git a/src/components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue b/src/components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue new file mode 100644 index 00000000..0c29e6ff --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue @@ -0,0 +1,399 @@ + + diff --git a/src/components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue b/src/components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue new file mode 100644 index 00000000..7746ca86 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue @@ -0,0 +1,447 @@ + + diff --git a/src/components/bpmnProcessDesigner/package/penal/listeners/template.js b/src/components/bpmnProcessDesigner/package/penal/listeners/template.js new file mode 100644 index 00000000..37b9c76a --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/listeners/template.js @@ -0,0 +1,178 @@ +export const template = (isTaskListener) => { + return ` +
+ + + + + + + + +
+ 添加监听器 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + ${ + isTaskListener + ? "" + + "" + + "" + + "" + + "" + + "" + + '' + + '' + + "" + + "" + + '' + : '' + } + + +

+ 注入字段: + 添加字段 +

+ + + + + + + + + + +
+ 取 消 + 保 存 +
+
+ + + + + + + + + + + + + + + + + + + + + +
+ ` +} diff --git a/src/components/bpmnProcessDesigner/package/penal/listeners/utilSelf.ts b/src/components/bpmnProcessDesigner/package/penal/listeners/utilSelf.ts new file mode 100644 index 00000000..5f46abd0 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/listeners/utilSelf.ts @@ -0,0 +1,62 @@ +// 初始化表单数据 +export function initListenerForm(listener) { + let self = { + ...listener + } + if (listener.script) { + self = { + ...listener, + ...listener.script, + scriptType: listener.script.resource ? 'externalScript' : 'inlineScript' + } + } + if (listener.event === 'timeout' && listener.eventDefinitions) { + if (listener.eventDefinitions.length) { + let k = '' + for (const key in listener.eventDefinitions[0]) { + console.log(listener.eventDefinitions, key) + if (key.indexOf('time') !== -1) { + k = key + self.eventDefinitionType = key.replace('time', '').toLowerCase() + } + } + console.log(k) + self.eventTimeDefinitions = listener.eventDefinitions[0][k].body + } + } + return self +} + +export function initListenerType(listener) { + let listenerType + if (listener.class) listenerType = 'classListener' + if (listener.expression) listenerType = 'expressionListener' + if (listener.delegateExpression) listenerType = 'delegateExpressionListener' + if (listener.script) listenerType = 'scriptListener' + return { + ...JSON.parse(JSON.stringify(listener)), + ...(listener.script ?? {}), + listenerType: listenerType + } +} + +export const listenerType = { + classListener: 'Java 类', + expressionListener: '表达式', + delegateExpressionListener: '代理表达式', + scriptListener: '脚本' +} + +export const eventType = { + create: '创建', + assignment: '指派', + complete: '完成', + delete: '删除', + update: '更新', + timeout: '超时' +} + +export const fieldType = { + string: '字符串', + expression: '表达式' +} diff --git a/src/components/bpmnProcessDesigner/package/penal/multi-instance/ElementMultiInstance.vue b/src/components/bpmnProcessDesigner/package/penal/multi-instance/ElementMultiInstance.vue new file mode 100644 index 00000000..6d0229bb --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/multi-instance/ElementMultiInstance.vue @@ -0,0 +1,253 @@ + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue b/src/components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue new file mode 100644 index 00000000..fe2d004d --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue b/src/components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue new file mode 100644 index 00000000..388c363f --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue @@ -0,0 +1,166 @@ + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue b/src/components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue new file mode 100644 index 00000000..285b3411 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue @@ -0,0 +1,110 @@ + + diff --git a/src/components/bpmnProcessDesigner/package/penal/task/ElementTask.vue b/src/components/bpmnProcessDesigner/package/penal/task/ElementTask.vue new file mode 100644 index 00000000..939116ab --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/task/ElementTask.vue @@ -0,0 +1,83 @@ + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue b/src/components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue new file mode 100644 index 00000000..d8e1b4af --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue @@ -0,0 +1,123 @@ + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue b/src/components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue new file mode 100644 index 00000000..ba3e6edf --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue @@ -0,0 +1,96 @@ + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue b/src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue new file mode 100644 index 00000000..118fa71b --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/components/bpmnProcessDesigner/package/theme/element-variables.scss b/src/components/bpmnProcessDesigner/package/theme/element-variables.scss new file mode 100644 index 00000000..49bd326d --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/theme/element-variables.scss @@ -0,0 +1,70 @@ +/* 改变主题色变量 */ +$--color-primary: #1890ff; +$--color-danger: #ff4d4f; + +/* 改变 icon 字体路径变量,必需 */ +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +@import '~element-ui/packages/theme-chalk/src/index'; + +.el-table td, +.el-table th { + color: #333; +} +.el-drawer__header { + padding: 16px 16px 8px 16px; + margin: 0; + line-height: 24px; + font-size: 18px; + color: #303133; + box-sizing: border-box; + border-bottom: 1px solid #e8e8e8; +} +div[class^='el-drawer']:focus, +span:focus { + outline: none; +} +.el-drawer__body { + box-sizing: border-box; + padding: 16px; + width: 100%; + overflow-y: auto; +} + +.el-dialog { + margin-top: 50vh !important; + transform: translateY(-50%); + overflow: hidden; +} +.el-dialog__wrapper { + overflow: hidden; + max-height: 100vh; +} +.el-dialog__header { + padding: 16px 16px 8px 16px; + box-sizing: border-box; + border-bottom: 1px solid #e8e8e8; +} +.el-dialog__body { + padding: 16px; + max-height: 80vh; + box-sizing: border-box; + overflow-y: auto; +} +.el-dialog__footer { + padding: 16px; + box-sizing: border-box; + border-top: 1px solid #e8e8e8; +} +.el-dialog__close { + font-weight: 600; +} +.el-select { + width: 100%; +} +.el-divider:not(.el-divider--horizontal) { + margin: 0 8px; +} +.el-divider.el-divider--horizontal { + margin: 16px 0; +} diff --git a/src/components/bpmnProcessDesigner/package/theme/index.scss b/src/components/bpmnProcessDesigner/package/theme/index.scss new file mode 100644 index 00000000..2e60fad4 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/theme/index.scss @@ -0,0 +1,2 @@ +@import './process-designer.scss'; +@import './process-panel.scss'; diff --git a/src/components/bpmnProcessDesigner/package/theme/process-designer.scss b/src/components/bpmnProcessDesigner/package/theme/process-designer.scss new file mode 100644 index 00000000..6af945da --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/theme/process-designer.scss @@ -0,0 +1,161 @@ +@import 'bpmn-js-token-simulation/assets/css/bpmn-js-token-simulation.css'; +@import 'bpmn-js-token-simulation/assets/css/font-awesome.min.css'; +@import 'bpmn-js-token-simulation/assets/css/normalize.css'; + +// 边框被 token-simulation 样式覆盖了 +.djs-palette { + background: var(--palette-background-color); + border: solid 1px var(--palette-border-color) !important; + border-radius: 2px; +} + +.my-process-designer { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + box-sizing: border-box; + .my-process-designer__header { + width: 100%; + min-height: 36px; + .el-button { + text-align: center; + } + .el-button-group { + margin: 4px; + } + .el-tooltip__popper { + .el-button { + width: 100%; + text-align: left; + padding-left: 8px; + padding-right: 8px; + } + .el-button:hover { + background: rgba(64, 158, 255, 0.8); + color: #ffffff; + } + } + .align { + position: relative; + i { + &:after { + content: '|'; + position: absolute; + // transform: rotate(90deg) translate(200%, 60%); + transform: rotate(180deg) translate(271%, -10%); + } + } + } + .align.align-left i { + transform: rotate(90deg); + } + .align.align-right i { + transform: rotate(-90deg); + } + .align.align-top i { + transform: rotate(180deg); + } + .align.align-bottom i { + transform: rotate(0deg); + } + .align.align-center i { + transform: rotate(0deg); + &:after { + // transform: rotate(90deg) translate(0, 60%); + transform: rotate(0deg) translate(-0%, -5%); + } + } + .align.align-middle i { + transform: rotate(-90deg); + &:after { + // transform: rotate(90deg) translate(0, 60%); + transform: rotate(0deg) translate(0, -10%); + } + } + } + .my-process-designer__container { + display: inline-flex; + width: 100%; + flex: 1; + .my-process-designer__canvas { + flex: 1; + height: 100%; + position: relative; + background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') + repeat !important; + div.toggle-mode { + display: none; + } + } + .my-process-designer__property-panel { + height: 100%; + overflow: scroll; + overflow-y: auto; + z-index: 10; + * { + box-sizing: border-box; + } + } + svg { + width: 100%; + height: 100%; + min-height: 100%; + overflow: hidden; + } + } +} + +//侧边栏配置 +// .djs-palette .two-column .open { +.open { + // .djs-palette.open { + .djs-palette-entries { + div[class^='bpmn-icon-']:before, + div[class*='bpmn-icon-']:before { + line-height: unset; + } + div.entry { + position: relative; + } + div.entry:hover { + &::after { + width: max-content; + content: attr(title); + vertical-align: text-bottom; + position: absolute; + right: -10px; + top: 0; + bottom: 0; + overflow: hidden; + transform: translateX(100%); + font-size: 0.5em; + display: inline-block; + text-decoration: inherit; + font-variant: normal; + text-transform: none; + background: #fafafa; + box-shadow: 0 0 6px #eeeeee; + border: 1px solid #cccccc; + box-sizing: border-box; + padding: 0 16px; + border-radius: 4px; + z-index: 100; + } + } + } +} +pre { + margin: 0; + height: 100%; + overflow: hidden; + max-height: calc(80vh - 32px); + overflow-y: auto; +} +.hljs { + word-break: break-word; + white-space: pre-wrap; +} +.hljs * { + font-family: Consolas, Monaco, monospace; +} diff --git a/src/components/bpmnProcessDesigner/package/theme/process-panel.scss b/src/components/bpmnProcessDesigner/package/theme/process-panel.scss new file mode 100644 index 00000000..f840cdde --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/theme/process-panel.scss @@ -0,0 +1,107 @@ +.process-panel__container { + box-sizing: border-box; + padding: 0 8px; + border-left: 1px solid #eeeeee; + box-shadow: 0 0 8px #cccccc; + max-height: 100%; + overflow-y: scroll; +} +.panel-tab__title { + font-weight: 600; + padding: 0 8px; + font-size: 1.1em; + line-height: 1.2em; + i { + margin-right: 8px; + font-size: 1.2em; + } +} +.panel-tab__content { + width: 100%; + box-sizing: border-box; + border-top: 1px solid #eeeeee; + padding: 8px 16px; + .panel-tab__content--title { + display: flex; + justify-content: space-between; + padding-bottom: 8px; + span { + flex: 1; + text-align: left; + } + } +} +.element-property { + width: 100%; + display: flex; + align-items: flex-start; + margin: 8px 0; + .element-property__label { + display: block; + width: 90px; + text-align: right; + overflow: hidden; + padding-right: 12px; + line-height: 32px; + font-size: 14px; + box-sizing: border-box; + } + .element-property__value { + flex: 1; + line-height: 32px; + } + .el-form-item { + width: 100%; + margin-bottom: 0; + padding-bottom: 18px; + } +} +.list-property { + flex-direction: column; + .element-listener-item { + width: 100%; + display: inline-grid; + grid-template-columns: 16px auto 32px 32px; + grid-column-gap: 8px; + } + .element-listener-item + .element-listener-item { + margin-top: 8px; + } +} +.listener-filed__title { + display: inline-flex; + width: 100%; + justify-content: space-between; + align-items: center; + margin-top: 0; + span { + width: 200px; + text-align: left; + font-size: 14px; + } + i { + margin-right: 8px; + } +} +.element-drawer__button { + margin-top: 8px; + width: 100%; + display: inline-flex; + justify-content: space-around; +} +.element-drawer__button > .el-button { + width: 100%; +} + +.el-collapse-item__content { + padding-bottom: 0; +} +.el-input.is-disabled .el-input__inner { + color: #999999; +} +.el-form-item.el-form-item--mini { + margin-bottom: 0; + & + .el-form-item { + margin-top: 16px; + } +} diff --git a/src/components/bpmnProcessDesigner/package/utils.ts b/src/components/bpmnProcessDesigner/package/utils.ts new file mode 100644 index 00000000..41f06801 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/utils.ts @@ -0,0 +1,76 @@ +import { toRaw } from 'vue' +// 创建监听器实例 +export function createListenerObject(options, isTask, prefix) { + const listenerObj = Object.create(null) + listenerObj.event = options.event + isTask && (listenerObj.id = options.id) // 任务监听器特有的 id 字段 + switch (options.listenerType) { + case 'scriptListener': + listenerObj.script = createScriptObject(options, prefix) + break + case 'expressionListener': + listenerObj.expression = options.expression + break + case 'delegateExpressionListener': + listenerObj.delegateExpression = options.delegateExpression + break + default: + listenerObj.class = options.class + } + // 注入字段 + if (options.fields) { + listenerObj.fields = options.fields.map((field) => { + return createFieldObject(field, prefix) + }) + } + // 任务监听器的 定时器 设置 + if (isTask && options.event === 'timeout' && !!options.eventDefinitionType) { + const timeDefinition = window.bpmnInstances.moddle.create('bpmn:FormalExpression', { + body: options.eventTimeDefinitions + }) + const TimerEventDefinition = window.bpmnInstances.moddle.create('bpmn:TimerEventDefinition', { + id: `TimerEventDefinition_${uuid(8)}`, + [`time${options.eventDefinitionType.replace(/^\S/, (s) => s.toUpperCase())}`]: timeDefinition + }) + listenerObj.eventDefinitions = [TimerEventDefinition] + } + return window.bpmnInstances.moddle.create( + `${prefix}:${isTask ? 'TaskListener' : 'ExecutionListener'}`, + listenerObj + ) +} + +// 创建 监听器的注入字段 实例 +export function createFieldObject(option, prefix) { + const { name, fieldType, string, expression } = option + const fieldConfig = fieldType === 'string' ? { name, string } : { name, expression } + return window.bpmnInstances.moddle.create(`${prefix}:Field`, fieldConfig) +} + +// 创建脚本实例 +export function createScriptObject(options, prefix) { + const { scriptType, scriptFormat, value, resource } = options + const scriptConfig = + scriptType === 'inlineScript' ? { scriptFormat, value } : { scriptFormat, resource } + return window.bpmnInstances.moddle.create(`${prefix}:Script`, scriptConfig) +} + +// 更新元素扩展属性 +export function updateElementExtensions(element, extensionList) { + const extensions = window.bpmnInstances.moddle.create('bpmn:ExtensionElements', { + values: extensionList + }) + window.bpmnInstances.modeling.updateProperties(toRaw(element), { + extensionElements: extensions + }) +} + +// 创建一个id +export function uuid(length = 8, chars) { + let result = '' + const charsString = chars || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + for (let i = length; i > 0; --i) { + result += charsString[Math.floor(Math.random() * charsString.length)] + } + return result +} diff --git a/src/components/bpmnProcessDesigner/src/highlight/index.js b/src/components/bpmnProcessDesigner/src/highlight/index.js new file mode 100644 index 00000000..5df38c9a --- /dev/null +++ b/src/components/bpmnProcessDesigner/src/highlight/index.js @@ -0,0 +1,5 @@ +const hljs = require('highlight.js/lib/core') +hljs.registerLanguage('xml', require('highlight.js/lib/languages/xml')) +hljs.registerLanguage('json', require('highlight.js/lib/languages/json')) + +module.exports = hljs diff --git a/src/components/bpmnProcessDesigner/src/modules/custom-renderer/CustomRenderer.js b/src/components/bpmnProcessDesigner/src/modules/custom-renderer/CustomRenderer.js new file mode 100644 index 00000000..e8760315 --- /dev/null +++ b/src/components/bpmnProcessDesigner/src/modules/custom-renderer/CustomRenderer.js @@ -0,0 +1,14 @@ +import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer' + +export default function CustomRenderer(config, eventBus, styles, pathMap, canvas, textRenderer) { + BpmnRenderer.call(this, config, eventBus, styles, pathMap, canvas, textRenderer, 2000) + + this.handlers['label'] = function () { + return null + } +} + +const F = function () {} // 核心,利用空对象作为中介; +F.prototype = BpmnRenderer.prototype // 核心,将父类的原型赋值给空对象F; +CustomRenderer.prototype = new F() // 核心,将 F的实例赋值给子类; +CustomRenderer.prototype.constructor = CustomRenderer // 修复子类CustomRenderer的构造器指向,防止原型链的混乱; diff --git a/src/components/bpmnProcessDesigner/src/modules/custom-renderer/index.js b/src/components/bpmnProcessDesigner/src/modules/custom-renderer/index.js new file mode 100644 index 00000000..79d8bd04 --- /dev/null +++ b/src/components/bpmnProcessDesigner/src/modules/custom-renderer/index.js @@ -0,0 +1,6 @@ +import CustomRenderer from './CustomRenderer' + +export default { + __init__: ['customRenderer'], + customRenderer: ['type', CustomRenderer] +} diff --git a/src/components/bpmnProcessDesigner/src/modules/rules/CustomRules.js b/src/components/bpmnProcessDesigner/src/modules/rules/CustomRules.js new file mode 100644 index 00000000..9fa1d14a --- /dev/null +++ b/src/components/bpmnProcessDesigner/src/modules/rules/CustomRules.js @@ -0,0 +1,16 @@ +import BpmnRules from 'bpmn-js/lib/features/rules/BpmnRules' +import inherits from 'inherits' + +export default function CustomRules(eventBus) { + BpmnRules.call(this, eventBus) +} + +inherits(CustomRules, BpmnRules) + +CustomRules.prototype.canDrop = function () { + return false +} + +CustomRules.prototype.canMove = function () { + return false +} diff --git a/src/components/bpmnProcessDesigner/src/modules/rules/index.js b/src/components/bpmnProcessDesigner/src/modules/rules/index.js new file mode 100644 index 00000000..12cf05a7 --- /dev/null +++ b/src/components/bpmnProcessDesigner/src/modules/rules/index.js @@ -0,0 +1,6 @@ +import CustomRules from './CustomRules' + +export default { + __init__: ['customRules'], + customRules: ['type', CustomRules] +} diff --git a/src/components/bpmnProcessDesigner/src/translations.ts b/src/components/bpmnProcessDesigner/src/translations.ts new file mode 100644 index 00000000..5f9b9a51 --- /dev/null +++ b/src/components/bpmnProcessDesigner/src/translations.ts @@ -0,0 +1,25 @@ +/** + * This is a sample file that should be replaced with the actual translation. + * + * Checkout https://github.com/bpmn-io/bpmn-js-i18n for a list of available + * translations and labels to translate. + */ +export default { + 'Exclusive Gateway': 'Exklusives Gateway', + 'Parallel Gateway': 'Paralleles Gateway', + 'Inclusive Gateway': 'Inklusives Gateway', + 'Complex Gateway': 'Komplexes Gateway', + 'Event based Gateway': 'Ereignis-basiertes Gateway', + 'Message Start Event': '消息启动事件', + 'Timer Start Event': '定时启动事件', + 'Conditional Start Event': '条件启动事件', + 'Signal Start Event': '信号启动事件', + 'Error Start Event': '错误启动事件', + 'Escalation Start Event': '升级启动事件', + 'Compensation Start Event': '补偿启动事件', + 'Message Start Event (non-interrupting)': '消息启动事件 (非中断)', + 'Timer Start Event (non-interrupting)': '定时启动事件 (非中断)', + 'Conditional Start Event (non-interrupting)': '条件启动事件 (非中断)', + 'Signal Start Event (non-interrupting)': '信号启动事件 (非中断)', + 'Escalation Start Event (non-interrupting)': '升级启动事件 (非中断)' +} diff --git a/src/components/bpmnProcessDesigner/src/utils/directive/clickOutSide.js b/src/components/bpmnProcessDesigner/src/utils/directive/clickOutSide.js new file mode 100644 index 00000000..bb71d442 --- /dev/null +++ b/src/components/bpmnProcessDesigner/src/utils/directive/clickOutSide.js @@ -0,0 +1,39 @@ +//outside.js + +const ctx = '@@clickoutsideContext' + +export default { + bind(el, binding, vnode) { + const ele = el + const documentHandler = (e) => { + if (!vnode.context || ele.contains(e.target)) { + return false + } + // 调用指令回调 + if (binding.expression) { + vnode.context[el[ctx].methodName](e) + } else { + el[ctx].bindingFn(e) + } + } + // 将方法添加到ele + ele[ctx] = { + documentHandler, + methodName: binding.expression, + bindingFn: binding.value + } + + setTimeout(() => { + document.addEventListener('touchstart', documentHandler) // 为document绑定事件 + }) + }, + update(el, binding) { + const ele = el + ele[ctx].methodName = binding.expression + ele[ctx].bindingFn = binding.value + }, + unbind(el) { + document.removeEventListener('touchstart', el[ctx].documentHandler) // 解绑 + delete el[ctx] + } +} diff --git a/src/components/bpmnProcessDesigner/src/utils/index.js b/src/components/bpmnProcessDesigner/src/utils/index.js new file mode 100644 index 00000000..7d970ecd --- /dev/null +++ b/src/components/bpmnProcessDesigner/src/utils/index.js @@ -0,0 +1,10 @@ +export function debounce(fn, delay = 500) { + let timer + return function (...args) { + if (timer) { + clearTimeout(timer) + timer = null + } + timer = setTimeout(fn.bind(this, ...args), delay) + } +} diff --git a/src/components/bpmnProcessDesigner/src/utils/xml2json.js b/src/components/bpmnProcessDesigner/src/utils/xml2json.js new file mode 100644 index 00000000..fe1a52fb --- /dev/null +++ b/src/components/bpmnProcessDesigner/src/utils/xml2json.js @@ -0,0 +1,50 @@ +function xmlStr2XmlObj(xmlStr) { + let xmlObj = {} + if (document.all) { + const xmlDom = new window.ActiveXObject('Microsoft.XMLDOM') + xmlDom.loadXML(xmlStr) + xmlObj = xmlDom + } else { + xmlObj = new DOMParser().parseFromString(xmlStr, 'text/xml') + } + return xmlObj +} + +function xml2json(xml) { + try { + let obj = {} + if (xml.children.length > 0) { + for (let i = 0; i < xml.children.length; i++) { + const item = xml.children.item(i) + const nodeName = item.nodeName + if (typeof obj[nodeName] == 'undefined') { + obj[nodeName] = xml2json(item) + } else { + if (typeof obj[nodeName].push == 'undefined') { + const old = obj[nodeName] + obj[nodeName] = [] + obj[nodeName].push(old) + } + obj[nodeName].push(xml2json(item)) + } + } + } else { + obj = xml.textContent + } + return obj + } catch (e) { + console.log(e.message) + } +} + +function xmlObj2json(xml) { + const xmlObj = xmlStr2XmlObj(xml) + console.log(xmlObj) + let jsonObj = {} + if (xmlObj.childNodes.length > 0) { + jsonObj = xml2json(xmlObj) + } + return jsonObj +} + +export default xmlObj2json diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 00000000..19b2aac6 --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,25 @@ +import type { App } from 'vue' +import { Icon } from './Icon' +import { Form } from '@/components/Form' +import { Table } from '@/components/Table' +import { Search } from '@/components/Search' +import { XModal } from '@/components/XModal' +import { XTable } from '@/components/XTable' +import { XButton, XTextButton } from '@/components/XButton' +import { DictTag } from '@/components/DictTag' +import { ContentWrap } from '@/components/ContentWrap' +import { Descriptions } from '@/components/Descriptions' + +export const setupGlobCom = (app: App): void => { + app.component('Icon', Icon) + app.component('Form', Form) + app.component('Table', Table) + app.component('Search', Search) + app.component('XModal', XModal) + app.component('XTable', XTable) + app.component('XButton', XButton) + app.component('XTextButton', XTextButton) + app.component('DictTag', DictTag) + app.component('ContentWrap', ContentWrap) + app.component('Descriptions', Descriptions) +} diff --git a/src/config/axios/config.ts b/src/config/axios/config.ts new file mode 100644 index 00000000..81165087 --- /dev/null +++ b/src/config/axios/config.ts @@ -0,0 +1,28 @@ +const config: { + base_url: string + result_code: number | string + default_headers: AxiosHeaders + request_timeout: number +} = { + /** + * api请求基础路径 + */ + base_url: import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL, + /** + * 接口成功返回状态码 + */ + result_code: 200, + + /** + * 接口请求超时时间 + */ + request_timeout: 30000, + + /** + * 默认接口请求类型 + * 可选值:application/x-www-form-urlencoded multipart/form-data + */ + default_headers: 'application/json' +} + +export { config } diff --git a/src/config/axios/errorCode.ts b/src/config/axios/errorCode.ts new file mode 100644 index 00000000..94d719f8 --- /dev/null +++ b/src/config/axios/errorCode.ts @@ -0,0 +1,6 @@ +export default { + '401': '认证失败,无法访问系统资源', + '403': '当前操作没有权限', + '404': '访问资源不存在', + default: '系统未知错误,请反馈给管理员' +} diff --git a/src/config/axios/index.ts b/src/config/axios/index.ts new file mode 100644 index 00000000..e6c6af8d --- /dev/null +++ b/src/config/axios/index.ts @@ -0,0 +1,50 @@ +import { service } from './service' + +import { config } from './config' + +const { default_headers } = config + +const request = (option: any) => { + const { url, method, params, data, headersType, responseType } = option + return service({ + url: url, + method, + params, + data, + responseType: responseType, + headers: { + 'Content-Type': headersType || default_headers + } + }) +} +export default { + get: async (option: any) => { + const res = await request({ method: 'GET', ...option }) + return res.data as unknown as T + }, + post: async (option: any) => { + const res = await request({ method: 'POST', ...option }) + return res.data as unknown as T + }, + postOriginal: async (option: any) => { + const res = await request({ method: 'POST', ...option }) + return res + }, + delete: async (option: any) => { + const res = await request({ method: 'DELETE', ...option }) + return res.data as unknown as T + }, + put: async (option: any) => { + const res = await request({ method: 'PUT', ...option }) + return res.data as unknown as T + }, + download: async (option: any) => { + const res = await request({ method: 'GET', responseType: 'blob', ...option }) + return res as unknown as Promise + }, + upload: async (option: any) => { + option.headersType = 'multipart/form-data' + const res = await request({ method: 'POST', ...option }) + return res as unknown as Promise + } +} diff --git a/src/config/axios/service.ts b/src/config/axios/service.ts new file mode 100644 index 00000000..fbafd3e8 --- /dev/null +++ b/src/config/axios/service.ts @@ -0,0 +1,241 @@ +import axios, { + AxiosInstance, + AxiosRequestHeaders, + AxiosResponse, + AxiosError, + InternalAxiosRequestConfig +} from 'axios' + +import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' +import qs from 'qs' +import { config } from '@/config/axios/config' +import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth' +import errorCode from './errorCode' + +import { resetRouter } from '@/router' +import { useCache } from '@/hooks/web/useCache' + +const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE +const { result_code, base_url, request_timeout } = config + +// 需要忽略的提示。忽略后,自动 Promise.reject('error') +const ignoreMsgs = [ + '无效的刷新令牌', // 刷新令牌被删除时,不用提示 + '刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面 +] +// 是否显示重新登录 +export const isRelogin = { show: false } +// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现 +// 请求队列 +let requestList: any[] = [] +// 是否正在刷新中 +let isRefreshToken = false +// 请求白名单,无须token的接口 +const whiteList: string[] = ['/login', '/refresh-token'] + +// 创建axios实例 +const service: AxiosInstance = axios.create({ + baseURL: base_url, // api 的 base_url + timeout: request_timeout, // 请求超时时间 + withCredentials: false // 禁用 Cookie 等信息 +}) + +// request拦截器 +service.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + // 是否需要设置 token + let isToken = (config!.headers || {}).isToken === false + whiteList.some((v) => { + if (config.url) { + config.url.indexOf(v) > -1 + return (isToken = false) + } + }) + if (getAccessToken() && !isToken) { + ;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token + } + // 设置租户 + if (tenantEnable && tenantEnable === 'true') { + const tenantId = getTenantId() + if (tenantId) (config as Recordable).headers['tenant-id'] = tenantId + } + const params = config.params || {} + const data = config.data || false + if ( + config.method?.toUpperCase() === 'POST' && + (config.headers as AxiosRequestHeaders)['Content-Type'] === + 'application/x-www-form-urlencoded' + ) { + config.data = qs.stringify(data) + } + // get参数编码 + if (config.method?.toUpperCase() === 'GET' && params) { + let url = config.url + '?' + for (const propName of Object.keys(params)) { + const value = params[propName] + if (value !== void 0 && value !== null && typeof value !== 'undefined') { + if (typeof value === 'object') { + for (const val of Object.keys(value)) { + const params = propName + '[' + val + ']' + const subPart = encodeURIComponent(params) + '=' + url += subPart + encodeURIComponent(value[val]) + '&' + } + } else { + url += `${propName}=${encodeURIComponent(value)}&` + } + } + } + // 给 get 请求加上时间戳参数,避免从缓存中拿数据 + // const now = new Date().getTime() + // params = params.substring(0, url.length - 1) + `?_t=${now}` + url = url.slice(0, -1) + config.params = {} + config.url = url + } + return config + }, + (error: AxiosError) => { + // Do something with request error + console.log(error) // for debug + Promise.reject(error) + } +) + +// response 拦截器 +service.interceptors.response.use( + async (response: AxiosResponse) => { + const { data } = response + const config = response.config + if (!data) { + // 返回“[HTTP]请求没有返回值”; + throw new Error() + } + const { t } = useI18n() + // 未设置状态码则默认成功状态 + const code = data.code || result_code + // 二进制数据则直接返回 + if ( + response.request.responseType === 'blob' || + response.request.responseType === 'arraybuffer' + ) { + return response.data + } + // 获取错误信息 + const msg = data.msg || errorCode[code] || errorCode['default'] + if (ignoreMsgs.indexOf(msg) !== -1) { + // 如果是忽略的错误码,直接返回 msg 异常 + return Promise.reject(msg) + } else if (code === 401) { + // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了 + if (!isRefreshToken) { + isRefreshToken = true + // 1. 如果获取不到刷新令牌,则只能执行登出操作 + if (!getRefreshToken()) { + return handleAuthorized() + } + // 2. 进行刷新访问令牌 + try { + const refreshTokenRes = await refreshToken() + // 2.1 刷新成功,则回放队列的请求 + 当前请求 + setToken((await refreshTokenRes).data.data) + config.headers!.Authorization = 'Bearer ' + getAccessToken() + requestList.forEach((cb: any) => { + cb() + }) + requestList = [] + return service(config) + } catch (e) { + // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。 + // 2.2 刷新失败,只回放队列的请求 + requestList.forEach((cb: any) => { + cb() + }) + // 提示是否要登出。即不回放当前请求!不然会形成递归 + return handleAuthorized() + } finally { + requestList = [] + isRefreshToken = false + } + } else { + // 添加到队列,等待刷新获取到新的令牌 + return new Promise((resolve) => { + requestList.push(() => { + config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改 + resolve(service(config)) + }) + }) + } + } else if (code === 500) { + ElMessage.error(t('sys.api.errMsg500')) + return Promise.reject(new Error(msg)) + } else if (code === 901) { + ElMessage.error({ + duration: 5, + offset: 300, + dangerouslyUseHTMLString: true, + message: + '
' + + t('sys.api.errMsg901') + + '
' + + '
 
' + + '
参考 https://doc.iocoder.cn/ 教程
' + + '
 
' + + '
5 分钟搭建本地环境
' + }) + return Promise.reject(new Error(msg)) + } else if (code !== 200) { + if (msg === '无效的刷新令牌') { + // hard coding:忽略这个提示,直接登出 + console.log(msg) + } else { + ElNotification.error({ title: msg }) + } + return Promise.reject('error') + } else { + return data + } + }, + (error: AxiosError) => { + console.log('err' + error) // for debug + let { message } = error + const { t } = useI18n() + if (message === 'Network Error') { + message = t('sys.api.errorMessage') + } else if (message.includes('timeout')) { + message = t('sys.api.apiTimeoutMessage') + } else if (message.includes('Request failed with status code')) { + message = t('sys.api.apiRequestFailed') + message.substr(message.length - 3) + } + ElMessage.error(message) + return Promise.reject(error) + } +) + +const refreshToken = async () => { + axios.defaults.headers.common['tenant-id'] = getTenantId() + return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken()) +} +const handleAuthorized = () => { + const { t } = useI18n() + if (!isRelogin.show) { + isRelogin.show = true + ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), { + confirmButtonText: t('login.relogin'), + cancelButtonText: t('common.cancel'), + type: 'warning' + }) + .then(() => { + const { wsCache } = useCache() + resetRouter() // 重置静态路由表 + wsCache.clear() + removeToken() + isRelogin.show = false + window.location.href = '/' + }) + .catch(() => { + isRelogin.show = false + }) + } + return Promise.reject(t('sys.api.timeoutMessage')) +} +export { service } diff --git a/src/directives/index.ts b/src/directives/index.ts new file mode 100644 index 00000000..89cc8ba1 --- /dev/null +++ b/src/directives/index.ts @@ -0,0 +1,13 @@ +import type { App } from 'vue' +import { hasRole } from './permission/hasRole' +import { hasPermi } from './permission/hasPermi' + +/** + * 导出指令:v-xxx + * @methods hasRole 用户权限,用法: v-hasRole + * @methods hasPermi 按钮权限,用法: v-hasPermi + */ +export const setupAuth = (app: App) => { + hasRole(app) + hasPermi(app) +} diff --git a/src/directives/permission/hasPermi.ts b/src/directives/permission/hasPermi.ts new file mode 100644 index 00000000..d86d2f54 --- /dev/null +++ b/src/directives/permission/hasPermi.ts @@ -0,0 +1,27 @@ +import type { App } from 'vue' +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' + +const { t } = useI18n() // 国际化 + +export function hasPermi(app: App) { + app.directive('hasPermi', (el, binding) => { + const { wsCache } = useCache() + const { value } = binding + const all_permission = '*:*:*' + const permissions = wsCache.get(CACHE_KEY.USER).permissions + + if (value && value instanceof Array && value.length > 0) { + const permissionFlag = value + + const hasPermissions = permissions.some((permission: string) => { + return all_permission === permission || permissionFlag.includes(permission) + }) + + if (!hasPermissions) { + el.parentNode && el.parentNode.removeChild(el) + } + } else { + throw new Error(t('permission.hasPermission')) + } + }) +} diff --git a/src/directives/permission/hasRole.ts b/src/directives/permission/hasRole.ts new file mode 100644 index 00000000..31a352a7 --- /dev/null +++ b/src/directives/permission/hasRole.ts @@ -0,0 +1,27 @@ +import type { App } from 'vue' +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' + +const { t } = useI18n() // 国际化 + +export function hasRole(app: App) { + app.directive('hasRole', (el, binding) => { + const { wsCache } = useCache() + const { value } = binding + const super_admin = 'admin' + const roles = wsCache.get(CACHE_KEY.USER).roles + + if (value && value instanceof Array && value.length > 0) { + const roleFlag = value + + const hasRole = roles.some((role: string) => { + return super_admin === role || roleFlag.includes(role) + }) + + if (!hasRole) { + el.parentNode && el.parentNode.removeChild(el) + } + } else { + throw new Error(t('permission.hasRole')) + } + }) +} diff --git a/src/hooks/event/useScrollTo.ts b/src/hooks/event/useScrollTo.ts new file mode 100644 index 00000000..92aec872 --- /dev/null +++ b/src/hooks/event/useScrollTo.ts @@ -0,0 +1,60 @@ +export interface ScrollToParams { + el: HTMLElement + to: number + position: string + duration?: number + callback?: () => void +} + +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, position: string, amount: number) => { + el[position] = amount +} + +export function useScrollTo({ + el, + position = 'scrollLeft', + to, + duration = 500, + callback +}: ScrollToParams) { + const isActiveRef = ref(false) + const start = el[position] + const change = to - start + const increment = 20 + let currentTime = 0 + + function animateScroll() { + if (!unref(isActiveRef)) { + return + } + currentTime += increment + const val = easeInOutQuad(currentTime, start, change, duration) + move(el, position, val) + if (currentTime < duration && unref(isActiveRef)) { + requestAnimationFrame(animateScroll) + } else { + if (callback) { + callback() + } + } + } + + function run() { + isActiveRef.value = true + animateScroll() + } + + function stop() { + isActiveRef.value = false + } + + return { start: run, stop } +} diff --git a/src/hooks/web/useCache.ts b/src/hooks/web/useCache.ts new file mode 100644 index 00000000..6d2a9318 --- /dev/null +++ b/src/hooks/web/useCache.ts @@ -0,0 +1,27 @@ +/** + * 配置浏览器本地存储的方式,可直接存储对象数组。 + */ + +import WebStorageCache from 'web-storage-cache' + +type CacheType = 'localStorage' | 'sessionStorage' + +export const CACHE_KEY = { + IS_DARK: 'isDark', + USER: 'user', + LANG: 'lang', + THEME: 'theme', + LAYOUT: 'layout', + ROLE_ROUTERS: 'roleRouters', + DICT_CACHE: 'dictCache' +} + +export const useCache = (type: CacheType = 'localStorage') => { + const wsCache: WebStorageCache = new WebStorageCache({ + storage: type + }) + + return { + wsCache + } +} diff --git a/src/hooks/web/useConfigGlobal.ts b/src/hooks/web/useConfigGlobal.ts new file mode 100644 index 00000000..afb3db3a --- /dev/null +++ b/src/hooks/web/useConfigGlobal.ts @@ -0,0 +1,9 @@ +import { ConfigGlobalTypes } from '@/types/configGlobal' + +export const useConfigGlobal = () => { + const configGlobal = inject('configGlobal', {}) as ConfigGlobalTypes + + return { + configGlobal + } +} diff --git a/src/hooks/web/useCrudSchemas.ts b/src/hooks/web/useCrudSchemas.ts new file mode 100644 index 00000000..34e655b1 --- /dev/null +++ b/src/hooks/web/useCrudSchemas.ts @@ -0,0 +1,293 @@ +import { reactive } from 'vue' +import { AxiosPromise } from 'axios' +import { findIndex } from '@/utils' +import { eachTree, treeMap, filter } from '@/utils/tree' +import { getBoolDictOptions, getDictOptions, getIntDictOptions } from '@/utils/dict' + +import { FormSchema } from '@/types/form' +import { TableColumn } from '@/types/table' +import { DescriptionsSchema } from '@/types/descriptions' +import { ComponentOptions, ComponentProps } from '@/types/components' + +export type CrudSchema = Omit & { + isSearch?: boolean // 是否在查询显示 + search?: CrudSearchParams // 查询的详细配置 + isTable?: boolean // 是否在列表显示 + table?: CrudTableParams // 列表的详细配置 + isForm?: boolean // 是否在表单显示 + form?: CrudFormParams // 表单的详细配置 + isDetail?: boolean // 是否在详情显示 + detail?: CrudDescriptionsParams // 详情的详细配置 + children?: CrudSchema[] + dictType?: string // 字典类型 + dictClass?: 'string' | 'number' | 'boolean' // 字典数据类型 string | number | boolean +} + +type CrudSearchParams = { + // 是否显示在查询项 + show?: boolean + // 接口 + api?: () => Promise +} & Omit + +type CrudTableParams = { + // 是否显示表头 + show?: boolean +} & Omit + +type CrudFormParams = { + // 是否显示表单项 + show?: boolean + // 接口 + api?: () => Promise +} & Omit + +type CrudDescriptionsParams = { + // 是否显示表单项 + show?: boolean +} & Omit + +interface AllSchemas { + searchSchema: FormSchema[] + tableColumns: TableColumn[] + formSchema: FormSchema[] + detailSchema: DescriptionsSchema[] +} + +const { t } = useI18n() + +// 过滤所有结构 +export const useCrudSchemas = ( + crudSchema: CrudSchema[] +): { + allSchemas: AllSchemas +} => { + // 所有结构数据 + const allSchemas = reactive({ + searchSchema: [], + tableColumns: [], + formSchema: [], + detailSchema: [] + }) + + const searchSchema = filterSearchSchema(crudSchema, allSchemas) + allSchemas.searchSchema = searchSchema || [] + + const tableColumns = filterTableSchema(crudSchema) + allSchemas.tableColumns = tableColumns || [] + + const formSchema = filterFormSchema(crudSchema, allSchemas) + allSchemas.formSchema = formSchema + + const detailSchema = filterDescriptionsSchema(crudSchema) + allSchemas.detailSchema = detailSchema + + return { + allSchemas + } +} + +// 过滤 Search 结构 +const filterSearchSchema = (crudSchema: CrudSchema[], allSchemas: AllSchemas): FormSchema[] => { + const searchSchema: FormSchema[] = [] + + // 获取字典列表队列 + const searchRequestTask: Array<() => Promise> = [] + eachTree(crudSchema, (schemaItem: CrudSchema) => { + // 判断是否显示 + if (schemaItem?.isSearch || schemaItem.search?.show) { + let component = schemaItem?.search?.component || 'Input' + const options: ComponentOptions[] = [] + let comonentProps: ComponentProps = {} + if (schemaItem.dictType) { + const allOptions: ComponentOptions = { label: '全部', value: '' } + options.push(allOptions) + getDictOptions(schemaItem.dictType).forEach((dict) => { + options.push(dict) + }) + comonentProps = { + options: options + } + if (!schemaItem.search?.component) component = 'Select' + } + const searchSchemaItem = { + // 默认为 input + component: component, + componentProps: comonentProps, + ...schemaItem.search, + field: schemaItem.field, + label: schemaItem.search?.label || schemaItem.label + } + if (searchSchemaItem.api) { + searchRequestTask.push(async () => { + const res = await (searchSchemaItem.api as () => AxiosPromise)() + if (res) { + const index = findIndex(allSchemas.searchSchema, (v: FormSchema) => { + return v.field === searchSchemaItem.field + }) + if (index !== -1) { + allSchemas.searchSchema[index]!.componentProps!.options = filterOptions( + res, + searchSchemaItem.componentProps.optionsAlias?.labelField + ) + } + } + }) + } + // 删除不必要的字段 + delete searchSchemaItem.show + + searchSchema.push(searchSchemaItem) + } + }) + for (const task of searchRequestTask) { + task() + } + return searchSchema +} + +// 过滤 table 结构 +const filterTableSchema = (crudSchema: CrudSchema[]): TableColumn[] => { + const tableColumns = treeMap(crudSchema, { + conversion: (schema: CrudSchema) => { + if (schema?.isTable !== false && schema?.table?.show !== false) { + return { + ...schema.table, + ...schema + } + } + } + }) + + // 第一次过滤会有 undefined 所以需要二次过滤 + return filter(tableColumns as TableColumn[], (data) => { + if (data.children === void 0) { + delete data.children + } + return !!data.field + }) +} + +// 过滤 form 结构 +const filterFormSchema = (crudSchema: CrudSchema[], allSchemas: AllSchemas): FormSchema[] => { + const formSchema: FormSchema[] = [] + + // 获取字典列表队列 + const formRequestTask: Array<() => Promise> = [] + + eachTree(crudSchema, (schemaItem: CrudSchema) => { + // 判断是否显示 + if (schemaItem?.isForm !== false && schemaItem?.form?.show !== false) { + let component = schemaItem?.form?.component || 'Input' + let defaultValue: any = '' + if (schemaItem.form?.value) { + defaultValue = schemaItem.form?.value + } else { + if (component === 'InputNumber') { + defaultValue = 0 + } + } + let comonentProps: ComponentProps = {} + if (schemaItem.dictType) { + const options: ComponentOptions[] = [] + if (schemaItem.dictClass && schemaItem.dictClass === 'number') { + getIntDictOptions(schemaItem.dictType).forEach((dict) => { + options.push(dict) + }) + } else if (schemaItem.dictClass && schemaItem.dictClass === 'boolean') { + getBoolDictOptions(schemaItem.dictType).forEach((dict) => { + options.push(dict) + }) + } else { + getDictOptions(schemaItem.dictType).forEach((dict) => { + options.push(dict) + }) + } + comonentProps = { + options: options + } + if (!(schemaItem.form && schemaItem.form.component)) component = 'Select' + } + const formSchemaItem = { + // 默认为 input + component: component, + componentProps: comonentProps, + value: defaultValue, + ...schemaItem.form, + field: schemaItem.field, + label: schemaItem.form?.label || schemaItem.label + } + + if (formSchemaItem.api) { + formRequestTask.push(async () => { + const res = await (formSchemaItem.api as () => AxiosPromise)() + if (res) { + const index = findIndex(allSchemas.formSchema, (v: FormSchema) => { + return v.field === formSchemaItem.field + }) + if (index !== -1) { + allSchemas.formSchema[index]!.componentProps!.options = filterOptions( + res, + formSchemaItem.componentProps.optionsAlias?.labelField + ) + } + } + }) + } + + // 删除不必要的字段 + delete formSchemaItem.show + + formSchema.push(formSchemaItem) + } + }) + + for (const task of formRequestTask) { + task() + } + return formSchema +} + +// 过滤 descriptions 结构 +const filterDescriptionsSchema = (crudSchema: CrudSchema[]): DescriptionsSchema[] => { + const descriptionsSchema: FormSchema[] = [] + + eachTree(crudSchema, (schemaItem: CrudSchema) => { + // 判断是否显示 + if (schemaItem?.isDetail !== false && schemaItem.detail?.show !== false) { + const descriptionsSchemaItem = { + ...schemaItem.detail, + field: schemaItem.field, + label: schemaItem.detail?.label || schemaItem.label + } + if (schemaItem.dictType) { + descriptionsSchemaItem.dictType = schemaItem.dictType + } + if (schemaItem.detail?.dateFormat || schemaItem.formatter == 'formatDate') { + // 优先使用 detail 下的配置,如果没有默认为 YYYY-MM-DD HH:mm:ss + descriptionsSchemaItem.dateFormat = schemaItem?.detail?.dateFormat + ? schemaItem?.detail?.dateFormat + : 'YYYY-MM-DD HH:mm:ss' + } + + // 删除不必要的字段 + delete descriptionsSchemaItem.show + + descriptionsSchema.push(descriptionsSchemaItem) + } + }) + + return descriptionsSchema +} + +// 给options添加国际化 +const filterOptions = (options: Recordable, labelField?: string) => { + return options.map((v: Recordable) => { + if (labelField) { + v['labelField'] = t(v.labelField) + } else { + v['label'] = t(v.label) + } + return v + }) +} diff --git a/src/hooks/web/useDesign.ts b/src/hooks/web/useDesign.ts new file mode 100644 index 00000000..8ee3b38c --- /dev/null +++ b/src/hooks/web/useDesign.ts @@ -0,0 +1,18 @@ +import variables from '@/styles/global.module.scss' + +export const useDesign = () => { + const scssVariables = variables + + /** + * @param scope 类名 + * @returns 返回空间名-类名 + */ + const getPrefixCls = (scope: string) => { + return `${scssVariables.namespace}-${scope}` + } + + return { + variables: scssVariables, + getPrefixCls + } +} diff --git a/src/hooks/web/useEmitt.ts b/src/hooks/web/useEmitt.ts new file mode 100644 index 00000000..d4efea72 --- /dev/null +++ b/src/hooks/web/useEmitt.ts @@ -0,0 +1,22 @@ +import mitt from 'mitt' + +interface Option { + name: string // 事件名称 + callback: Fn // 回调 +} + +const emitter = mitt() + +export const useEmitt = (option?: Option) => { + if (option) { + emitter.on(option.name, option.callback) + + onBeforeUnmount(() => { + emitter.off(option.name) + }) + } + + return { + emitter + } +} diff --git a/src/hooks/web/useForm.ts b/src/hooks/web/useForm.ts new file mode 100644 index 00000000..53a8a94d --- /dev/null +++ b/src/hooks/web/useForm.ts @@ -0,0 +1,94 @@ +import type { Form, FormExpose } from '@/components/Form' +import type { ElForm } from 'element-plus' +import type { FormProps } from '@/components/Form/src/types' +import { FormSchema, FormSetPropsType } from '@/types/form' + +export const useForm = (props?: FormProps) => { + // From实例 + const formRef = ref() + + // ElForm实例 + const elFormRef = ref>() + + /** + * @param ref Form实例 + * @param elRef ElForm实例 + */ + const register = (ref: typeof Form & FormExpose, elRef: ComponentRef) => { + formRef.value = ref + elFormRef.value = elRef + } + + const getForm = async () => { + await nextTick() + const form = unref(formRef) + if (!form) { + console.error('The form is not registered. Please use the register method to register') + } + return form + } + + // 一些内置的方法 + const methods: { + setProps: (props: Recordable) => void + setValues: (data: Recordable) => void + getFormData: () => Promise + setSchema: (schemaProps: FormSetPropsType[]) => void + addSchema: (formSchema: FormSchema, index?: number) => void + delSchema: (field: string) => void + } = { + setProps: async (props: FormProps = {}) => { + const form = await getForm() + form?.setProps(props) + if (props.model) { + form?.setValues(props.model) + } + }, + + setValues: async (data: Recordable) => { + const form = await getForm() + form?.setValues(data) + }, + + /** + * @param schemaProps 需要设置的schemaProps + */ + setSchema: async (schemaProps: FormSetPropsType[]) => { + const form = await getForm() + form?.setSchema(schemaProps) + }, + + /** + * @param formSchema 需要新增数据 + * @param index 在哪里新增 + */ + addSchema: async (formSchema: FormSchema, index?: number) => { + const form = await getForm() + form?.addSchema(formSchema, index) + }, + + /** + * @param field 删除哪个数据 + */ + delSchema: async (field: string) => { + const form = await getForm() + form?.delSchema(field) + }, + + /** + * @returns form data + */ + getFormData: async (): Promise => { + const form = await getForm() + return form?.formModel as T + } + } + + props && methods.setProps(props) + + return { + register, + elFormRef, + methods + } +} diff --git a/src/hooks/web/useI18n.ts b/src/hooks/web/useI18n.ts new file mode 100644 index 00000000..8b6e7650 --- /dev/null +++ b/src/hooks/web/useI18n.ts @@ -0,0 +1,52 @@ +import { i18n } from '@/plugins/vueI18n' + +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] + +const getKey = (namespace: string | undefined, key: string) => { + if (!namespace) { + return key + } + if (key.startsWith(namespace)) { + return key + } + return `${namespace}.${key}` +} + +export const 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 + } +} + +export const t = (key: string) => key diff --git a/src/hooks/web/useIcon.ts b/src/hooks/web/useIcon.ts new file mode 100644 index 00000000..35002049 --- /dev/null +++ b/src/hooks/web/useIcon.ts @@ -0,0 +1,8 @@ +import { h } from 'vue' +import type { VNode } from 'vue' +import { Icon } from '@/components/Icon' +import { IconTypes } from '@/types/icon' + +export const useIcon = (props: IconTypes): VNode => { + return h(Icon, props) +} diff --git a/src/hooks/web/useIntro.ts b/src/hooks/web/useIntro.ts new file mode 100644 index 00000000..7fe00845 --- /dev/null +++ b/src/hooks/web/useIntro.ts @@ -0,0 +1,47 @@ +import introJs from 'intro.js' +import { IntroJs, Step, Options } from 'intro.js' +import 'intro.js/introjs.css' + +import { useDesign } from '@/hooks/web/useDesign' + +export const useIntro = (setps?: Step[], options?: Options) => { + const { t } = useI18n() + + const { variables } = useDesign() + + const defaultSetps: Step[] = setps || [ + { + element: `#${variables.namespace}-menu`, + title: t('common.menu'), + intro: t('common.menuDes'), + position: 'right' + }, + { + element: `#${variables.namespace}-tool-header`, + title: t('common.tool'), + intro: t('common.toolDes'), + position: 'left' + }, + { + element: `#${variables.namespace}-tags-view`, + title: t('common.tagsView'), + intro: t('common.tagsViewDes'), + position: 'bottom' + } + ] + + const defaultOptions: Options = options || { + prevLabel: t('common.prevLabel'), + nextLabel: t('common.nextLabel'), + skipLabel: t('common.skipLabel'), + doneLabel: t('common.doneLabel') + } + + const introRef: IntroJs = introJs() + + introRef.addSteps(defaultSetps).setOptions(defaultOptions) + + return { + introRef + } +} diff --git a/src/hooks/web/useLocale.ts b/src/hooks/web/useLocale.ts new file mode 100644 index 00000000..c65070ef --- /dev/null +++ b/src/hooks/web/useLocale.ts @@ -0,0 +1,35 @@ +import { i18n } from '@/plugins/vueI18n' +import { useLocaleStoreWithOut } from '@/store/modules/locale' +import { setHtmlPageLang } from '@/plugins/vueI18n/helper' + +const setI18nLanguage = (locale: LocaleType) => { + const localeStore = useLocaleStoreWithOut() + + if (i18n.mode === 'legacy') { + i18n.global.locale = locale + } else { + ;(i18n.global.locale as any).value = locale + } + localeStore.setCurrentLocale({ + lang: locale + }) + setHtmlPageLang(locale) +} + +export const useLocale = () => { + // Switching the language will change the locale of useI18n + // And submit to configuration modification + const changeLocale = async (locale: LocaleType) => { + const globalI18n = i18n.global + + const langModule = await import(`../../locales/${locale}.ts`) + + globalI18n.setLocaleMessage(locale, langModule.default) + + setI18nLanguage(locale) + } + + return { + changeLocale + } +} diff --git a/src/hooks/web/useMessage.ts b/src/hooks/web/useMessage.ts new file mode 100644 index 00000000..ac2b552e --- /dev/null +++ b/src/hooks/web/useMessage.ts @@ -0,0 +1,95 @@ +import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' +import { useI18n } from './useI18n' +export const useMessage = () => { + const { t } = useI18n() + return { + // 消息提示 + info(content: string) { + ElMessage.info(content) + }, + // 错误消息 + error(content: string) { + ElMessage.error(content) + }, + // 成功消息 + success(content: string) { + ElMessage.success(content) + }, + // 警告消息 + warning(content: string) { + ElMessage.warning(content) + }, + // 弹出提示 + alert(content: string) { + ElMessageBox.alert(content, t('common.confirmTitle')) + }, + // 错误提示 + alertError(content: string) { + ElMessageBox.alert(content, t('common.confirmTitle'), { type: 'error' }) + }, + // 成功提示 + alertSuccess(content: string) { + ElMessageBox.alert(content, t('common.confirmTitle'), { type: 'success' }) + }, + // 警告提示 + alertWarning(content: string) { + ElMessageBox.alert(content, t('common.confirmTitle'), { type: 'warning' }) + }, + // 通知提示 + notify(content: string) { + ElNotification.info(content) + }, + // 错误通知 + notifyError(content: string) { + ElNotification.error(content) + }, + // 成功通知 + notifySuccess(content: string) { + ElNotification.success(content) + }, + // 警告通知 + notifyWarning(content: string) { + ElNotification.warning(content) + }, + // 确认窗体 + confirm(content: string, tip?: string) { + return ElMessageBox.confirm(content, tip ? tip : t('common.confirmTitle'), { + confirmButtonText: t('common.ok'), + cancelButtonText: t('common.cancel'), + type: 'warning' + }) + }, + // 删除窗体 + delConfirm(content?: string, tip?: string) { + return ElMessageBox.confirm( + content ? content : t('common.delMessage'), + tip ? tip : t('common.confirmTitle'), + { + confirmButtonText: t('common.ok'), + cancelButtonText: t('common.cancel'), + type: 'warning' + } + ) + }, + // 导出窗体 + exportConfirm(content?: string, tip?: string) { + return ElMessageBox.confirm( + content ? content : t('common.exportMessage'), + tip ? tip : t('common.confirmTitle'), + { + confirmButtonText: t('common.ok'), + cancelButtonText: t('common.cancel'), + type: 'warning' + } + ) + }, + // 提交内容 + prompt(content: string, tip: string) { + return ElMessageBox.prompt(content, tip, { + confirmButtonText: t('common.ok'), + cancelButtonText: t('common.cancel'), + type: 'warning' + }) + } + } +} diff --git a/src/hooks/web/useNProgress.ts b/src/hooks/web/useNProgress.ts new file mode 100644 index 00000000..6d8c0b91 --- /dev/null +++ b/src/hooks/web/useNProgress.ts @@ -0,0 +1,33 @@ +import { useCssVar } from '@vueuse/core' +import type { NProgressOptions } from 'nprogress' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' + +const primaryColor = useCssVar('--el-color-primary', document.documentElement) + +export const useNProgress = () => { + NProgress.configure({ showSpinner: false } as NProgressOptions) + + const initColor = async () => { + await nextTick() + const bar = document.getElementById('nprogress')?.getElementsByClassName('bar')[0] as ElRef + if (bar) { + bar.style.background = unref(primaryColor.value) + } + } + + initColor() + + const start = () => { + NProgress.start() + } + + const done = () => { + NProgress.done() + } + + return { + start, + done + } +} diff --git a/src/hooks/web/usePageLoading.ts b/src/hooks/web/usePageLoading.ts new file mode 100644 index 00000000..bb89457d --- /dev/null +++ b/src/hooks/web/usePageLoading.ts @@ -0,0 +1,18 @@ +import { useAppStoreWithOut } from '@/store/modules/app' + +const appStore = useAppStoreWithOut() + +export const usePageLoading = () => { + const loadStart = () => { + appStore.setPageLoading(true) + } + + const loadDone = () => { + appStore.setPageLoading(false) + } + + return { + loadStart, + loadDone + } +} diff --git a/src/hooks/web/useTable.ts b/src/hooks/web/useTable.ts new file mode 100644 index 00000000..7a9b1afe --- /dev/null +++ b/src/hooks/web/useTable.ts @@ -0,0 +1,223 @@ +import download from '@/utils/download' +import { Table, TableExpose } from '@/components/Table' +import { ElMessage, ElMessageBox, ElTable } from 'element-plus' +import { computed, nextTick, reactive, ref, unref, watch } from 'vue' +import type { TableProps } from '@/components/Table/src/types' + +import { TableSetPropsType } from '@/types/table' + +const { t } = useI18n() +interface ResponseType { + list: T[] + total?: number +} + +interface UseTableConfig { + getListApi: (option: any) => Promise + delListApi?: (option: any) => Promise + exportListApi?: (option: any) => Promise + // 返回数据格式配置 + response?: ResponseType + // 默认传递的参数 + defaultParams?: Recordable + props?: TableProps +} + +interface TableObject { + pageSize: number + currentPage: number + total: number + tableList: T[] + params: any + loading: boolean + exportLoading: boolean + currentRow: Nullable +} + +export const useTable = (config?: UseTableConfig) => { + const tableObject = reactive>({ + // 页数 + pageSize: 10, + // 当前页 + currentPage: 1, + // 总条数 + total: 10, + // 表格数据 + tableList: [], + // AxiosConfig 配置 + params: { + ...(config?.defaultParams || {}) + }, + // 加载中 + loading: true, + // 导出加载中 + exportLoading: false, + // 当前行的数据 + currentRow: null + }) + + const paramsObj = computed(() => { + return { + ...tableObject.params, + pageSize: tableObject.pageSize, + pageNo: tableObject.currentPage + } + }) + + watch( + () => tableObject.currentPage, + () => { + methods.getList() + } + ) + + watch( + () => tableObject.pageSize, + () => { + // 当前页不为1时,修改页数后会导致多次调用getList方法 + if (tableObject.currentPage === 1) { + methods.getList() + } else { + tableObject.currentPage = 1 + methods.getList() + } + } + ) + + // Table实例 + const tableRef = ref() + + // ElTable实例 + const elTableRef = ref>() + + const register = (ref: typeof Table & TableExpose, elRef: ComponentRef) => { + tableRef.value = ref + elTableRef.value = elRef + } + + const getTable = async () => { + await nextTick() + const table = unref(tableRef) + if (!table) { + console.error('The table is not registered. Please use the register method to register') + } + return table + } + + const delData = async (ids: string | number | string[] | number[]) => { + let idsLength = 1 + if (ids instanceof Array) { + idsLength = ids.length + await Promise.all( + ids.map(async (id: string | number) => { + await (config?.delListApi && config?.delListApi(id)) + }) + ) + } else { + await (config?.delListApi && config?.delListApi(ids)) + } + ElMessage.success(t('common.delSuccess')) + + // 计算出临界点 + tableObject.currentPage = + tableObject.total % tableObject.pageSize === idsLength || tableObject.pageSize === 1 + ? tableObject.currentPage > 1 + ? tableObject.currentPage - 1 + : tableObject.currentPage + : tableObject.currentPage + await methods.getList() + } + + const methods = { + getList: async () => { + tableObject.loading = true + const res = await config?.getListApi(unref(paramsObj)).finally(() => { + tableObject.loading = false + }) + if (res) { + tableObject.tableList = (res as unknown as ResponseType).list + if ((res as unknown as ResponseType).total) { + tableObject.total = (res as unknown as ResponseType).total as unknown as number + } + } + }, + setProps: async (props: TableProps = {}) => { + const table = await getTable() + table?.setProps(props) + }, + setColumn: async (columnProps: TableSetPropsType[]) => { + const table = await getTable() + table?.setColumn(columnProps) + }, + getSelections: async () => { + const table = await getTable() + return (table?.selections || []) as T[] + }, + // 与Search组件结合 + setSearchParams: (data: Recordable) => { + tableObject.params = Object.assign(tableObject.params, { + pageSize: tableObject.pageSize, + pageNo: 1, + ...data + }) + // 页码不等于1时更新页码重新获取数据,页码等于1时重新获取数据 + if (tableObject.currentPage !== 1) { + tableObject.currentPage = 1 + } else { + methods.getList() + } + }, + // 删除数据 + delList: async ( + ids: string | number | string[] | number[], + multiple: boolean, + message = true + ) => { + const tableRef = await getTable() + if (multiple) { + if (!tableRef?.selections.length) { + ElMessage.warning(t('common.delNoData')) + return + } + } + if (message) { + ElMessageBox.confirm(t('common.delMessage'), t('common.confirmTitle'), { + confirmButtonText: t('common.ok'), + cancelButtonText: t('common.cancel'), + type: 'warning' + }).then(async () => { + await delData(ids) + }) + } else { + await delData(ids) + } + }, + // 导出列表 + exportList: async (fileName: string) => { + tableObject.exportLoading = true + ElMessageBox.confirm(t('common.exportMessage'), t('common.confirmTitle'), { + confirmButtonText: t('common.ok'), + cancelButtonText: t('common.cancel'), + type: 'warning' + }) + .then(async () => { + const res = await config?.exportListApi?.(unref(paramsObj) as unknown as T) + if (res) { + download.excel(res as unknown as Blob, fileName) + } + }) + .finally(() => { + tableObject.exportLoading = false + }) + } + } + + config?.props && methods.setProps(config.props) + + return { + register, + elTableRef, + tableObject, + methods + } +} diff --git a/src/hooks/web/useTimeAgo.ts b/src/hooks/web/useTimeAgo.ts new file mode 100644 index 00000000..08f5d1e6 --- /dev/null +++ b/src/hooks/web/useTimeAgo.ts @@ -0,0 +1,47 @@ +import { useTimeAgo as useTimeAgoCore, UseTimeAgoMessages } from '@vueuse/core' +import { useLocaleStoreWithOut } from '@/store/modules/locale' + +const TIME_AGO_MESSAGE_MAP: { + 'zh-CN': UseTimeAgoMessages + en: UseTimeAgoMessages +} = { + 'zh-CN': { + justNow: '刚刚', + past: (n) => (n.match(/\d/) ? `${n}前` : n), + future: (n) => (n.match(/\d/) ? `${n}后` : n), + month: (n, past) => (n === 1 ? (past ? '上个月' : '下个月') : `${n} 个月`), + year: (n, past) => (n === 1 ? (past ? '去年' : '明年') : `${n} 年`), + day: (n, past) => (n === 1 ? (past ? '昨天' : '明天') : `${n} 天`), + week: (n, past) => (n === 1 ? (past ? '上周' : '下周') : `${n} 周`), + hour: (n) => `${n} 小时`, + minute: (n) => `${n} 分钟`, + second: (n) => `${n} 秒` + }, + en: { + justNow: 'just now', + past: (n) => (n.match(/\d/) ? `${n} ago` : n), + future: (n) => (n.match(/\d/) ? `in ${n}` : n), + month: (n, past) => + n === 1 ? (past ? 'last month' : 'next month') : `${n} month${n > 1 ? 's' : ''}`, + year: (n, past) => + n === 1 ? (past ? 'last year' : 'next year') : `${n} year${n > 1 ? 's' : ''}`, + day: (n, past) => (n === 1 ? (past ? 'yesterday' : 'tomorrow') : `${n} day${n > 1 ? 's' : ''}`), + week: (n, past) => + n === 1 ? (past ? 'last week' : 'next week') : `${n} week${n > 1 ? 's' : ''}`, + hour: (n) => `${n} hour${n > 1 ? 's' : ''}`, + minute: (n) => `${n} minute${n > 1 ? 's' : ''}`, + second: (n) => `${n} second${n > 1 ? 's' : ''}` + } +} + +export const useTimeAgo = (time: Date | number | string) => { + const localeStore = useLocaleStoreWithOut() + + const currentLocale = computed(() => localeStore.getCurrentLocale) + + const timeAgo = useTimeAgoCore(time, { + messages: TIME_AGO_MESSAGE_MAP[unref(currentLocale).lang] + }) + + return timeAgo +} diff --git a/src/hooks/web/useTitle.ts b/src/hooks/web/useTitle.ts new file mode 100644 index 00000000..020a9b77 --- /dev/null +++ b/src/hooks/web/useTitle.ts @@ -0,0 +1,24 @@ +import { watch, ref } from 'vue' +import { isString } from '@/utils/is' +import { useAppStoreWithOut } from '@/store/modules/app' + +const appStore = useAppStoreWithOut() + +export const useTitle = (newTitle?: string) => { + const { t } = useI18n() + const title = ref( + newTitle ? `${appStore.getTitle} - ${t(newTitle as string)}` : appStore.getTitle + ) + + watch( + title, + (n, o) => { + if (isString(n) && n !== o && document) { + document.title = n + } + }, + { immediate: true } + ) + + return title +} diff --git a/src/hooks/web/useValidator.ts b/src/hooks/web/useValidator.ts new file mode 100644 index 00000000..0c16fa31 --- /dev/null +++ b/src/hooks/web/useValidator.ts @@ -0,0 +1,62 @@ +const { t } = useI18n() + +type Callback = (error?: string | Error | undefined) => void + +interface LengthRange { + min: number + max: number + message: string +} + +export const useValidator = () => { + const required = (message?: string) => { + return { + required: true, + message: message || t('common.required') + } + } + + const lengthRange = (val: any, callback: Callback, options: LengthRange) => { + const { min, max, message } = options + if (val.length < min || val.length > max) { + callback(new Error(message)) + } else { + callback() + } + } + + const notSpace = (val: any, callback: Callback, message: string) => { + // 用户名不能有空格 + if (val.indexOf(' ') !== -1) { + callback(new Error(message)) + } else { + callback() + } + } + + const notSpecialCharacters = (val: any, callback: Callback, message: string) => { + // 密码不能是特殊字符 + if (/[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/gi.test(val)) { + callback(new Error(message)) + } else { + callback() + } + } + + // 两个字符串是否想等 + const isEqual = (val1: string, val2: string, callback: Callback, message: string) => { + if (val1 === val2) { + callback() + } else { + callback(new Error(message)) + } + } + + return { + required, + lengthRange, + notSpace, + notSpecialCharacters, + isEqual + } +} diff --git a/src/hooks/web/useVxeCrudSchemas.ts b/src/hooks/web/useVxeCrudSchemas.ts new file mode 100644 index 00000000..5c765d4f --- /dev/null +++ b/src/hooks/web/useVxeCrudSchemas.ts @@ -0,0 +1,354 @@ +import { + FormItemRenderOptions, + VxeColumnPropTypes, + VxeFormItemProps, + VxeGridPropTypes, + VxeTableDefines +} from 'vxe-table' +import { eachTree } from 'xe-utils' + +import { getBoolDictOptions, getDictOptions, getIntDictOptions } from '@/utils/dict' +import { FormSchema } from '@/types/form' +import { VxeTableColumn } from '@/types/table' +import { ComponentOptions } from '@/types/components' +import { DescriptionsSchema } from '@/types/descriptions' + +export type VxeCrudSchema = { + primaryKey?: string // 主键ID + primaryTitle?: string // 主键标题 默认为序号 + primaryType?: VxeColumnPropTypes.Type | 'id' // 还支持 "id" | "seq" | "radio" | "checkbox" | "expand" | "html" | null + firstColumn?: VxeColumnPropTypes.Type // 第一列显示类型 + action?: boolean // 是否开启表格内右侧操作栏插槽 + actionTitle?: string // 操作栏标题 默认为操作 + actionWidth?: string // 操作栏插槽宽度,一般2个字带图标 text 类型按钮 50-70 + columns: VxeCrudColumns[] + searchSpan?: number +} +type VxeCrudColumns = Omit & { + field: string // 字段名 + title?: string // 标题名 + formatter?: VxeColumnPropTypes.Formatter // vxe formatter格式化 + isSearch?: boolean // 是否在查询显示 + search?: CrudSearchParams // 查询的详细配置 + isTable?: boolean // 是否在列表显示 + table?: CrudTableParams // 列表的详细配置 + isForm?: boolean // 是否在表单显示 + form?: CrudFormParams // 表单的详细配置 + isDetail?: boolean // 是否在详情显示 + detail?: CrudDescriptionsParams // 详情的详细配置 + print?: CrudPrintParams // vxe 打印的字段 + children?: VxeCrudColumns[] // 子级 + dictType?: string // 字典类型 + dictClass?: 'string' | 'number' | 'boolean' // 字典数据类型 string | number | boolean +} + +type CrudSearchParams = { + // 是否显示在查询项 + show?: boolean +} & Omit + +type CrudTableParams = { + // 是否显示表头 + show?: boolean +} & Omit + +type CrudFormParams = { + // 是否显示表单项 + show?: boolean +} & Omit + +type CrudDescriptionsParams = { + // 是否显示表单项 + show?: boolean +} & Omit + +type CrudPrintParams = { + // 是否显示打印项 + show?: boolean +} & Omit + +export type VxeAllSchemas = { + searchSchema: VxeFormItemProps[] + tableSchema: VxeGridPropTypes.Columns + formSchema: FormSchema[] + detailSchema: DescriptionsSchema[] + printSchema: VxeTableDefines.ColumnInfo[] +} + +// 过滤所有结构 +export const useVxeCrudSchemas = ( + crudSchema: VxeCrudSchema +): { + allSchemas: VxeAllSchemas +} => { + // 所有结构数据 + const allSchemas = reactive({ + searchSchema: [], + tableSchema: [], + formSchema: [], + detailSchema: [], + printSchema: [] + }) + + const searchSchema = filterSearchSchema(crudSchema) + allSchemas.searchSchema = searchSchema || [] + + const tableSchema = filterTableSchema(crudSchema) + allSchemas.tableSchema = tableSchema || [] + + const formSchema = filterFormSchema(crudSchema) + allSchemas.formSchema = formSchema + + const detailSchema = filterDescriptionsSchema(crudSchema) + allSchemas.detailSchema = detailSchema + + const printSchema = filterPrintSchema(crudSchema) + allSchemas.printSchema = printSchema + + return { + allSchemas + } +} + +// 过滤 Search 结构 +const filterSearchSchema = (crudSchema: VxeCrudSchema): VxeFormItemProps[] => { + const { t } = useI18n() + const span = crudSchema.searchSpan ? crudSchema.searchSpan : 6 + const spanLength = 24 / span + const searchSchema: VxeFormItemProps[] = [] + eachTree(crudSchema.columns, (schemaItem: VxeCrudColumns) => { + // 判断是否显示 + if (schemaItem?.isSearch || schemaItem.search?.show) { + let itemRenderName = schemaItem?.search?.itemRender?.name || '$input' + const options: any[] = [] + let itemRender: FormItemRenderOptions = {} + + if (schemaItem.dictType) { + const allOptions = { label: '全部', value: '' } + options.push(allOptions) + getDictOptions(schemaItem.dictType).forEach((dict) => { + options.push(dict) + }) + itemRender.options = options + if (!schemaItem?.search?.itemRender?.name) itemRenderName = '$select' + itemRender = { + name: itemRenderName, + options: options, + props: { placeholder: t('common.selectText') } + } + } else { + if (schemaItem.search?.itemRender) { + itemRender = schemaItem.search.itemRender + } else { + itemRender = { + name: itemRenderName, + props: + itemRenderName == '$input' + ? { placeholder: t('common.inputText') } + : { placeholder: t('common.selectText') } + } + } + } + const searchSchemaItem = { + // 默认为 input + folding: searchSchema.length > spanLength - 1, + itemRender: schemaItem.itemRender ? schemaItem.itemRender : itemRender, + field: schemaItem.field, + title: schemaItem.search?.title || schemaItem.title, + slots: schemaItem.search?.slots, + span: span + } + searchSchema.push(searchSchemaItem) + } + }) + if (searchSchema.length > 0) { + // 添加搜索按钮 + const buttons: VxeFormItemProps = { + span: 24, + align: 'right', + collapseNode: searchSchema.length > spanLength, + itemRender: { + name: '$buttons', + children: [ + { props: { type: 'submit', content: t('common.query'), status: 'primary' } }, + { props: { type: 'reset', content: t('common.reset') } } + ] + } + } + searchSchema.push(buttons) + } + return searchSchema +} + +// 过滤 table 结构 +const filterTableSchema = (crudSchema: VxeCrudSchema): VxeGridPropTypes.Columns => { + const { t } = useI18n() + const tableSchema: VxeGridPropTypes.Columns = [] + // 第一列 + if (crudSchema.firstColumn) { + const tableSchemaItem = { + type: crudSchema.firstColumn, + width: '50px' + } + tableSchema.push(tableSchemaItem) + } + // 主键ID + if (crudSchema.primaryKey && crudSchema.primaryType) { + const primaryTitle = crudSchema.primaryTitle ? crudSchema.primaryTitle : t('common.index') + const primaryWidth = primaryTitle.length * 30 + 'px' + + let tableSchemaItem: { [x: string]: any } = { + title: primaryTitle, + field: crudSchema.primaryKey, + width: primaryWidth + } + if (crudSchema.primaryType != 'id') { + tableSchemaItem = { + ...tableSchemaItem, + type: crudSchema.primaryType + } + } + tableSchema.push(tableSchemaItem) + } + + eachTree(crudSchema.columns, (schemaItem: VxeCrudColumns) => { + // 判断是否显示 + if (schemaItem?.isTable !== false && schemaItem?.table?.show !== false) { + const tableSchemaItem = { + ...schemaItem.table, + field: schemaItem.field, + title: schemaItem.table?.title || schemaItem.title, + minWidth: '80px' + } + tableSchemaItem.showOverflow = 'tooltip' + if (schemaItem?.formatter) { + tableSchemaItem.formatter = schemaItem.formatter + tableSchemaItem.width = tableSchemaItem.width ? tableSchemaItem.width : 160 + } + if (schemaItem?.dictType) { + tableSchemaItem.cellRender = { + name: 'XDict', + content: schemaItem.dictType + } + tableSchemaItem.width = tableSchemaItem.width ? tableSchemaItem.width : 160 + } + + tableSchema.push(tableSchemaItem) + } + }) + // 操作栏插槽 + if (crudSchema.action && crudSchema.action == true) { + const tableSchemaItem = { + title: crudSchema.actionTitle ? crudSchema.actionTitle : t('table.action'), + field: 'actionbtns', + fixed: 'right' as unknown as VxeColumnPropTypes.Fixed, + width: crudSchema.actionWidth ? crudSchema.actionWidth : '200px', + slots: { + default: 'actionbtns_default' + } + } + tableSchema.push(tableSchemaItem) + } + return tableSchema +} + +// 过滤 form 结构 +const filterFormSchema = (crudSchema: VxeCrudSchema): FormSchema[] => { + const formSchema: FormSchema[] = [] + + eachTree(crudSchema.columns, (schemaItem: VxeCrudColumns) => { + // 判断是否显示 + if (schemaItem?.isForm !== false && schemaItem?.form?.show !== false) { + // 默认为 input + let component = schemaItem?.form?.component || 'Input' + let defaultValue: any = '' + if (schemaItem.form?.value) { + defaultValue = schemaItem.form?.value + } else { + if (component === 'InputNumber') { + defaultValue = 0 + } + } + let comonentProps = {} + if (schemaItem.dictType) { + const options: ComponentOptions[] = [] + if (schemaItem.dictClass && schemaItem.dictClass === 'number') { + getIntDictOptions(schemaItem.dictType).forEach((dict) => { + options.push(dict) + }) + } else if (schemaItem.dictClass && schemaItem.dictClass === 'boolean') { + getBoolDictOptions(schemaItem.dictType).forEach((dict) => { + options.push(dict) + }) + } else { + getDictOptions(schemaItem.dictType).forEach((dict) => { + options.push(dict) + }) + } + comonentProps = { + options: options + } + if (!(schemaItem.form && schemaItem.form.component)) component = 'Select' + } + const formSchemaItem = { + component: component, + componentProps: comonentProps, + value: defaultValue, + ...schemaItem.form, + field: schemaItem.field, + label: schemaItem.form?.label || schemaItem.title + } + + formSchema.push(formSchemaItem) + } + }) + + return formSchema +} + +// 过滤 descriptions 结构 +const filterDescriptionsSchema = (crudSchema: VxeCrudSchema): DescriptionsSchema[] => { + const descriptionsSchema: DescriptionsSchema[] = [] + + eachTree(crudSchema.columns, (schemaItem: VxeCrudColumns) => { + // 判断是否显示 + if (schemaItem?.isDetail !== false && schemaItem.detail?.show !== false) { + const descriptionsSchemaItem = { + ...schemaItem.detail, + field: schemaItem.field, + label: schemaItem.detail?.label || schemaItem.title + } + if (schemaItem.dictType) { + descriptionsSchemaItem.dictType = schemaItem.dictType + } + if (schemaItem.detail?.dateFormat || schemaItem.formatter == 'formatDate') { + // 优先使用 detail 下的配置,如果没有默认为 YYYY-MM-DD HH:mm:ss + descriptionsSchemaItem.dateFormat = schemaItem?.detail?.dateFormat + ? schemaItem?.detail?.dateFormat + : 'YYYY-MM-DD HH:mm:ss' + } + + descriptionsSchema.push(descriptionsSchemaItem) + } + }) + + return descriptionsSchema +} + +// 过滤 打印 结构 +const filterPrintSchema = (crudSchema: VxeCrudSchema): any[] => { + const printSchema: any[] = [] + + eachTree(crudSchema.columns, (schemaItem: VxeCrudColumns) => { + // 判断是否显示 + if (schemaItem?.print?.show !== false) { + const printSchemaItem = { + field: schemaItem.field + } + + printSchema.push(printSchemaItem) + } + }) + + return printSchema +} diff --git a/src/hooks/web/useVxeGrid.ts b/src/hooks/web/useVxeGrid.ts new file mode 100644 index 00000000..5ac07c86 --- /dev/null +++ b/src/hooks/web/useVxeGrid.ts @@ -0,0 +1,264 @@ +import { computed, nextTick, reactive } from 'vue' +import { SizeType, VxeGridProps, VxeTablePropTypes } from 'vxe-table' +import { useAppStore } from '@/store/modules/app' +import { VxeAllSchemas } from './useVxeCrudSchemas' + +import download from '@/utils/download' + +const { t } = useI18n() +const message = useMessage() // 消息弹窗 + +interface UseVxeGridConfig { + allSchemas: VxeAllSchemas + height?: number // 高度 默认730 + topActionSlots?: boolean // 是否开启表格内顶部操作栏插槽 + treeConfig?: VxeTablePropTypes.TreeConfig // 树形表单配置 + isList?: boolean // 是否不带分页的list + getListApi: (option: any) => Promise // 获取列表接口 + getAllListApi?: (option: any) => Promise // 获取全部数据接口 用于VXE导出 + deleteApi?: (option: any) => Promise // 删除接口 + exportListApi?: (option: any) => Promise // 导出接口 + exportName?: string // 导出文件夹名称 + queryParams?: any // 其他查询参数 +} + +const appStore = useAppStore() + +const currentSize = computed(() => { + let resSize: SizeType = 'small' + const appsize = appStore.getCurrentSize + switch (appsize) { + case 'large': + resSize = 'medium' + break + case 'default': + resSize = 'small' + break + case 'small': + resSize = 'mini' + break + } + return resSize +}) + +export const useVxeGrid = (config?: UseVxeGridConfig) => { + /** + * grid options 初始化 + */ + const gridOptions = reactive>({ + loading: true, + size: currentSize as any, + height: config?.height ? config.height : 730, + rowConfig: { + isCurrent: true, // 当鼠标点击行时,是否要高亮当前行 + isHover: true // 当鼠标移到行时,是否要高亮当前行 + }, + toolbarConfig: { + slots: + !config?.topActionSlots && config?.topActionSlots != false + ? { buttons: 'toolbar_buttons' } + : {} + }, + printConfig: { + columns: config?.allSchemas.printSchema + }, + formConfig: { + enabled: true, + titleWidth: 100, + titleAlign: 'right', + items: config?.allSchemas.searchSchema + }, + columns: config?.allSchemas.tableSchema, + proxyConfig: { + seq: true, // 启用动态序号代理(分页之后索引自动计算为当前页的起始序号) + form: true, // 启用表单代理,当点击表单提交按钮时会自动触发 reload 行为 + props: { result: 'list', total: 'total' }, + ajax: { + query: ({ page, form }) => { + let queryParams: any = Object.assign({}, JSON.parse(JSON.stringify(form))) + if (config?.queryParams) { + queryParams = Object.assign(queryParams, config.queryParams) + } + if (!config?.treeConfig) { + queryParams.pageSize = page.pageSize + queryParams.pageNo = page.currentPage + } + gridOptions.loading = false + return new Promise(async (resolve) => { + resolve(await config?.getListApi(queryParams)) + }) + }, + delete: ({ body }) => { + return new Promise(async (resolve) => { + if (config?.deleteApi) { + resolve(await config?.deleteApi(JSON.stringify(body))) + } else { + Promise.reject('未设置deleteApi') + } + }) + }, + queryAll: ({ form }) => { + const queryParams = Object.assign({}, JSON.parse(JSON.stringify(form))) + return new Promise(async (resolve) => { + if (config?.getAllListApi) { + resolve(await config?.getAllListApi(queryParams)) + } else { + resolve(await config?.getListApi(queryParams)) + } + }) + } + } + }, + exportConfig: { + filename: config?.exportName, + // 默认选中类型 + type: 'csv', + // 自定义数据量列表 + modes: config?.getAllListApi ? ['current', 'all'] : ['current'], + columns: config?.allSchemas.printSchema + } + }) + + if (config?.treeConfig) { + gridOptions.treeConfig = config.treeConfig + } else if (config?.isList) { + gridOptions.proxyConfig = { + seq: true, // 启用动态序号代理(分页之后索引自动计算为当前页的起始序号) + form: true, // 启用表单代理,当点击表单提交按钮时会自动触发 reload 行为 + props: { result: 'data' }, + ajax: { + query: ({ form }) => { + let queryParams: any = Object.assign({}, JSON.parse(JSON.stringify(form))) + if (config?.queryParams) { + queryParams = Object.assign(queryParams, config.queryParams) + } + gridOptions.loading = false + return new Promise(async (resolve) => { + resolve(await config?.getListApi(queryParams)) + }) + } + } + } + } else { + gridOptions.pagerConfig = { + border: false, // 带边框 + background: true, // 带背景颜色 + perfect: false, // 配套的样式 + pageSize: 10, // 每页大小 + pagerCount: 7, // 显示页码按钮的数量 + autoHidden: false, // 当只有一页时自动隐藏 + pageSizes: [5, 10, 20, 30, 50, 100], // 每页大小选项列表 + layouts: [ + 'PrevJump', + 'PrevPage', + 'JumpNumber', + 'NextPage', + 'NextJump', + 'Sizes', + 'FullJump', + 'Total' + ] + } + } + + /** + * 刷新列表 + * @param ref + * @returns + */ + const getList = async (ref) => { + if (!ref) { + console.error('未传入gridRef') + return + } + await nextTick() + ref.value.commitProxy('query') + } + + // 获取查询参数 + const getSearchData = async (ref) => { + if (!ref) { + console.error('未传入gridRef') + return + } + await nextTick() + const queryParams = Object.assign( + {}, + JSON.parse(JSON.stringify(ref.value.getProxyInfo()?.form)) + ) + return queryParams + } + + /** + * 删除 + * @param ref + * @param ids rowid + * @returns + */ + const deleteData = async (ref, ids: string | number) => { + if (!ref) { + console.error('未传入gridRef') + return + } + if (!config?.deleteApi) { + console.error('未传入delListApi') + return + } + await nextTick() + return new Promise(async () => { + message.delConfirm().then(async () => { + await (config?.deleteApi && config?.deleteApi(ids)) + message.success(t('common.delSuccess')) + // 刷新列表 + ref.value.commitProxy('query') + }) + }) + } + /** + * 导出 + * @param ref + * @param fileName 文件名,默认excel.xls + * @returns + */ + const exportList = async (ref, fileName?: string) => { + if (!ref) { + console.error('未传入gridRef') + return + } + if (!config?.exportListApi) { + console.error('未传入exportListApi') + return + } + await nextTick() + const queryParams = Object.assign( + {}, + JSON.parse(JSON.stringify(ref.value?.getProxyInfo()?.form)) + ) + message.exportConfirm().then(async () => { + const res = await (config?.exportListApi && config?.exportListApi(queryParams)) + download.excel(res as unknown as Blob, fileName ? fileName : 'excel.xls') + }) + } + /** + * 表格最大/最小化 + * @param ref + * @returns + */ + const zoom = async (ref) => { + if (!ref) { + console.error('未传入gridRef') + return + } + await nextTick() + ref.value.zoom(!ref.value.isMaximized()) + } + + return { + gridOptions, + getList, + getSearchData, + deleteData, + exportList, + zoom + } +} diff --git a/src/hooks/web/useWatermark.ts b/src/hooks/web/useWatermark.ts new file mode 100644 index 00000000..4a313594 --- /dev/null +++ b/src/hooks/web/useWatermark.ts @@ -0,0 +1,55 @@ +const domSymbol = Symbol('watermark-dom') + +export function useWatermark(appendEl: HTMLElement | null = document.body) { + let func: Fn = () => {} + const id = domSymbol.toString() + const clear = () => { + const domId = document.getElementById(id) + if (domId) { + const el = appendEl + el && el.removeChild(domId) + } + window.removeEventListener('resize', func) + } + const createWatermark = (str: string) => { + clear() + + const can = document.createElement('canvas') + can.width = 300 + can.height = 240 + + 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, can.width / 20, can.height) + } + + const div = document.createElement('div') + div.id = id + div.style.pointerEvents = 'none' + div.style.top = '0px' + div.style.left = '0px' + div.style.position = 'absolute' + div.style.zIndex = '100000000' + div.style.width = document.documentElement.clientWidth + 'px' + div.style.height = document.documentElement.clientHeight + 'px' + div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat' + const el = appendEl + el && el.appendChild(div) + return id + } + + function setWatermark(str: string) { + createWatermark(str) + func = () => { + createWatermark(str) + } + window.addEventListener('resize', func) + } + + return { setWatermark, clear } +} diff --git a/src/hooks/web/useXTable.ts b/src/hooks/web/useXTable.ts new file mode 100644 index 00000000..501f982b --- /dev/null +++ b/src/hooks/web/useXTable.ts @@ -0,0 +1,39 @@ +import { XTableProps } from '@/components/XTable/src/type' + +export interface tableMethod { + reload: () => void // 刷新表格 + setProps: (props: XTableProps) => void + deleteData: (id: string | number) => void // 删除数据 + deleteBatch: () => void // 批量删除 + exportList: (fileName?: string) => void // 导出列表 + getCurrentColumn: () => void // 获取当前列 + getRadioRecord: () => void // 获取当前选中列,radio + getCheckboxRecords: () => void //获取当前选中列, checkbox +} + +export const useXTable = (props: XTableProps): [Function, tableMethod] => { + const tableRef = ref>(null) + + const register = (instance) => { + tableRef.value = instance + props && instance.setProps(props) + } + const getInstance = (): tableMethod => { + const table = unref(tableRef) + if (!table) { + console.error('表格实例不存在') + } + return table as tableMethod + } + const methods: tableMethod = { + reload: () => getInstance().reload(), + setProps: (props) => getInstance().setProps(props), + deleteData: (id: string | number) => getInstance().deleteData(id), + deleteBatch: () => getInstance().deleteBatch(), + exportList: (fileName?: string) => getInstance().exportList(fileName), + getCurrentColumn: () => getInstance().getCheckboxRecords(), + getRadioRecord: () => getInstance().getRadioRecord(), + getCheckboxRecords: () => getInstance().getCheckboxRecords() + } + return [register, methods] +} diff --git a/src/layout/Layout.vue b/src/layout/Layout.vue new file mode 100644 index 00000000..5c43e52d --- /dev/null +++ b/src/layout/Layout.vue @@ -0,0 +1,78 @@ + + + diff --git a/src/layout/components/AppView.vue b/src/layout/components/AppView.vue new file mode 100644 index 00000000..dda59ed1 --- /dev/null +++ b/src/layout/components/AppView.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/layout/components/Breadcrumb/index.ts b/src/layout/components/Breadcrumb/index.ts new file mode 100644 index 00000000..93ffe705 --- /dev/null +++ b/src/layout/components/Breadcrumb/index.ts @@ -0,0 +1,3 @@ +import Breadcrumb from './src/Breadcrumb.vue' + +export { Breadcrumb } diff --git a/src/layout/components/Breadcrumb/src/Breadcrumb.vue b/src/layout/components/Breadcrumb/src/Breadcrumb.vue new file mode 100644 index 00000000..19be7400 --- /dev/null +++ b/src/layout/components/Breadcrumb/src/Breadcrumb.vue @@ -0,0 +1,128 @@ + + + diff --git a/src/layout/components/Breadcrumb/src/helper.ts b/src/layout/components/Breadcrumb/src/helper.ts new file mode 100644 index 00000000..fb3ec197 --- /dev/null +++ b/src/layout/components/Breadcrumb/src/helper.ts @@ -0,0 +1,31 @@ +import { pathResolve } from '@/utils/routerHelper' +import type { RouteMeta } from 'vue-router' + +export const filterBreadcrumb = ( + routes: AppRouteRecordRaw[], + parentPath = '' +): AppRouteRecordRaw[] => { + const res: AppRouteRecordRaw[] = [] + + for (const route of routes) { + const meta = route?.meta as RouteMeta + if (meta.hidden && !meta.canTo) { + continue + } + + const data: AppRouteRecordRaw = + !meta.alwaysShow && route.children?.length === 1 + ? { ...route.children[0], path: pathResolve(route.path, route.children[0].path) } + : { ...route } + + data.path = pathResolve(parentPath, data.path) + + if (data.children) { + data.children = filterBreadcrumb(data.children, data.path) + } + if (data) { + res.push(data) + } + } + return res +} diff --git a/src/layout/components/Collapse/index.ts b/src/layout/components/Collapse/index.ts new file mode 100644 index 00000000..73f65a3a --- /dev/null +++ b/src/layout/components/Collapse/index.ts @@ -0,0 +1,3 @@ +import Collapse from './src/Collapse.vue' + +export { Collapse } diff --git a/src/layout/components/Collapse/src/Collapse.vue b/src/layout/components/Collapse/src/Collapse.vue new file mode 100644 index 00000000..a8f47dc6 --- /dev/null +++ b/src/layout/components/Collapse/src/Collapse.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/layout/components/ContextMenu/index.ts b/src/layout/components/ContextMenu/index.ts new file mode 100644 index 00000000..2a7c1f0d --- /dev/null +++ b/src/layout/components/ContextMenu/index.ts @@ -0,0 +1,10 @@ +import ContextMenu from './src/ContextMenu.vue' +import { ElDropdown } from 'element-plus' +import type { RouteLocationNormalizedLoaded } from 'vue-router' + +export interface ContextMenuExpose { + elDropdownMenuRef: ComponentRef + tagItem: RouteLocationNormalizedLoaded +} + +export { ContextMenu } diff --git a/src/layout/components/ContextMenu/src/ContextMenu.vue b/src/layout/components/ContextMenu/src/ContextMenu.vue new file mode 100644 index 00000000..8c18f2a1 --- /dev/null +++ b/src/layout/components/ContextMenu/src/ContextMenu.vue @@ -0,0 +1,73 @@ + + + diff --git a/src/layout/components/Footer/index.ts b/src/layout/components/Footer/index.ts new file mode 100644 index 00000000..bd052e0c --- /dev/null +++ b/src/layout/components/Footer/index.ts @@ -0,0 +1,3 @@ +import Footer from './src/Footer.vue' + +export { Footer } diff --git a/src/layout/components/Footer/src/Footer.vue b/src/layout/components/Footer/src/Footer.vue new file mode 100644 index 00000000..239812c6 --- /dev/null +++ b/src/layout/components/Footer/src/Footer.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/layout/components/LocaleDropdown/index.ts b/src/layout/components/LocaleDropdown/index.ts new file mode 100644 index 00000000..d02e640f --- /dev/null +++ b/src/layout/components/LocaleDropdown/index.ts @@ -0,0 +1,3 @@ +import LocaleDropdown from './src/LocaleDropdown.vue' + +export { LocaleDropdown } diff --git a/src/layout/components/LocaleDropdown/src/LocaleDropdown.vue b/src/layout/components/LocaleDropdown/src/LocaleDropdown.vue new file mode 100644 index 00000000..0e56724b --- /dev/null +++ b/src/layout/components/LocaleDropdown/src/LocaleDropdown.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/layout/components/Logo/index.ts b/src/layout/components/Logo/index.ts new file mode 100644 index 00000000..1c4224c9 --- /dev/null +++ b/src/layout/components/Logo/index.ts @@ -0,0 +1,3 @@ +import Logo from './src/Logo.vue' + +export { Logo } diff --git a/src/layout/components/Logo/src/Logo.vue b/src/layout/components/Logo/src/Logo.vue new file mode 100644 index 00000000..9b70a0dc --- /dev/null +++ b/src/layout/components/Logo/src/Logo.vue @@ -0,0 +1,87 @@ + + + diff --git a/src/layout/components/Menu/index.ts b/src/layout/components/Menu/index.ts new file mode 100644 index 00000000..a6ec6965 --- /dev/null +++ b/src/layout/components/Menu/index.ts @@ -0,0 +1,3 @@ +import Menu from './src/Menu.vue' + +export { Menu } diff --git a/src/layout/components/Menu/src/Menu.vue b/src/layout/components/Menu/src/Menu.vue new file mode 100644 index 00000000..4d22f7da --- /dev/null +++ b/src/layout/components/Menu/src/Menu.vue @@ -0,0 +1,298 @@ + + + + + diff --git a/src/layout/components/Menu/src/components/useRenderMenuItem.tsx b/src/layout/components/Menu/src/components/useRenderMenuItem.tsx new file mode 100644 index 00000000..17a520a6 --- /dev/null +++ b/src/layout/components/Menu/src/components/useRenderMenuItem.tsx @@ -0,0 +1,59 @@ +import { ElSubMenu, ElMenuItem } from 'element-plus' +import type { RouteMeta } from 'vue-router' +import { hasOneShowingChild } from '../helper' +import { isUrl } from '@/utils/is' +import { useRenderMenuTitle } from './useRenderMenuTitle' +import { useDesign } from '@/hooks/web/useDesign' +import { pathResolve } from '@/utils/routerHelper' + +export const useRenderMenuItem = ( + // allRouters: AppRouteRecordRaw[] = [], + menuMode: 'vertical' | 'horizontal' +) => { + const renderMenuItem = (routers: AppRouteRecordRaw[], parentPath = '/') => { + return routers.map((v) => { + const meta = (v.meta ?? {}) as RouteMeta + if (!meta.hidden) { + const { oneShowingChild, onlyOneChild } = hasOneShowingChild(v.children, v) + const fullPath = isUrl(v.path) ? v.path : pathResolve(parentPath, v.path) // getAllParentPath(allRouters, v.path).join('/') + + const { renderMenuTitle } = useRenderMenuTitle() + + if ( + oneShowingChild && + (!onlyOneChild?.children || onlyOneChild?.noShowingChildren) && + !meta?.alwaysShow + ) { + return ( + + {{ + default: () => renderMenuTitle(onlyOneChild ? onlyOneChild?.meta : meta) + }} + + ) + } else { + const { getPrefixCls } = useDesign() + + const preFixCls = getPrefixCls('menu-popper') + return ( + + {{ + title: () => renderMenuTitle(meta), + default: () => renderMenuItem(v.children!, fullPath) + }} + + ) + } + } + }) + } + + return { + renderMenuItem + } +} diff --git a/src/layout/components/Menu/src/components/useRenderMenuTitle.tsx b/src/layout/components/Menu/src/components/useRenderMenuTitle.tsx new file mode 100644 index 00000000..fc30b900 --- /dev/null +++ b/src/layout/components/Menu/src/components/useRenderMenuTitle.tsx @@ -0,0 +1,22 @@ +import type { RouteMeta } from 'vue-router' +import { Icon } from '@/components/Icon' + +export const useRenderMenuTitle = () => { + const renderMenuTitle = (meta: RouteMeta) => { + const { t } = useI18n() + const { title = 'Please set title', icon } = meta + + return icon ? ( + <> + + {t(title as string)} + + ) : ( + {t(title as string)} + ) + } + + return { + renderMenuTitle + } +} diff --git a/src/layout/components/Menu/src/helper.ts b/src/layout/components/Menu/src/helper.ts new file mode 100644 index 00000000..c26f5f4b --- /dev/null +++ b/src/layout/components/Menu/src/helper.ts @@ -0,0 +1,54 @@ +import type { RouteMeta } from 'vue-router' +import { findPath } from '@/utils/tree' + +type OnlyOneChildType = AppRouteRecordRaw & { noShowingChildren?: boolean } + +interface HasOneShowingChild { + oneShowingChild?: boolean + onlyOneChild?: OnlyOneChildType +} + +export const getAllParentPath = (treeData: T[], path: string) => { + const menuList = findPath(treeData, (n) => n.path === path) as AppRouteRecordRaw[] + return (menuList || []).map((item) => item.path) +} + +export const hasOneShowingChild = ( + children: AppRouteRecordRaw[] = [], + parent: AppRouteRecordRaw +): HasOneShowingChild => { + const onlyOneChild = ref() + + const showingChildren = children.filter((v) => { + const meta = (v.meta ?? {}) as RouteMeta + if (meta.hidden) { + return false + } else { + // Temp set(will be used if only has one showing child) + onlyOneChild.value = v + return true + } + }) + + // When there is only one child router, the child router is displayed by default + if (showingChildren.length === 1) { + return { + oneShowingChild: true, + onlyOneChild: unref(onlyOneChild) + } + } + + // Show parent if there are no child router to display + if (!showingChildren.length) { + onlyOneChild.value = { ...parent, path: '', noShowingChildren: true } + return { + oneShowingChild: true, + onlyOneChild: unref(onlyOneChild) + } + } + + return { + oneShowingChild: false, + onlyOneChild: unref(onlyOneChild) + } +} diff --git a/src/layout/components/Message/index.ts b/src/layout/components/Message/index.ts new file mode 100644 index 00000000..dfe02076 --- /dev/null +++ b/src/layout/components/Message/index.ts @@ -0,0 +1,3 @@ +import Message from './src/Message.vue' + +export { Message } diff --git a/src/layout/components/Message/src/Message.vue b/src/layout/components/Message/src/Message.vue new file mode 100644 index 00000000..94347812 --- /dev/null +++ b/src/layout/components/Message/src/Message.vue @@ -0,0 +1,113 @@ + + + diff --git a/src/layout/components/Screenfull/index.ts b/src/layout/components/Screenfull/index.ts new file mode 100644 index 00000000..faec2d8e --- /dev/null +++ b/src/layout/components/Screenfull/index.ts @@ -0,0 +1,3 @@ +import Screenfull from './src/Screenfull.vue' + +export { Screenfull } diff --git a/src/layout/components/Screenfull/src/Screenfull.vue b/src/layout/components/Screenfull/src/Screenfull.vue new file mode 100644 index 00000000..32a12e73 --- /dev/null +++ b/src/layout/components/Screenfull/src/Screenfull.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/layout/components/Setting/index.ts b/src/layout/components/Setting/index.ts new file mode 100644 index 00000000..b64c9add --- /dev/null +++ b/src/layout/components/Setting/index.ts @@ -0,0 +1,3 @@ +import Setting from './src/Setting.vue' + +export { Setting } diff --git a/src/layout/components/Setting/src/Setting.vue b/src/layout/components/Setting/src/Setting.vue new file mode 100644 index 00000000..de0532fd --- /dev/null +++ b/src/layout/components/Setting/src/Setting.vue @@ -0,0 +1,298 @@ + + + + + diff --git a/src/layout/components/Setting/src/components/ColorRadioPicker.vue b/src/layout/components/Setting/src/components/ColorRadioPicker.vue new file mode 100644 index 00000000..7c5e2404 --- /dev/null +++ b/src/layout/components/Setting/src/components/ColorRadioPicker.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/src/layout/components/Setting/src/components/InterfaceDisplay.vue b/src/layout/components/Setting/src/components/InterfaceDisplay.vue new file mode 100644 index 00000000..e0dc0885 --- /dev/null +++ b/src/layout/components/Setting/src/components/InterfaceDisplay.vue @@ -0,0 +1,222 @@ + + + diff --git a/src/layout/components/Setting/src/components/LayoutRadioPicker.vue b/src/layout/components/Setting/src/components/LayoutRadioPicker.vue new file mode 100644 index 00000000..07dbab95 --- /dev/null +++ b/src/layout/components/Setting/src/components/LayoutRadioPicker.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/src/layout/components/SizeDropdown/index.ts b/src/layout/components/SizeDropdown/index.ts new file mode 100644 index 00000000..516488d6 --- /dev/null +++ b/src/layout/components/SizeDropdown/index.ts @@ -0,0 +1,3 @@ +import SizeDropdown from './src/SizeDropdown.vue' + +export { SizeDropdown } diff --git a/src/layout/components/SizeDropdown/src/SizeDropdown.vue b/src/layout/components/SizeDropdown/src/SizeDropdown.vue new file mode 100644 index 00000000..0dc698c0 --- /dev/null +++ b/src/layout/components/SizeDropdown/src/SizeDropdown.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/layout/components/TabMenu/index.ts b/src/layout/components/TabMenu/index.ts new file mode 100644 index 00000000..b5fd71cd --- /dev/null +++ b/src/layout/components/TabMenu/index.ts @@ -0,0 +1,3 @@ +import TabMenu from './src/TabMenu.vue' + +export { TabMenu } diff --git a/src/layout/components/TabMenu/src/TabMenu.vue b/src/layout/components/TabMenu/src/TabMenu.vue new file mode 100644 index 00000000..015d6c73 --- /dev/null +++ b/src/layout/components/TabMenu/src/TabMenu.vue @@ -0,0 +1,251 @@ + + + diff --git a/src/layout/components/TabMenu/src/helper.ts b/src/layout/components/TabMenu/src/helper.ts new file mode 100644 index 00000000..cce39323 --- /dev/null +++ b/src/layout/components/TabMenu/src/helper.ts @@ -0,0 +1,51 @@ +import { getAllParentPath } from '@/layout/components/Menu/src/helper' +import type { RouteMeta } from 'vue-router' +import { isUrl } from '@/utils/is' +import { cloneDeep } from 'lodash-es' + +export type TabMapTypes = { + [key: string]: string[] +} + +export const tabPathMap = reactive({}) + +export const initTabMap = (routes: AppRouteRecordRaw[]) => { + for (const v of routes) { + const meta = (v.meta ?? {}) as RouteMeta + if (!meta?.hidden) { + tabPathMap[v.path] = [] + } + } +} + +export const filterMenusPath = ( + routes: AppRouteRecordRaw[], + allRoutes: AppRouteRecordRaw[] +): AppRouteRecordRaw[] => { + const res: AppRouteRecordRaw[] = [] + for (const v of routes) { + let data: Nullable = null + const meta = (v.meta ?? {}) as RouteMeta + if (!meta.hidden || meta.canTo) { + const allParentPath = getAllParentPath(allRoutes, v.path) + + const fullPath = isUrl(v.path) ? v.path : allParentPath.join('/') + + data = cloneDeep(v) + data.path = fullPath + if (v.children && data) { + data.children = filterMenusPath(v.children, allRoutes) + } + + if (data) { + res.push(data) + } + + if (allParentPath.length && Reflect.has(tabPathMap, allParentPath[0])) { + tabPathMap[allParentPath[0]].push(fullPath) + } + } + } + + return res +} diff --git a/src/layout/components/TagsView/index.ts b/src/layout/components/TagsView/index.ts new file mode 100644 index 00000000..30e604a8 --- /dev/null +++ b/src/layout/components/TagsView/index.ts @@ -0,0 +1,3 @@ +import TagsView from './src/TagsView.vue' + +export { TagsView } diff --git a/src/layout/components/TagsView/src/TagsView.vue b/src/layout/components/TagsView/src/TagsView.vue new file mode 100644 index 00000000..b3f95594 --- /dev/null +++ b/src/layout/components/TagsView/src/TagsView.vue @@ -0,0 +1,581 @@ + + + + + diff --git a/src/layout/components/TagsView/src/helper.ts b/src/layout/components/TagsView/src/helper.ts new file mode 100644 index 00000000..22f6a507 --- /dev/null +++ b/src/layout/components/TagsView/src/helper.ts @@ -0,0 +1,21 @@ +import type { RouteMeta, RouteLocationNormalizedLoaded } from 'vue-router' +import { pathResolve } from '@/utils/routerHelper' + +export const filterAffixTags = (routes: AppRouteRecordRaw[], parentPath = '') => { + let tags: RouteLocationNormalizedLoaded[] = [] + routes.forEach((route) => { + const meta = route.meta as RouteMeta + const tagPath = pathResolve(parentPath, route.path) + if (meta?.affix) { + tags.push({ ...route, path: tagPath, fullPath: tagPath } as RouteLocationNormalizedLoaded) + } + if (route.children) { + const tempTags: RouteLocationNormalizedLoaded[] = filterAffixTags(route.children, tagPath) + if (tempTags.length >= 1) { + tags = [...tags, ...tempTags] + } + } + }) + + return tags +} diff --git a/src/layout/components/ThemeSwitch/index.ts b/src/layout/components/ThemeSwitch/index.ts new file mode 100644 index 00000000..823a2765 --- /dev/null +++ b/src/layout/components/ThemeSwitch/index.ts @@ -0,0 +1,3 @@ +import ThemeSwitch from './src/ThemeSwitch.vue' + +export { ThemeSwitch } diff --git a/src/layout/components/ThemeSwitch/src/ThemeSwitch.vue b/src/layout/components/ThemeSwitch/src/ThemeSwitch.vue new file mode 100644 index 00000000..1af67cf0 --- /dev/null +++ b/src/layout/components/ThemeSwitch/src/ThemeSwitch.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/layout/components/ToolHeader.vue b/src/layout/components/ToolHeader.vue new file mode 100644 index 00000000..49307773 --- /dev/null +++ b/src/layout/components/ToolHeader.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/layout/components/UserInfo/index.ts b/src/layout/components/UserInfo/index.ts new file mode 100644 index 00000000..c3a34aba --- /dev/null +++ b/src/layout/components/UserInfo/index.ts @@ -0,0 +1,3 @@ +import UserInfo from './src/UserInfo.vue' + +export { UserInfo } diff --git a/src/layout/components/UserInfo/src/UserInfo.vue b/src/layout/components/UserInfo/src/UserInfo.vue new file mode 100644 index 00000000..73e048ab --- /dev/null +++ b/src/layout/components/UserInfo/src/UserInfo.vue @@ -0,0 +1,76 @@ + + + diff --git a/src/layout/components/useRenderLayout.tsx b/src/layout/components/useRenderLayout.tsx new file mode 100644 index 00000000..dfec3ff6 --- /dev/null +++ b/src/layout/components/useRenderLayout.tsx @@ -0,0 +1,276 @@ +import { computed } from 'vue' +import { useAppStore } from '@/store/modules/app' +import { Menu } from '@/layout/components/Menu' +import { TabMenu } from '@/layout/components/TabMenu' +import { TagsView } from '@/layout/components/TagsView' +import { Logo } from '@/layout/components/Logo' +import AppView from './AppView.vue' +import ToolHeader from './ToolHeader.vue' +import { ElScrollbar } from 'element-plus' +import { useDesign } from '@/hooks/web/useDesign' + +const { getPrefixCls } = useDesign() + +const prefixCls = getPrefixCls('layout') + +const appStore = useAppStore() + +const pageLoading = computed(() => appStore.getPageLoading) + +// 标签页 +const tagsView = computed(() => appStore.getTagsView) + +// 菜单折叠 +const collapse = computed(() => appStore.getCollapse) + +// logo +const logo = computed(() => appStore.logo) + +// 固定头部 +const fixedHeader = computed(() => appStore.getFixedHeader) + +// 是否是移动端 +const mobile = computed(() => appStore.getMobile) + +// 固定菜单 +const fixedMenu = computed(() => appStore.getFixedMenu) + +export const useRenderLayout = () => { + const renderClassic = () => { + return ( + <> +
+ {logo.value ? ( + + ) : undefined} + +
+
+ +
+ + + {tagsView.value ? ( + + ) : undefined} +
+ + +
+
+ + ) + } + + const renderTopLeft = () => { + return ( + <> +
+ {logo.value ? : undefined} + + +
+
+ +
+ + {tagsView.value ? ( + + ) : undefined} + + + +
+
+ + ) + } + + const renderTop = () => { + return ( + <> +
+ {logo.value ? : undefined} + + +
+
+ + {tagsView.value ? ( + + ) : undefined} + + + +
+ + ) + } + + const renderCutMenu = () => { + return ( + <> +
+ {logo.value ? : undefined} + + +
+
+ +
+ + {tagsView.value ? ( + + ) : undefined} + + + +
+
+ + ) + } + + return { + renderClassic, + renderTopLeft, + renderTop, + renderCutMenu + } +} diff --git a/src/locales/en.ts b/src/locales/en.ts new file mode 100644 index 00000000..ed5bf72c --- /dev/null +++ b/src/locales/en.ts @@ -0,0 +1,445 @@ +export default { + common: { + inputText: 'Please input', + selectText: 'Please select', + startTimeText: 'Start time', + endTimeText: 'End time', + login: 'Login', + required: 'This is required', + loginOut: 'Login out', + document: 'Document', + profile: 'User Center', + reminder: 'Reminder', + loginOutMessage: 'Exit the system?', + back: 'Back', + ok: 'OK', + save: 'Save', + cancel: 'Cancel', + close: 'Close', + reload: 'Reload current', + success: 'Success', + closeTab: 'Close current', + closeTheLeftTab: 'Close left', + closeTheRightTab: 'Close right', + closeOther: 'Close other', + closeAll: 'Close all', + prevLabel: 'Prev', + nextLabel: 'Next', + skipLabel: 'Jump', + doneLabel: 'End', + menu: 'Menu', + menuDes: 'Menu bar rendered in routed structure', + collapse: 'Collapse', + collapseDes: 'Expand and zoom the menu bar', + tagsView: 'Tags view', + tagsViewDes: 'Used to record routing history', + tool: 'Tool', + toolDes: 'Used to set up custom systems', + query: 'Query', + reset: 'Reset', + shrink: 'Put away', + expand: 'Expand', + confirmTitle: 'System Hint', + exportMessage: 'Whether to confirm export data item?', + importMessage: 'Whether to confirm import data item?', + createSuccess: 'Create Success', + updateSuccess: 'Update Success', + delMessage: 'Delete the selected data?', + delDataMessage: 'Delete the data?', + delNoData: 'Please select the data to delete', + delSuccess: 'Deleted successfully', + index: 'Index', + status: 'Status', + createTime: 'Create Time', + updateTime: 'Update Time', + copy: 'Copy', + copySuccess: 'Copy Success', + copyError: 'Copy Error' + }, + error: { + noPermission: `Sorry, you don't have permission to access this page.`, + pageError: 'Sorry, the page you visited does not exist.', + networkError: 'Sorry, the server reported an error.', + returnToHome: 'Return to home' + }, + permission: { + hasPermission: `Please set the operation permission label value`, + hasRole: `Please set the role permission tag value` + }, + setting: { + projectSetting: 'Project setting', + theme: 'Theme', + layout: 'Layout', + systemTheme: 'System theme', + menuTheme: 'Menu theme', + interfaceDisplay: 'Interface display', + breadcrumb: 'Breadcrumb', + breadcrumbIcon: 'Breadcrumb icon', + collapseMenu: 'Collapse menu', + hamburgerIcon: 'Hamburger icon', + screenfullIcon: 'Screenfull icon', + sizeIcon: 'Size icon', + localeIcon: 'Locale icon', + messageIcon: 'Message icon', + tagsView: 'Tags view', + logo: 'Logo', + greyMode: 'Grey mode', + fixedHeader: 'Fixed header', + headerTheme: 'Header theme', + cutMenu: 'Cut Menu', + copy: 'Copy', + clearAndReset: 'Clear cache and reset', + copySuccess: 'Copy success', + copyFailed: 'Copy failed', + footer: 'Footer', + uniqueOpened: 'Unique opened', + tagsViewIcon: 'Tags view icon', + reExperienced: 'Please exit the login experience again', + fixedMenu: 'Fixed menu' + }, + size: { + default: 'Default', + large: 'Large', + small: 'Small' + }, + login: { + welcome: 'Welcome to the system', + message: 'Backstage management system', + tenantname: 'TenantName', + username: 'Username', + password: 'Password', + code: 'verification code', + login: 'Sign in', + relogin: 'Sign in again', + otherLogin: 'Sign in with', + register: 'Register', + checkPassword: 'Confirm password', + remember: 'Remember me', + hasUser: 'Existing account? Go to login', + forgetPassword: 'Forget password?', + tenantNamePlaceholder: 'Please Enter Tenant Name', + usernamePlaceholder: 'Please Enter Username', + passwordPlaceholder: 'Please Enter Password', + codePlaceholder: 'Please Enter Verification Code', + mobileTitle: 'Mobile sign in', + mobileNumber: 'Mobile Number', + mobileNumberPlaceholder: 'Plaease Enter Mobile Number', + backLogin: 'back', + getSmsCode: 'Get SMS Code', + btnMobile: 'Mobile sign in', + btnQRCode: 'QR code sign in', + qrcode: 'Scan the QR code to log in', + btnRegister: 'Sign up', + SmsSendMsg: 'code has been sent' + }, + captcha: { + verification: 'Please complete security verification', + slide: 'Swipe right to complete verification', + point: 'Please click', + success: 'Verification succeeded', + fail: 'verification failed' + }, + router: { + login: 'Login', + home: 'Home', + analysis: 'Analysis', + workplace: 'Workplace' + }, + analysis: { + newUser: 'New user', + unreadInformation: 'Unread information', + transactionAmount: 'Transaction amount', + totalShopping: 'Total Shopping', + monthlySales: 'Monthly sales', + userAccessSource: 'User access source', + january: 'January', + february: 'February', + march: 'March', + april: 'April', + may: 'May', + june: 'June', + july: 'July', + august: 'August', + september: 'September', + october: 'October', + november: 'November', + december: 'December', + estimate: 'Estimate', + actual: 'Actual', + directAccess: 'Airect access', + mailMarketing: 'Mail marketing', + allianceAdvertising: 'Alliance advertising', + videoAdvertising: 'Video advertising', + searchEngines: 'Search engines', + weeklyUserActivity: 'Weekly user activity', + activeQuantity: 'Active quantity', + monday: 'Monday', + tuesday: 'Tuesday', + wednesday: 'Wednesday', + thursday: 'Thursday', + friday: 'Friday', + saturday: 'Saturday', + sunday: 'Sunday' + }, + workplace: { + welcome: 'Hello', + happyDay: 'Wish you happy every day!', + toady: `It's sunny today`, + notice: 'Announcement', + project: 'Project', + access: 'Project access', + toDo: 'To do', + introduction: 'A serious introduction', + shortcutOperation: 'Quick entry', + operation: 'Operation', + index: 'Index', + personal: 'Personal', + team: 'Team', + quote: 'Quote', + contribution: 'Contribution', + hot: 'Hot', + yield: 'Yield', + dynamic: 'Dynamic', + push: 'push', + follow: 'Follow' + }, + form: { + input: 'Input', + inputNumber: 'InputNumber', + default: 'Default', + icon: 'Icon', + mixed: 'Mixed', + textarea: 'Textarea', + slot: 'Slot', + position: 'Position', + autocomplete: 'Autocomplete', + select: 'Select', + selectGroup: 'Select Group', + selectV2: 'SelectV2', + cascader: 'Cascader', + switch: 'Switch', + rate: 'Rate', + colorPicker: 'Color Picker', + transfer: 'Transfer', + render: 'Render', + radio: 'Radio', + button: 'Button', + checkbox: 'Checkbox', + slider: 'Slider', + datePicker: 'Date Picker', + shortcuts: 'Shortcuts', + today: 'Today', + yesterday: 'Yesterday', + aWeekAgo: 'A week ago', + week: 'Week', + year: 'Year', + month: 'Month', + dates: 'Dates', + daterange: 'Date Range', + monthrange: 'Month Range', + dateTimePicker: 'DateTimePicker', + dateTimerange: 'Datetime Range', + timePicker: 'Time Picker', + timeSelect: 'Time Select', + inputPassword: 'input Password', + passwordStrength: 'Password Strength', + operate: 'operate', + change: 'Change', + restore: 'Restore', + disabled: 'Disabled', + disablement: 'Disablement', + delete: 'Delete', + add: 'Add', + setValue: 'Set value', + resetValue: 'Reset value', + set: 'Set', + subitem: 'Subitem', + formValidation: 'Form validation', + verifyReset: 'Verify reset', + remark: 'Remark' + }, + watermark: { + watermark: 'Watermark' + }, + table: { + table: 'Table', + index: 'Index', + title: 'Title', + author: 'Author', + createTime: 'Create time', + action: 'Action', + pagination: 'pagination', + reserveIndex: 'Reserve index', + restoreIndex: 'Restore index', + showSelections: 'Show selections', + hiddenSelections: 'Restore selections', + showExpandedRows: 'Show expanded rows', + hiddenExpandedRows: 'Hidden expanded rows', + header: 'Header' + }, + action: { + create: 'Create', + add: 'Add', + del: 'Delete', + edit: 'Edit', + update: 'Update', + preview: 'Preview', + more: 'More', + sync: 'Sync', + save: 'Save', + detail: 'Detail', + export: 'Export', + import: 'Import', + generate: 'Generate', + logout: 'Login Out', + test: 'Test', + typeCreate: 'Dict Type Create', + typeUpdate: 'Dict Type Eidt', + dataCreate: 'Dict Data Create', + dataUpdate: 'Dict Data Eidt' + }, + dialog: { + dialog: 'Dialog', + open: 'Open', + close: 'Close' + }, + sys: { + api: { + operationFailed: 'Operation failed', + errorTip: 'Error 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!', + errMsg901: 'Demo mode, no write operations are possible!' + }, + app: { + logoutTip: 'Reminder', + logoutMessage: 'Confirm to exit the system?', + menuLoading: 'Menu loading...' + }, + 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' + } + }, + profile: { + user: { + title: 'Personal Information', + username: 'User Name', + nickname: 'Nick Name', + mobile: 'Phone Number', + email: 'User Mail', + dept: 'Department', + posts: 'Position', + roles: 'Own Role', + sex: 'Sex', + man: 'Man', + woman: 'Woman', + createTime: 'Created Date' + }, + info: { + title: 'Basic Information', + basicInfo: 'Basic Information', + resetPwd: 'Reset Password', + userSocial: 'Social Information' + }, + rules: { + nickname: 'Please Enter User Nickname', + mail: 'Please Input The Email Address', + truemail: 'Please Input The Correct Email Address', + phone: 'Please Enter The Phone Number', + truephone: 'Please Enter The Correct Phone Number' + }, + password: { + oldPassword: 'Old PassWord', + newPassword: 'New Password', + confirmPassword: 'Confirm Password', + oldPwdMsg: 'Please Enter Old Password', + newPwdMsg: 'Please Enter New Password', + cfPwdMsg: 'Please Enter Confirm Password', + diffPwd: 'The Passwords Entered Twice No Match' + } + }, + 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' + } +} diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts new file mode 100644 index 00000000..7faf145d --- /dev/null +++ b/src/locales/zh-CN.ts @@ -0,0 +1,438 @@ +export default { + common: { + inputText: '请输入', + selectText: '请选择', + startTimeText: '开始时间', + endTimeText: '结束时间', + login: '登录', + required: '该项为必填项', + loginOut: '退出系统', + document: '项目文档', + profile: '个人中心', + reminder: '温馨提示', + loginOutMessage: '是否退出本系统?', + back: '返回', + ok: '确定', + save: '保存', + cancel: '取消', + close: '关闭', + reload: '重新加载', + success: '成功', + closeTab: '关闭标签页', + closeTheLeftTab: '关闭左侧标签页', + closeTheRightTab: '关闭右侧标签页', + closeOther: '关闭其他标签页', + closeAll: '关闭全部标签页', + prevLabel: '上一步', + nextLabel: '下一步', + skipLabel: '跳过', + doneLabel: '结束', + menu: '菜单', + menuDes: '以路由的结构渲染的菜单栏', + collapse: '展开缩收', + collapseDes: '展开和缩放菜单栏', + tagsView: '标签页', + tagsViewDes: '用于记录路由历史记录', + tool: '工具', + toolDes: '用于设置定制系统', + query: '查询', + reset: '重置', + shrink: '收起', + expand: '展开', + confirmTitle: '系统提示', + exportMessage: '是否确认导出数据项?', + importMessage: '是否确认导入数据项?', + createSuccess: '新增成功', + updateSuccess: '修改成功', + delMessage: '是否删除所选中数据?', + delDataMessage: '是否删除数据?', + delNoData: '请选择需要删除的数据', + delSuccess: '删除成功', + index: '序号', + status: '状态', + createTime: '创建时间', + updateTime: '更新时间', + copy: '复制', + copySuccess: '复制成功', + copyError: '复制失败' + }, + error: { + noPermission: `抱歉,您无权访问此页面。`, + pageError: '抱歉,您访问的页面不存在。', + networkError: '抱歉,服务器报告错误。', + returnToHome: '返回首页' + }, + permission: { + hasPermission: `请设置操作权限标签值`, + hasRole: `请设置角色权限标签值` + }, + setting: { + projectSetting: '项目配置', + theme: '主题', + layout: '布局', + systemTheme: '系统主题', + menuTheme: '菜单主题', + interfaceDisplay: '界面显示', + breadcrumb: '面包屑', + breadcrumbIcon: '面包屑图标', + collapseMenu: '折叠菜单', + hamburgerIcon: '折叠图标', + screenfullIcon: '全屏图标', + sizeIcon: '尺寸图标', + localeIcon: '多语言图标', + messageIcon: '消息图标', + tagsView: '标签页', + logo: '标志', + greyMode: '灰色模式', + fixedHeader: '固定头部', + headerTheme: '头部主题', + cutMenu: '切割菜单', + copy: '拷贝', + clearAndReset: '清除缓存并且重置', + copySuccess: '拷贝成功', + copyFailed: '拷贝失败', + footer: '页脚', + uniqueOpened: '菜单手风琴', + tagsViewIcon: '标签页图标', + reExperienced: '请重新退出登录体验', + fixedMenu: '固定菜单' + }, + size: { + default: '默认', + large: '大', + small: '小' + }, + login: { + welcome: '欢迎使用本系统', + message: '开箱即用的中后台管理系统', + tenantname: '租户名称', + username: '用户名', + password: '密码', + code: '验证码', + login: '登录', + relogin: '重新登录', + otherLogin: '其他登录方式', + register: '注册', + checkPassword: '确认密码', + remember: '记住我', + hasUser: '已有账号?去登录', + forgetPassword: '忘记密码?', + tenantNamePlaceholder: '请输入租户名称', + usernamePlaceholder: '请输入用户名', + passwordPlaceholder: '请输入密码', + codePlaceholder: '请输入验证码', + mobileTitle: '手机登录', + mobileNumber: '手机号码', + mobileNumberPlaceholder: '请输入手机号码', + backLogin: '返回', + getSmsCode: '获取验证码', + btnMobile: '手机登录', + btnQRCode: '二维码登录', + qrcode: '扫描二维码登录', + btnRegister: '注册', + SmsSendMsg: '验证码已发送' + }, + captcha: { + verification: '请完成安全验证', + slide: '向右滑动完成验证', + point: '请依次点击', + success: '验证成功', + fail: '验证失败' + }, + router: { + login: '登录', + home: '首页', + analysis: '分析页', + workplace: '工作台' + }, + analysis: { + newUser: '新增用户', + unreadInformation: '未读消息', + transactionAmount: '成交金额', + totalShopping: '购物总量', + monthlySales: '每月销售额', + userAccessSource: '用户访问来源', + january: '一月', + february: '二月', + march: '三月', + april: '四月', + may: '五月', + june: '六月', + july: '七月', + august: '八月', + september: '九月', + october: '十月', + november: '十一月', + december: '十二月', + estimate: '预计', + actual: '实际', + directAccess: '直接访问', + mailMarketing: '邮件营销', + allianceAdvertising: '联盟广告', + videoAdvertising: '视频广告', + searchEngines: '搜索引擎', + weeklyUserActivity: '每周用户活跃量', + activeQuantity: '活跃量', + monday: '周一', + tuesday: '周二', + wednesday: '周三', + thursday: '周四', + friday: '周五', + saturday: '周六', + sunday: '周日' + }, + workplace: { + welcome: '你好', + happyDay: '祝你开心每一天!', + toady: '今日晴', + notice: '通知公告', + project: '项目数', + access: '项目访问', + toDo: '待办', + introduction: '一个正经的简介', + shortcutOperation: '快捷入口', + operation: '操作', + index: '指数', + personal: '个人', + team: '团队', + quote: '引用', + contribution: '贡献', + hot: '热度', + yield: '产量', + dynamic: '动态', + push: '推送', + follow: '关注' + }, + form: { + input: '输入框', + inputNumber: '数字输入框', + default: '默认', + icon: '图标', + mixed: '复合型', + textarea: '多行文本', + slot: '插槽', + position: '位置', + autocomplete: '自动补全', + select: '选择器', + selectGroup: '选项分组', + selectV2: '虚拟列表选择器', + cascader: '级联选择器', + switch: '开关', + rate: '评分', + colorPicker: '颜色选择器', + transfer: '穿梭框', + render: '渲染器', + radio: '单选框', + button: '按钮', + checkbox: '多选框', + slider: '滑块', + datePicker: '日期选择器', + shortcuts: '快捷选项', + today: '今天', + yesterday: '昨天', + aWeekAgo: '一周前', + week: '周', + year: '年', + month: '月', + dates: '日期', + daterange: '日期范围', + monthrange: '月份范围', + dateTimePicker: '日期时间选择器', + dateTimerange: '日期时间范围', + timePicker: '时间选择器', + timeSelect: '时间选择', + inputPassword: '密码输入框', + passwordStrength: '密码强度', + operate: '操作', + change: '更改', + restore: '还原', + disabled: '禁用', + disablement: '解除禁用', + delete: '删除', + add: '添加', + setValue: '设置值', + resetValue: '重置值', + set: '设置', + subitem: '子项', + formValidation: '表单验证', + verifyReset: '验证重置', + remark: '备注' + }, + watermark: { + watermark: '水印' + }, + table: { + table: '表格', + index: '序号', + title: '标题', + author: '作者', + createTime: '创建时间', + action: '操作', + pagination: '分页', + reserveIndex: '叠加序号', + restoreIndex: '还原序号', + showSelections: '显示多选', + hiddenSelections: '隐藏多选', + showExpandedRows: '显示展开行', + hiddenExpandedRows: '隐藏展开行', + header: '头部' + }, + action: { + create: '新增', + add: '新增', + del: '删除', + edit: '编辑', + update: '编辑', + preview: '预览', + more: '更多', + sync: '同步', + save: '保存', + detail: '详情', + export: '导出', + import: '导入', + generate: '生成', + logout: '强制退出', + test: '测试', + typeCreate: '字典类型新增', + typeUpdate: '字典类型编辑', + dataCreate: '字典数据新增', + dataUpdate: '字典数据编辑' + }, + dialog: { + dialog: '弹窗', + open: '打开', + close: '关闭' + }, + sys: { + api: { + operationFailed: '操作失败', + errorTip: '错误提示', + errorMessage: '操作失败,系统异常!', + timeoutMessage: '登录超时,请重新登录!', + apiTimeoutMessage: '接口请求超时,请刷新页面重试!', + apiRequestFailed: '请求出错,请稍候重试', + networkException: '网络异常', + networkExceptionMsg: '网络异常,请检查您的网络连接是否正常!', + errMsg401: '用户没有权限(令牌、用户名、密码错误)!', + errMsg403: '用户得到授权,但是访问是被禁止的。!', + errMsg404: '网络请求错误,未找到该资源!', + errMsg405: '网络请求错误,请求方法未允许!', + errMsg408: '网络请求超时!', + errMsg500: '服务器错误,请联系管理员!', + errMsg501: '网络未实现!', + errMsg502: '网络错误!', + errMsg503: '服务不可用,服务器暂时过载或维护!', + errMsg504: '网络超时!', + errMsg505: 'http版本不支持该请求!', + errMsg901: '演示模式,无法进行写操作!' + }, + app: { + logoutTip: '温馨提醒', + logoutMessage: '是否确认退出系统?', + menuLoading: '菜单加载中...' + }, + 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: '手机号码' + } + }, + profile: { + user: { + title: '个人信息', + username: '用户名称', + nickname: '用户昵称', + mobile: '手机号码', + email: '用户邮箱', + dept: '所属部门', + posts: '所属岗位', + roles: '所属角色', + sex: '性别', + man: '男', + woman: '女', + createTime: '创建日期' + }, + info: { + title: '基本信息', + basicInfo: '基本资料', + resetPwd: '修改密码', + userSocial: '社交信息' + }, + rules: { + nickname: '请输入用户昵称', + mail: '请输入邮箱地址', + truemail: '请输入正确的邮箱地址', + phone: '请输入正确的手机号码', + truephone: '请输入正确的手机号码' + }, + password: { + oldPassword: '旧密码', + newPassword: '新密码', + confirmPassword: '确认密码', + oldPwdMsg: '请输入旧密码', + newPwdMsg: '请输入新密码', + cfPwdMsg: '请输入确认密码', + pwdRules: '长度在 6 到 20 个字符', + diffPwd: '两次输入密码不一致' + } + }, + 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: '预览' + } +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 00000000..4aba7d93 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,99 @@ +// 引入windi css +import '@/plugins/windi.css' + +// 导入全局的svg图标 +import '@/plugins/svgIcon' + +// 初始化多语言 +import { setupI18n } from '@/plugins/vueI18n' + +// 引入状态管理 +import { setupStore } from '@/store' + +// 全局组件 +import { setupGlobCom } from '@/components' + +// 引入 element-plus +import { setupElementPlus } from '@/plugins/elementPlus' + +// 引入 vxe-table +import { setupVxeTable } from '@/plugins/vxeTable' + +// 引入 form-create +import { setupFormCreate } from '@/plugins/formCreate' + +// 引入全局样式 +import '@/styles/index.scss' + +// 引入动画 +import '@/plugins/animate.css' + +// 路由 +import router, { setupRouter } from '@/router' + +// 权限 +import { setupAuth } from '@/directives' + +import { createApp } from 'vue' + +import App from './App.vue' + +import './permission' + +import { isDevMode } from '@/utils/env' + +import { MyPD } from '@/components/bpmnProcessDesigner/package/index.js' +import '@/components/bpmnProcessDesigner/package/theme/index.scss' +import 'bpmn-js/dist/assets/diagram-js.css' +import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css' +import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css' +import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css' + +import hljs from 'highlight.js' //导入代码高亮文件 +import 'highlight.js/styles/github.css' //导入代码高亮样式 新版 + +import Logger from '@/utils/Logger' + +// 本地开发模式 全局引入 element-plus 样式,加快第一次进入速度 +if (isDevMode() == true) { + import('element-plus/dist/index.css') +} + +// 创建实例 +const setupAll = async () => { + const app = createApp(App) + + //自定义一个代码高亮指令 + app.directive('highlight', function (el) { + const blocks = el.querySelectorAll('code') + blocks.forEach((block: any) => { + hljs.highlightElement(block) + }) + }) + + await setupI18n(app) + + MyPD(app) + + setupStore(app) + + setupGlobCom(app) + + setupElementPlus(app) + + setupVxeTable(app) + + setupFormCreate(app) + + setupRouter(app) + + setupAuth(app) + + await router.isReady() + + app.mount('#app') +} + +setupAll() + +Logger.prettyPrimary(`欢迎使用`, import.meta.env.VITE_APP_TITLE) diff --git a/src/permission.ts b/src/permission.ts new file mode 100644 index 00000000..bcba0119 --- /dev/null +++ b/src/permission.ts @@ -0,0 +1,70 @@ +import router from './router' +import type { RouteRecordRaw } from 'vue-router' +import { isRelogin } from '@/config/axios/service' +import { getAccessToken } from '@/utils/auth' +import { useTitle } from '@/hooks/web/useTitle' +import { useNProgress } from '@/hooks/web/useNProgress' +import { usePageLoading } from '@/hooks/web/usePageLoading' +import { useDictStoreWithOut } from '@/store/modules/dict' +import { useUserStoreWithOut } from '@/store/modules/user' +import { usePermissionStoreWithOut } from '@/store/modules/permission' + +const { start, done } = useNProgress() + +const { loadStart, loadDone } = usePageLoading() +// 路由不重定向白名单 +const whiteList = [ + '/login', + '/social-login', + '/auth-redirect', + '/bind', + '/register', + '/oauthLogin/gitee' +] + +// 路由加载前 +router.beforeEach(async (to, from, next) => { + start() + loadStart() + if (getAccessToken()) { + if (to.path === '/login') { + next({ path: '/' }) + } else { + // 获取所有字典 + const dictStore = useDictStoreWithOut() + const userStore = useUserStoreWithOut() + const permissionStore = usePermissionStoreWithOut() + if (!dictStore.getIsSetDict) { + dictStore.setDictMap() + } + if (!userStore.getIsSetUser) { + isRelogin.show = true + await userStore.setUserInfoAction() + isRelogin.show = false + // 后端过滤菜单 + await permissionStore.generateRoutes() + permissionStore.getAddRouters.forEach((route) => { + router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表 + }) + const redirectPath = from.query.redirect || to.path + const redirect = decodeURIComponent(redirectPath as string) + const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect } + next(nextData) + } else { + next() + } + } + } else { + if (whiteList.indexOf(to.path) !== -1) { + next() + } else { + next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页 + } + } +}) + +router.afterEach((to) => { + useTitle(to?.meta?.title as string) + done() // 结束Progress + loadDone() +}) diff --git a/src/plugins/animate.css/index.ts b/src/plugins/animate.css/index.ts new file mode 100644 index 00000000..3e934513 --- /dev/null +++ b/src/plugins/animate.css/index.ts @@ -0,0 +1 @@ +import 'animate.css' diff --git a/src/plugins/echarts/index.ts b/src/plugins/echarts/index.ts new file mode 100644 index 00000000..34f756f6 --- /dev/null +++ b/src/plugins/echarts/index.ts @@ -0,0 +1,41 @@ +import * as echarts from 'echarts/core' + +import { + BarChart, + LineChart, + PieChart, + MapChart, + PictorialBarChart, + RadarChart +} from 'echarts/charts' + +import { + TitleComponent, + TooltipComponent, + GridComponent, + PolarComponent, + AriaComponent, + ParallelComponent, + LegendComponent +} from 'echarts/components' + +import { CanvasRenderer } from 'echarts/renderers' + +echarts.use([ + LegendComponent, + TitleComponent, + TooltipComponent, + GridComponent, + PolarComponent, + AriaComponent, + ParallelComponent, + BarChart, + LineChart, + PieChart, + MapChart, + CanvasRenderer, + PictorialBarChart, + RadarChart +]) + +export default echarts diff --git a/src/plugins/elementPlus/index.ts b/src/plugins/elementPlus/index.ts new file mode 100644 index 00000000..0ae2a8b2 --- /dev/null +++ b/src/plugins/elementPlus/index.ts @@ -0,0 +1,17 @@ +import type { App } from 'vue' +// 需要全局引入一些组件,如ElScrollbar,不然一些下拉项样式有问题 +import { ElLoading, ElScrollbar, ElButton } from 'element-plus' + +const plugins = [ElLoading] + +const components = [ElScrollbar, ElButton] + +export const setupElementPlus = (app: App) => { + plugins.forEach((plugin) => { + app.use(plugin) + }) + + components.forEach((component) => { + app.component(component.name, component) + }) +} diff --git a/src/plugins/formCreate/index.ts b/src/plugins/formCreate/index.ts new file mode 100644 index 00000000..69816d8a --- /dev/null +++ b/src/plugins/formCreate/index.ts @@ -0,0 +1,43 @@ +import type { App } from 'vue' +// 👇使用 form-create 需额外全局引入 element plus 组件 +import { + ElAside, + ElPopconfirm, + ElHeader, + ElMain, + ElContainer, + ElDivider, + ElTransfer, + ElAlert, + ElTabs, + ElTabPane +} from 'element-plus' + +import formCreate from '@form-create/element-ui' +import install from '@form-create/element-ui/auto-import' +import FcDesigner from '@form-create/designer' + +const components = [ + ElAside, + ElPopconfirm, + ElHeader, + ElMain, + ElContainer, + ElDivider, + ElTransfer, + ElAlert, + ElTabs, + ElTabPane +] + +export const setupFormCreate = (app: App) => { + components.forEach((component) => { + app.component(component.name, component) + }) + + formCreate.use(install) + + app.use(formCreate) + + app.use(FcDesigner) +} diff --git a/src/plugins/svgIcon/index.ts b/src/plugins/svgIcon/index.ts new file mode 100644 index 00000000..b5b7f70d --- /dev/null +++ b/src/plugins/svgIcon/index.ts @@ -0,0 +1,3 @@ +import 'virtual:svg-icons-register' + +import '@purge-icons/generated' diff --git a/src/plugins/vueI18n/helper.ts b/src/plugins/vueI18n/helper.ts new file mode 100644 index 00000000..da6bc8c9 --- /dev/null +++ b/src/plugins/vueI18n/helper.ts @@ -0,0 +1,3 @@ +export const setHtmlPageLang = (locale: LocaleType) => { + document.querySelector('html')?.setAttribute('lang', locale) +} diff --git a/src/plugins/vueI18n/index.ts b/src/plugins/vueI18n/index.ts new file mode 100644 index 00000000..f845b13f --- /dev/null +++ b/src/plugins/vueI18n/index.ts @@ -0,0 +1,42 @@ +import type { App } from 'vue' +import { createI18n } from 'vue-i18n' +import { useLocaleStoreWithOut } from '@/store/modules/locale' +import type { I18n, I18nOptions } from 'vue-i18n' +import { setHtmlPageLang } from './helper' + +export let i18n: ReturnType + +const createI18nOptions = async (): Promise => { + const localeStore = useLocaleStoreWithOut() + const locale = localeStore.getCurrentLocale + const localeMap = localeStore.getLocaleMap + const defaultLocal = await import(`../../locales/${locale.lang}.ts`) + const message = defaultLocal.default ?? {} + + setHtmlPageLang(locale.lang) + + localeStore.setCurrentLocale({ + lang: locale.lang + // elLocale: elLocal + }) + + return { + legacy: false, + locale: locale.lang, + fallbackLocale: locale.lang, + messages: { + [locale.lang]: message + }, + availableLocales: localeMap.map((v) => v.lang), + sync: true, + silentTranslationWarn: true, + missingWarn: false, + silentFallbackWarn: true + } +} + +export const setupI18n = async (app: App) => { + const options = await createI18nOptions() + i18n = createI18n(options) as I18n + app.use(i18n) +} diff --git a/src/plugins/vxeTable/index.ts b/src/plugins/vxeTable/index.ts new file mode 100644 index 00000000..6b61bb5c --- /dev/null +++ b/src/plugins/vxeTable/index.ts @@ -0,0 +1,223 @@ +import { App } from 'vue' +import XEUtils from 'xe-utils' +import './renderer' +import 'vxe-table/lib/style.css' +import { i18n } from '@/plugins/vueI18n' +import zhCN from 'vxe-table/lib/locale/lang/zh-CN' +import enUS from 'vxe-table/lib/locale/lang/en-US' +import { + // 全局对象 + VXETable, + // 表格功能 + Filter, + Edit, + Menu, + Export, + Keyboard, + Validator, + // 可选组件 + Icon, + Column, + Colgroup, + Grid, + Tooltip, + Toolbar, + Pager, + Form, + FormItem, + FormGather, + Checkbox, + CheckboxGroup, + Radio, + RadioGroup, + RadioButton, + Switch, + Input, + Select, + Optgroup, + Option, + Textarea, + Button, + Modal, + List, + Pulldown, + // 表格 + Table +} from 'vxe-table' + +// 全局默认参数 +VXETable.setup({ + size: 'medium', // 全局尺寸 + version: 0, // 版本号,对于某些带数据缓存的功能有用到,上升版本号可以用于重置数据 + zIndex: 1008, // 全局 zIndex 起始值,如果项目的的 z-index 样式值过大时就需要跟随设置更大,避免被遮挡 + loadingText: '加载中...', // 全局loading提示内容,如果为null则不显示文本 + height: 600, + table: { + border: 'inner', // default(默认), full(完整边框), outer(外边框), inner(内边框), none(无边框) + align: 'center', // eft(左对齐), center(居中对齐), right(右对齐) + autoResize: true, // 自动监听父元素的变化去重新计算表格 + resizable: true, // 列是否允许拖动列宽调整大小 + emptyText: '暂无数据', // 空表单 + highlightHoverRow: true, // 自动监听父元素的变化去重新计算表格 + treeConfig: { + rowField: 'id', + parentField: 'parentId', + children: 'children', + indent: 20, + showIcon: true + } + }, + grid: { + toolbarConfig: { + refresh: true, + export: true, + print: true, + zoom: true, + custom: true + }, + pagerConfig: { + border: false, + background: false, + autoHidden: true, + perfect: true, + pageSize: 10, + pagerCount: 7, + pageSizes: [5, 10, 15, 20, 50, 100, 200, 500], + layouts: [ + 'Sizes', + 'PrevJump', + 'PrevPage', + 'Number', + 'NextPage', + 'NextJump', + 'FullJump', + 'Total' + ] + } + }, + pager: { + background: false, + autoHidden: false, + perfect: true, + pageSize: 10, + pagerCount: 7, + pageSizes: [10, 15, 20, 50, 100], + layouts: ['PrevJump', 'PrevPage', 'Jump', 'PageCount', 'NextPage', 'NextJump', 'Sizes', 'Total'] + }, + input: { + clearable: true + }, + form: { + titleColon: true // 是否显示标题冒号 + }, + modal: { + width: 800, // 窗口的宽度 + height: 600, // 窗口的高度 + minWidth: 460, + minHeight: 320, + showZoom: true, // 标题是否标显示最大化与还原按钮 + resize: true, // 是否允许窗口边缘拖动调整窗口大小 + marginSize: 0, // 只对 resize 启用后有效,用于设置可拖动界限范围,如果为负数则允许拖动超出屏幕边界 + remember: false, // 记忆功能,会记住最后操作状态,再次打开窗口时还原窗口状态 + destroyOnClose: true, // 在窗口关闭时销毁内容 + storage: false, // 是否启用 localStorage 本地保存,会将窗口拖动的状态保存到本地 + transfer: true, // 是否将弹框容器插入于 body 内 + showFooter: true, // 是否显示底部 + mask: true, // 是否显示遮罩层 + maskClosable: true, // 是否允许点击遮罩层关闭窗口 + escClosable: true // 是否允许按 Esc 键关闭窗口 + }, + i18n: (key, args) => { + return unref(i18n.global.locale) === 'zh-CN' + ? XEUtils.toFormatString(XEUtils.get(zhCN, key), args) + : XEUtils.toFormatString(XEUtils.get(enUS, key), args) + } +}) +// 自定义全局的格式化处理函数 +VXETable.formats.mixin({ + // 格式精简日期,默认 yyyy-MM-dd HH:mm:ss + formatDay({ cellValue }, format) { + if (cellValue != null) { + return XEUtils.toDateString(cellValue, format || 'yyyy-MM-dd') + } else { + return '' + } + }, + // 格式完整日期,默认 yyyy-MM-dd HH:mm:ss + formatDate({ cellValue }, format) { + if (cellValue != null) { + return XEUtils.toDateString(cellValue, format || 'yyyy-MM-dd HH:mm:ss') + } else { + return '' + } + }, + // 四舍五入金额,每隔3位逗号分隔,默认2位数 + formatAmount({ cellValue }, digits = 2) { + return XEUtils.commafy(Number(cellValue), { digits }) + }, + // 格式化银行卡,默认每4位空格隔开 + formatBankcard({ cellValue }) { + return XEUtils.commafy(XEUtils.toValueString(cellValue), { spaceNumber: 4, separator: ' ' }) + }, + // 四舍五入,默认两位数 + formatFixedNumber({ cellValue }, digits = 2) { + return XEUtils.toFixed(XEUtils.round(cellValue, digits), digits) + }, + // 向下舍入,默认两位数 + formatCutNumber({ cellValue }, digits = 2) { + return XEUtils.toFixed(XEUtils.floor(cellValue, digits), digits) + }, + // 格式化图片,将图片链接转换为html标签 + formatImg({ cellValue }) { + return ' ' + }, + // 格式化文件大小 + formatSize({ cellValue }, digits = 0) { + const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + const srcSize = parseFloat(cellValue) + const index = Math.floor(Math.log(srcSize) / Math.log(1024)) + const size = srcSize / Math.pow(1024, index) + return XEUtils.toFixed(XEUtils.floor(size, 2), 2) + ' ' + unitArr[digits] + } +}) +export const setupVxeTable = (app: App) => { + // 表格功能 + app.use(Filter).use(Edit).use(Menu).use(Export).use(Keyboard).use(Validator) + + // 可选组件 + app + .use(Icon) + .use(Column) + .use(Colgroup) + .use(Grid) + .use(Tooltip) + .use(Toolbar) + .use(Pager) + .use(Form) + .use(FormItem) + .use(FormGather) + .use(Checkbox) + .use(CheckboxGroup) + .use(Radio) + .use(RadioGroup) + .use(RadioButton) + .use(Switch) + .use(Input) + .use(Select) + .use(Optgroup) + .use(Option) + .use(Textarea) + .use(Button) + .use(Modal) + .use(List) + .use(Pulldown) + + // 安装表格 + .use(Table) + + // 给 vue 实例挂载内部对象,例如: + // app.config.globalProperties.$XModal = VXETable.modal + // app.config.globalProperties.$XPrint = VXETable.print + // app.config.globalProperties.$XSaveFile = VXETable.saveFile + // app.config.globalProperties.$XReadFile = VXETable.readFile +} diff --git a/src/plugins/vxeTable/renderer/dataPicker.tsx b/src/plugins/vxeTable/renderer/dataPicker.tsx new file mode 100644 index 00000000..6a1c1d8f --- /dev/null +++ b/src/plugins/vxeTable/renderer/dataPicker.tsx @@ -0,0 +1,20 @@ +import { ElDatePicker } from 'element-plus' +import { VXETable } from 'vxe-table' + +// 日期区间选择渲染 +VXETable.renderer.add('XDataPicker', { + // 默认显示模板 + renderItemContent(renderOpts, params) { + const { data, field } = params + const { content } = renderOpts + return ( + + ) + } +}) diff --git a/src/plugins/vxeTable/renderer/dataTimeRangePicker.tsx b/src/plugins/vxeTable/renderer/dataTimeRangePicker.tsx new file mode 100644 index 00000000..d6508d2c --- /dev/null +++ b/src/plugins/vxeTable/renderer/dataTimeRangePicker.tsx @@ -0,0 +1,23 @@ +import { ElDatePicker } from 'element-plus' +import { VXETable } from 'vxe-table' + +// 日期区间选择渲染 +VXETable.renderer.add('XDataTimePicker', { + // 默认显示模板 + renderItemContent(renderOpts, params) { + const { t } = useI18n() + const { data, field } = params + const { content } = renderOpts + return ( + + ) + } +}) diff --git a/src/plugins/vxeTable/renderer/dict.tsx b/src/plugins/vxeTable/renderer/dict.tsx new file mode 100644 index 00000000..9cdf282d --- /dev/null +++ b/src/plugins/vxeTable/renderer/dict.tsx @@ -0,0 +1,12 @@ +import { DictTag } from '@/components/DictTag' +import { VXETable } from 'vxe-table' + +// 字典渲染 +VXETable.renderer.add('XDict', { + // 默认显示模板 + renderDefault(renderOpts, params) { + const { row, column } = params + const { content } = renderOpts + return + } +}) diff --git a/src/plugins/vxeTable/renderer/html.tsx b/src/plugins/vxeTable/renderer/html.tsx new file mode 100644 index 00000000..46483a1c --- /dev/null +++ b/src/plugins/vxeTable/renderer/html.tsx @@ -0,0 +1,10 @@ +import { VXETable } from 'vxe-table' + +// 图片渲染 +VXETable.renderer.add('XHtml', { + // 默认显示模板 + renderDefault(_renderOpts, params) { + const { row, column } = params + return + } +}) diff --git a/src/plugins/vxeTable/renderer/img.tsx b/src/plugins/vxeTable/renderer/img.tsx new file mode 100644 index 00000000..fbbf24a1 --- /dev/null +++ b/src/plugins/vxeTable/renderer/img.tsx @@ -0,0 +1,20 @@ +import { VXETable } from 'vxe-table' +import { ElImage } from 'element-plus' + +// 图片渲染 +VXETable.renderer.add('XImg', { + // 默认显示模板 + renderDefault(_renderOpts, params) { + const { row, column } = params + return ( + + ) + } +}) diff --git a/src/plugins/vxeTable/renderer/index.tsx b/src/plugins/vxeTable/renderer/index.tsx new file mode 100644 index 00000000..744e46c3 --- /dev/null +++ b/src/plugins/vxeTable/renderer/index.tsx @@ -0,0 +1,7 @@ +import './dataPicker' +import './dataTimeRangePicker' +import './dict' +import './html' +import './link' +import './img' +import './preview' diff --git a/src/plugins/vxeTable/renderer/link.tsx b/src/plugins/vxeTable/renderer/link.tsx new file mode 100644 index 00000000..84e1a5a1 --- /dev/null +++ b/src/plugins/vxeTable/renderer/link.tsx @@ -0,0 +1,15 @@ +import { VXETable } from 'vxe-table' + +// 超链接渲染 +VXETable.renderer.add('XLink', { + // 默认显示模板 + renderDefault(renderOpts, params) { + const { row, column } = params + const { events = {} } = renderOpts + return ( + events.click(params)}> + {row[column.field]} + + ) + } +}) diff --git a/src/plugins/vxeTable/renderer/preview.tsx b/src/plugins/vxeTable/renderer/preview.tsx new file mode 100644 index 00000000..3cb1270b --- /dev/null +++ b/src/plugins/vxeTable/renderer/preview.tsx @@ -0,0 +1,34 @@ +import { VXETable } from 'vxe-table' +import { ElImage, ElLink } from 'element-plus' + +// 图片渲染 +VXETable.renderer.add('XPreview', { + // 默认显示模板 + renderDefault(_renderOpts, params) { + const { row, column } = params + if (row.type.indexOf('image/') === 0) { + return ( + + ) + } else if (row.type.indexOf('video/') === 0) { + return ( + + ) + } else { + return ( + + {row[column.field]} + + ) + } + } +}) diff --git a/src/plugins/windi.css/index.ts b/src/plugins/windi.css/index.ts new file mode 100644 index 00000000..dbdfbbf2 --- /dev/null +++ b/src/plugins/windi.css/index.ts @@ -0,0 +1,3 @@ +import 'virtual:windi.css' + +import 'virtual:windi-devtools' diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 00000000..02d913f8 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,28 @@ +import type { App } from 'vue' +import type { RouteRecordRaw } from 'vue-router' +import { createRouter, createWebHashHistory } from 'vue-router' +import remainingRouter from './modules/remaining' + +// 创建路由实例 +const router = createRouter({ + history: createWebHashHistory(), // createWebHashHistory URL带#,createWebHistory URL不带# + strict: true, + routes: remainingRouter as RouteRecordRaw[], + scrollBehavior: () => ({ left: 0, top: 0 }) +}) + +export const resetRouter = (): void => { + const resetWhiteNameList = ['Redirect', 'Login', 'NoFind', 'Root'] + router.getRoutes().forEach((route) => { + const { name } = route + if (name && !resetWhiteNameList.includes(name as string)) { + router.hasRoute(name) && router.removeRoute(name) + } + }) +} + +export const setupRouter = (app: App) => { + app.use(router) +} + +export default router diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts new file mode 100644 index 00000000..43375961 --- /dev/null +++ b/src/router/modules/remaining.ts @@ -0,0 +1,300 @@ +import { Layout } from '@/utils/routerHelper' + +const { t } = useI18n() +/** +* redirect: noredirect 当设置 noredirect 的时候该路由在面包屑导航中不可被点击 +* name:'router-name' 设定路由的名字,一定要填写不然使用时会出现各种问题 +* meta : { + hidden: true 当设置 true 的时候该路由不会再侧边栏出现 如404,login等页面(默认 false) + + alwaysShow: true 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式, + 只有一个时,会将那个子路由当做根路由显示在侧边栏, + 若你想不管路由下面的 children 声明的个数都显示你的根路由, + 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则, + 一直显示根路由(默认 false) + + title: 'title' 设置该路由在侧边栏和面包屑中展示的名字 + + icon: 'svg-name' 设置该路由的图标 + + noCache: true 如果设置为true,则不会被 缓存(默认 false) + + breadcrumb: false 如果设置为false,则不会在breadcrumb面包屑中显示(默认 true) + + affix: true 如果设置为true,则会一直固定在tag项中(默认 false) + + noTagsView: true 如果设置为true,则不会出现在tag中(默认 false) + + activeMenu: '/dashboard' 显示高亮的路由路径 + + followAuth: '/dashboard' 跟随哪个路由进行权限过滤 + + canTo: true 设置为true即使hidden为true,也依然可以进行路由跳转(默认 false) + } +**/ +const remainingRouter: AppRouteRecordRaw[] = [ + { + path: '/redirect', + component: Layout, + name: 'Redirect', + children: [ + { + path: '/redirect/:path(.*)', + name: 'Redirect', + component: () => import('@/views/Redirect/Redirect.vue'), + meta: {} + } + ], + meta: { + hidden: true, + noTagsView: true + } + }, + { + path: '/', + component: Layout, + redirect: '/index', + name: 'Home', + meta: {}, + children: [ + { + path: 'index', + component: () => import('@/views/Home/Index.vue'), + name: 'Index', + meta: { + title: t('router.home'), + icon: 'ep:home-filled', + noCache: false, + affix: true + } + } + ] + }, + { + path: '/user', + component: Layout, + name: 'UserInfo', + meta: { + hidden: true + }, + children: [ + { + path: 'profile', + component: () => import('@/views/Profile/Index.vue'), + name: 'Profile', + meta: { + canTo: true, + hidden: true, + noTagsView: false, + icon: 'ep:user', + title: t('common.profile') + } + }, + { + path: 'notify-message', + component: () => import('@/views/system/notify/my/index.vue'), + name: 'MyNotifyMessage', + meta: { + canTo: true, + hidden: true, + noTagsView: false, + icon: 'ep:message', + title: '我的站内信' + } + } + ] + }, + { + path: '/codegen', + component: Layout, + name: 'CodegenEdit', + meta: { + hidden: true + }, + children: [ + { + path: 'edit', + component: () => import('@/views/infra/codegen/EditTable.vue'), + name: 'EditTable', + meta: { + noCache: true, + hidden: true, + canTo: true, + icon: 'ep:edit', + title: '修改生成配置', + activeMenu: 'infra/codegen/index' + } + } + ] + }, + { + path: '/job', + component: Layout, + name: 'JobL', + meta: { + hidden: true + }, + children: [ + { + path: 'job-log', + component: () => import('@/views/infra/job/JobLog.vue'), + name: 'JobLog', + meta: { + noCache: true, + hidden: true, + canTo: true, + icon: 'ep:edit', + title: '调度日志', + activeMenu: 'infra/job/index' + } + } + ] + }, + { + path: '/login', + component: () => import('@/views/Login/Login.vue'), + name: 'Login', + meta: { + hidden: true, + title: t('router.login'), + noTagsView: true + } + }, + { + path: '/403', + component: () => import('@/views/Error/403.vue'), + name: 'NoAccess', + meta: { + hidden: true, + title: '403', + noTagsView: true + } + }, + { + path: '/404', + component: () => import('@/views/Error/404.vue'), + name: 'NoFound', + meta: { + hidden: true, + title: '404', + noTagsView: true + } + }, + { + path: '/500', + component: () => import('@/views/Error/500.vue'), + name: 'Error', + meta: { + hidden: true, + title: '500', + noTagsView: true + } + }, + { + path: '/bpm', + component: Layout, + name: 'bpm', + meta: { + hidden: true + }, + children: [ + { + path: '/manager/form/edit', + component: () => import('@/views/bpm/form/formEditor.vue'), + name: 'bpmFormEditor', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '设计流程表单', + activeMenu: 'bpm/manager/form/formEditor' + } + }, + { + path: '/manager/model/edit', + component: () => import('@/views/bpm/model/modelEditor.vue'), + name: 'modelEditor', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '设计流程', + activeMenu: 'bpm/manager/model/design' + } + }, + { + path: '/manager/definition', + component: () => import('@/views/bpm/definition/index.vue'), + name: 'BpmProcessDefinitionList', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '流程定义', + activeMenu: 'bpm/definition/index' + } + }, + { + path: '/manager/task-assign-rule', + component: () => import('@/views/bpm/taskAssignRule/index.vue'), + name: 'BpmTaskAssignRuleList', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '任务分配规则' + } + }, + { + path: '/process-instance/create', + component: () => import('@/views/bpm/processInstance/create.vue'), + name: 'BpmProcessInstanceCreate', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '发起流程', + activeMenu: 'bpm/processInstance/create' + } + }, + { + path: '/process-instance/detail', + component: () => import('@/views/bpm/processInstance/detail.vue'), + name: 'BpmProcessInstanceDetail', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '流程详情', + activeMenu: 'bpm/processInstance/detail' + } + }, + { + path: '/bpm/oa/leave/create', + component: () => import('@/views/bpm/oa/leave/create.vue'), + name: 'OALeaveCreate', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '发起 OA 请假', + activeMenu: 'bpm/oa/leave/create' + } + }, + { + path: '/bpm/oa/leave/detail', + component: () => import('@/views/bpm/oa/leave/detail.vue'), + name: 'OALeaveDetail', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '查看 OA 请假', + activeMenu: 'bpm/oa/leave/detail' + } + } + ] + } +] + +export default remainingRouter diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 00000000..65964ea8 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,10 @@ +import type { App } from 'vue' +import { createPinia } from 'pinia' + +const store = createPinia() + +export const 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..2489f87e --- /dev/null +++ b/src/store/modules/app.ts @@ -0,0 +1,274 @@ +import { defineStore } from 'pinia' +import { store } from '../index' +import { setCssVar, humpToUnderline } from '@/utils' +import { ElMessage } from 'element-plus' +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' +import { ElementPlusSize } from '@/types/elementPlus' +import { LayoutType } from '@/types/layout' +import { ThemeTypes } from '@/types/theme' + +const { wsCache } = useCache() + +interface AppState { + breadcrumb: boolean + breadcrumbIcon: boolean + collapse: boolean + uniqueOpened: boolean + hamburger: boolean + screenfull: boolean + size: boolean + locale: boolean + message: boolean + tagsView: boolean + tagsViewIcon: boolean + logo: boolean + fixedHeader: boolean + greyMode: boolean + pageLoading: boolean + layout: LayoutType + title: string + userInfo: string + isDark: boolean + currentSize: ElementPlusSize + sizeMap: ElementPlusSize[] + mobile: boolean + footer: boolean + theme: ThemeTypes + fixedMenu: boolean +} + +export const useAppStore = defineStore('app', { + state: (): AppState => { + return { + userInfo: 'userInfo', // 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突 + sizeMap: ['default', 'large', 'small'], + mobile: false, // 是否是移动端 + title: import.meta.env.VITE_APP_TITLE, // 标题 + pageLoading: false, // 路由跳转loading + + breadcrumb: true, // 面包屑 + breadcrumbIcon: true, // 面包屑图标 + collapse: false, // 折叠菜单 + uniqueOpened: true, // 是否只保持一个子菜单的展开 + hamburger: true, // 折叠图标 + screenfull: true, // 全屏图标 + size: true, // 尺寸图标 + locale: true, // 多语言图标 + message: true, // 消息图标 + tagsView: true, // 标签页 + tagsViewIcon: true, // 是否显示标签图标 + logo: true, // logo + fixedHeader: true, // 固定toolheader + footer: true, // 显示页脚 + greyMode: false, // 是否开始灰色模式,用于特殊悼念日 + fixedMenu: wsCache.get('fixedMenu') || false, // 是否固定菜单 + + layout: wsCache.get(CACHE_KEY.LAYOUT) || 'classic', // layout布局 + isDark: wsCache.get(CACHE_KEY.IS_DARK) || false, // 是否是暗黑模式 + currentSize: wsCache.get('default') || 'default', // 组件尺寸 + theme: wsCache.get(CACHE_KEY.THEME) || { + // 主题色 + elColorPrimary: '#409eff', + // 左侧菜单边框颜色 + leftMenuBorderColor: 'inherit', + // 左侧菜单背景颜色 + leftMenuBgColor: '#001529', + // 左侧菜单浅色背景颜色 + leftMenuBgLightColor: '#0f2438', + // 左侧菜单选中背景颜色 + leftMenuBgActiveColor: 'var(--el-color-primary)', + // 左侧菜单收起选中背景颜色 + leftMenuCollapseBgActiveColor: 'var(--el-color-primary)', + // 左侧菜单字体颜色 + leftMenuTextColor: '#bfcbd9', + // 左侧菜单选中字体颜色 + leftMenuTextActiveColor: '#fff', + // logo字体颜色 + logoTitleTextColor: '#fff', + // logo边框颜色 + logoBorderColor: 'inherit', + // 头部背景颜色 + topHeaderBgColor: '#fff', + // 头部字体颜色 + topHeaderTextColor: 'inherit', + // 头部悬停颜色 + topHeaderHoverColor: '#f6f6f6', + // 头部边框颜色 + topToolBorderColor: '#eee' + } + } + }, + getters: { + getBreadcrumb(): boolean { + return this.breadcrumb + }, + getBreadcrumbIcon(): boolean { + return this.breadcrumbIcon + }, + getCollapse(): boolean { + return this.collapse + }, + getUniqueOpened(): boolean { + return this.uniqueOpened + }, + getHamburger(): boolean { + return this.hamburger + }, + getScreenfull(): boolean { + return this.screenfull + }, + getSize(): boolean { + return this.size + }, + getLocale(): boolean { + return this.locale + }, + getMessage(): boolean { + return this.message + }, + getTagsView(): boolean { + return this.tagsView + }, + getTagsViewIcon(): boolean { + return this.tagsViewIcon + }, + getLogo(): boolean { + return this.logo + }, + getFixedHeader(): boolean { + return this.fixedHeader + }, + getGreyMode(): boolean { + return this.greyMode + }, + getFixedMenu(): boolean { + return this.fixedMenu + }, + getPageLoading(): boolean { + return this.pageLoading + }, + getLayout(): LayoutType { + return this.layout + }, + getTitle(): string { + return this.title + }, + getUserInfo(): string { + return this.userInfo + }, + getIsDark(): boolean { + return this.isDark + }, + getCurrentSize(): ElementPlusSize { + return this.currentSize + }, + getSizeMap(): ElementPlusSize[] { + return this.sizeMap + }, + getMobile(): boolean { + return this.mobile + }, + getTheme(): ThemeTypes { + return this.theme + }, + getFooter(): boolean { + return this.footer + } + }, + actions: { + setBreadcrumb(breadcrumb: boolean) { + this.breadcrumb = breadcrumb + }, + setBreadcrumbIcon(breadcrumbIcon: boolean) { + this.breadcrumbIcon = breadcrumbIcon + }, + setCollapse(collapse: boolean) { + this.collapse = collapse + }, + setUniqueOpened(uniqueOpened: boolean) { + this.uniqueOpened = uniqueOpened + }, + setHamburger(hamburger: boolean) { + this.hamburger = hamburger + }, + setScreenfull(screenfull: boolean) { + this.screenfull = screenfull + }, + setSize(size: boolean) { + this.size = size + }, + setLocale(locale: boolean) { + this.locale = locale + }, + setMessage(message: boolean) { + this.message = message + }, + setTagsView(tagsView: boolean) { + this.tagsView = tagsView + }, + setTagsViewIcon(tagsViewIcon: boolean) { + this.tagsViewIcon = tagsViewIcon + }, + setLogo(logo: boolean) { + this.logo = logo + }, + setFixedHeader(fixedHeader: boolean) { + this.fixedHeader = fixedHeader + }, + setGreyMode(greyMode: boolean) { + this.greyMode = greyMode + }, + setFixedMenu(fixedMenu: boolean) { + wsCache.set('fixedMenu', fixedMenu) + this.fixedMenu = fixedMenu + }, + setPageLoading(pageLoading: boolean) { + this.pageLoading = pageLoading + }, + setLayout(layout: LayoutType) { + if (this.mobile && layout !== 'classic') { + ElMessage.warning('移动端模式下不支持切换其他布局') + return + } + this.layout = layout + wsCache.set(CACHE_KEY.LAYOUT, this.layout) + }, + setTitle(title: string) { + this.title = title + }, + setIsDark(isDark: boolean) { + this.isDark = isDark + if (this.isDark) { + document.documentElement.classList.add('dark') + document.documentElement.classList.remove('light') + } else { + document.documentElement.classList.add('light') + document.documentElement.classList.remove('dark') + } + wsCache.set(CACHE_KEY.IS_DARK, this.isDark) + }, + setCurrentSize(currentSize: ElementPlusSize) { + this.currentSize = currentSize + wsCache.set('currentSize', this.currentSize) + }, + setMobile(mobile: boolean) { + this.mobile = mobile + }, + setTheme(theme: ThemeTypes) { + this.theme = Object.assign(this.theme, theme) + wsCache.set(CACHE_KEY.THEME, this.theme) + }, + setCssVarTheme() { + for (const key in this.theme) { + setCssVar(`--${humpToUnderline(key)}`, this.theme[key]) + } + }, + setFooter(footer: boolean) { + this.footer = footer + } + } +}) + +export const useAppStoreWithOut = () => { + return useAppStore(store) +} diff --git a/src/store/modules/dict.ts b/src/store/modules/dict.ts new file mode 100644 index 00000000..4e9ab39c --- /dev/null +++ b/src/store/modules/dict.ts @@ -0,0 +1,103 @@ +import { defineStore } from 'pinia' +import { store } from '../index' +import { DictDataVO } from '@/api/system/dict/types' +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' +const { wsCache } = useCache('sessionStorage') +import { listSimpleDictDataApi } from '@/api/system/dict/dict.data' + +export interface DictValueType { + value: any + label: string + clorType?: string + cssClass?: string +} +export interface DictTypeType { + dictType: string + dictValue: DictValueType[] +} +export interface DictState { + dictMap: Map + isSetDict: boolean +} + +export const useDictStore = defineStore('dict', { + state: (): DictState => ({ + dictMap: new Map(), + isSetDict: false + }), + getters: { + getDictMap(): Recordable { + const dictMap = wsCache.get(CACHE_KEY.DICT_CACHE) + if (dictMap) { + this.dictMap = dictMap + } + return this.dictMap + }, + getIsSetDict(): boolean { + return this.isSetDict + } + }, + actions: { + async setDictMap() { + const dictMap = wsCache.get(CACHE_KEY.DICT_CACHE) + if (dictMap) { + this.dictMap = dictMap + this.isSetDict = true + } else { + const res = await listSimpleDictDataApi() + // 设置数据 + const dictDataMap = new Map() + res.forEach((dictData: DictDataVO) => { + // 获得 dictType 层级 + const enumValueObj = dictDataMap[dictData.dictType] + if (!enumValueObj) { + dictDataMap[dictData.dictType] = [] + } + // 处理 dictValue 层级 + dictDataMap[dictData.dictType].push({ + value: dictData.value, + label: dictData.label, + colorType: dictData.colorType, + cssClass: dictData.cssClass + }) + }) + this.dictMap = dictDataMap + this.isSetDict = true + wsCache.set(CACHE_KEY.DICT_CACHE, dictDataMap, { exp: 60 }) // 60 秒 过期 + } + }, + getDictByType(type: string) { + if (!this.isSetDict) { + this.setDictMap() + } + return this.dictMap[type] + }, + async resetDict() { + wsCache.delete(CACHE_KEY.DICT_CACHE) + const res = await listSimpleDictDataApi() + // 设置数据 + const dictDataMap = new Map() + res.forEach((dictData: DictDataVO) => { + // 获得 dictType 层级 + const enumValueObj = dictDataMap[dictData.dictType] + if (!enumValueObj) { + dictDataMap[dictData.dictType] = [] + } + // 处理 dictValue 层级 + dictDataMap[dictData.dictType].push({ + value: dictData.value, + label: dictData.label, + colorType: dictData.colorType, + cssClass: dictData.cssClass + }) + }) + this.dictMap = dictDataMap + this.isSetDict = true + wsCache.set(CACHE_KEY.DICT_CACHE, dictDataMap, { exp: 60 }) // 60 秒 过期 + } + } +}) + +export const useDictStoreWithOut = () => { + return useDictStore(store) +} diff --git a/src/store/modules/locale.ts b/src/store/modules/locale.ts new file mode 100644 index 00000000..1fc772a7 --- /dev/null +++ b/src/store/modules/locale.ts @@ -0,0 +1,59 @@ +import { defineStore } from 'pinia' +import { store } from '../index' +import zhCn from 'element-plus/es/locale/lang/zh-cn' +import en from 'element-plus/es/locale/lang/en' +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' +import { LocaleDropdownType } from '@/types/localeDropdown' + +const { wsCache } = useCache() + +const elLocaleMap = { + 'zh-CN': zhCn, + en: en +} +interface LocaleState { + currentLocale: LocaleDropdownType + localeMap: LocaleDropdownType[] +} + +export const useLocaleStore = defineStore('locales', { + state: (): LocaleState => { + return { + currentLocale: { + lang: wsCache.get(CACHE_KEY.LANG) || 'zh-CN', + elLocale: elLocaleMap[wsCache.get(CACHE_KEY.LANG) || 'zh-CN'] + }, + // 多语言 + localeMap: [ + { + lang: 'zh-CN', + name: '简体中文' + }, + { + lang: 'en', + name: 'English' + } + ] + } + }, + getters: { + getCurrentLocale(): LocaleDropdownType { + return this.currentLocale + }, + getLocaleMap(): LocaleDropdownType[] { + return this.localeMap + } + }, + actions: { + setCurrentLocale(localeMap: LocaleDropdownType) { + // this.locale = Object.assign(this.locale, localeMap) + this.currentLocale.lang = localeMap?.lang + this.currentLocale.elLocale = elLocaleMap[localeMap?.lang] + wsCache.set(CACHE_KEY.LANG, localeMap?.lang) + } + } +}) + +export const useLocaleStoreWithOut = () => { + return useLocaleStore(store) +} diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts new file mode 100644 index 00000000..491bafdc --- /dev/null +++ b/src/store/modules/permission.ts @@ -0,0 +1,70 @@ +import { defineStore } from 'pinia' +import { store } from '../index' +import { cloneDeep } from 'lodash-es' +import remainingRouter from '@/router/modules/remaining' +import { generateRoute, flatMultiLevelRoutes } from '@/utils/routerHelper' +import { getAsyncRoutesApi } from '@/api/login' +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' + +const { wsCache } = useCache() + +export interface PermissionState { + routers: AppRouteRecordRaw[] + addRouters: AppRouteRecordRaw[] + menuTabRouters: AppRouteRecordRaw[] +} + +export const usePermissionStore = defineStore('permission', { + state: (): PermissionState => ({ + routers: [], + addRouters: [], + menuTabRouters: [] + }), + getters: { + getRouters(): AppRouteRecordRaw[] { + return this.routers + }, + getAddRouters(): AppRouteRecordRaw[] { + return flatMultiLevelRoutes(cloneDeep(this.addRouters)) + }, + getMenuTabRouters(): AppRouteRecordRaw[] { + return this.menuTabRouters + } + }, + actions: { + async generateRoutes(): Promise { + return new Promise(async (resolve) => { + let res: AppCustomRouteRecordRaw[] + if (wsCache.get(CACHE_KEY.ROLE_ROUTERS)) { + res = wsCache.get(CACHE_KEY.ROLE_ROUTERS) as AppCustomRouteRecordRaw[] + } else { + res = await getAsyncRoutesApi() + wsCache.set(CACHE_KEY.ROLE_ROUTERS, res) + } + const routerMap: AppRouteRecordRaw[] = generateRoute(res as AppCustomRouteRecordRaw[]) + // 动态路由,404一定要放到最后面 + this.addRouters = routerMap.concat([ + { + path: '/:path(.*)*', + redirect: '/404', + name: '404Page', + meta: { + hidden: true, + breadcrumb: false + } + } + ]) + // 渲染菜单的所有路由 + this.routers = cloneDeep(remainingRouter).concat(routerMap) + resolve() + }) + }, + setMenuTabRouters(routers: AppRouteRecordRaw[]): void { + this.menuTabRouters = routers + } + } +}) + +export const usePermissionStoreWithOut = () => { + return usePermissionStore(store) +} diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts new file mode 100644 index 00000000..e9fbf371 --- /dev/null +++ b/src/store/modules/tagsView.ts @@ -0,0 +1,140 @@ +import router from '@/router' +import type { RouteLocationNormalizedLoaded } from 'vue-router' +import { getRawRoute } from '@/utils/routerHelper' +import { defineStore } from 'pinia' +import { store } from '../index' +import { findIndex } from '@/utils' + +export interface TagsViewState { + visitedViews: RouteLocationNormalizedLoaded[] + cachedViews: Set +} + +export const useTagsViewStore = defineStore('tagsView', { + state: (): TagsViewState => ({ + visitedViews: [], + cachedViews: new Set() + }), + getters: { + getVisitedViews(): RouteLocationNormalizedLoaded[] { + return this.visitedViews + }, + getCachedViews(): string[] { + return Array.from(this.cachedViews) + } + }, + actions: { + // 新增缓存和tag + addView(view: RouteLocationNormalizedLoaded): void { + this.addVisitedView(view) + this.addCachedView() + }, + // 新增tag + addVisitedView(view: RouteLocationNormalizedLoaded) { + if (this.visitedViews.some((v) => v.path === view.path)) return + if (view.meta?.noTagsView) return + this.visitedViews.push( + Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }) + ) + }, + // 新增缓存 + addCachedView() { + const cacheMap: Set = new Set() + for (const v of this.visitedViews) { + const item = getRawRoute(v) + const needCache = !item.meta?.noCache + if (!needCache) { + continue + } + const name = item.name as string + cacheMap.add(name) + } + if (Array.from(this.cachedViews).sort().toString() === Array.from(cacheMap).sort().toString()) + return + this.cachedViews = cacheMap + }, + // 删除某个 + delView(view: RouteLocationNormalizedLoaded) { + this.delVisitedView(view) + this.addCachedView() + }, + // 删除tag + delVisitedView(view: RouteLocationNormalizedLoaded) { + for (const [i, v] of this.visitedViews.entries()) { + if (v.path === view.path) { + this.visitedViews.splice(i, 1) + break + } + } + }, + // 删除缓存 + delCachedView() { + const route = router.currentRoute.value + const index = findIndex(this.getCachedViews, (v) => v === route.name) + if (index > -1) { + this.cachedViews.delete(this.getCachedViews[index]) + } + }, + // 删除所有缓存和tag + delAllViews() { + this.delAllVisitedViews() + this.addCachedView() + }, + // 删除所有tag + delAllVisitedViews() { + // const affixTags = this.visitedViews.filter((tag) => tag.meta.affix) + this.visitedViews = [] + }, + // 删除其他 + delOthersViews(view: RouteLocationNormalizedLoaded) { + this.delOthersVisitedViews(view) + this.addCachedView() + }, + // 删除其他tag + delOthersVisitedViews(view: RouteLocationNormalizedLoaded) { + this.visitedViews = this.visitedViews.filter((v) => { + return v?.meta?.affix || v.path === view.path + }) + }, + // 删除左侧 + delLeftViews(view: RouteLocationNormalizedLoaded) { + const index = findIndex( + this.visitedViews, + (v) => v.path === view.path + ) + if (index > -1) { + this.visitedViews = this.visitedViews.filter((v, i) => { + return v?.meta?.affix || v.path === view.path || i > index + }) + this.addCachedView() + } + }, + // 删除右侧 + delRightViews(view: RouteLocationNormalizedLoaded) { + const index = findIndex( + this.visitedViews, + (v) => v.path === view.path + ) + if (index > -1) { + this.visitedViews = this.visitedViews.filter((v, i) => { + return v?.meta?.affix || v.path === view.path || i < index + }) + this.addCachedView() + } + }, + updateVisitedView(view: RouteLocationNormalizedLoaded) { + for (let v of this.visitedViews) { + if (v.path === view.path) { + v = Object.assign(v, view) + break + } + } + } + } +}) + +export const useTagsViewStoreWithOut = () => { + return useTagsViewStore(store) +} diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts new file mode 100644 index 00000000..16ff3902 --- /dev/null +++ b/src/store/modules/user.ts @@ -0,0 +1,83 @@ +import { store } from '../index' +import { defineStore } from 'pinia' +import { getAccessToken, removeToken } from '@/utils/auth' +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' +import { getInfoApi, loginOutApi } from '@/api/login' + +const { wsCache } = useCache() + +interface UserVO { + id: number + avatar: string + nickname: string +} +interface UserInfoVO { + permissions: string[] + roles: string[] + isSetUser: boolean + user: UserVO +} + +export const useUserStore = defineStore('admin-user', { + state: (): UserInfoVO => ({ + permissions: [], + roles: [], + isSetUser: false, + user: { + id: 0, + avatar: '', + nickname: '' + } + }), + getters: { + getPermissions(): string[] { + return this.permissions + }, + getRoles(): string[] { + return this.roles + }, + getIsSetUser(): boolean { + return this.isSetUser + }, + getUser(): UserVO { + return this.user + } + }, + actions: { + async setUserInfoAction() { + if (!getAccessToken()) { + this.resetState() + return null + } + let userInfo = wsCache.get(CACHE_KEY.USER) + if (!userInfo) { + userInfo = await getInfoApi() + } + this.permissions = userInfo.permissions + this.roles = userInfo.roles + this.user = userInfo.user + this.isSetUser = true + wsCache.set(CACHE_KEY.USER, userInfo) + }, + async loginOut() { + await loginOutApi() + removeToken() + wsCache.clear() + this.resetState() + }, + resetState() { + this.permissions = [] + this.roles = [] + this.isSetUser = false + this.user = { + id: 0, + avatar: '', + nickname: '' + } + } + } +}) + +export const useUserStoreWithOut = () => { + return useUserStore(store) +} diff --git a/src/styles/global.module.scss b/src/styles/global.module.scss new file mode 100644 index 00000000..8448a924 --- /dev/null +++ b/src/styles/global.module.scss @@ -0,0 +1,6 @@ +@import './variables.scss'; +// 导出变量 +:export { + namespace: $namespace; + elNamespace: $elNamespace; +} diff --git a/src/styles/index.scss b/src/styles/index.scss new file mode 100644 index 00000000..d53c2a6c --- /dev/null +++ b/src/styles/index.scss @@ -0,0 +1,23 @@ +@import './var.css'; +@import './vxe.css'; +@import 'element-plus/theme-chalk/dark/css-vars.css'; + +.reset-margin [class*='el-icon'] + span { + margin-left: 2px !important; +} + +/* nprogress 适配 element-plus 的主题色 */ +#nprogress { + & .bar { + background-color: var(--el-color-primary) !important; + } + + & .peg { + box-shadow: 0 0 10px var(--el-color-primary), 0 0 5px var(--el-color-primary) !important; + } + + & .spinner-icon { + border-top-color: var(--el-color-primary); + border-left-color: var(--el-color-primary); + } +} diff --git a/src/styles/theme.scss b/src/styles/theme.scss new file mode 100644 index 00000000..39b03b3d --- /dev/null +++ b/src/styles/theme.scss @@ -0,0 +1,6 @@ +// .text-color { +// color: var(--el-text-color-regular); +// } +// .dark .dark\:text-color { +// color: rgba(255, 255, 255, var(--dark-text-color)); +// } diff --git a/src/styles/var.css b/src/styles/var.css new file mode 100644 index 00000000..d0badc16 --- /dev/null +++ b/src/styles/var.css @@ -0,0 +1,73 @@ +:root { + --dark-bg-color: #293146; + + /* left menu start */ + --left-menu-border-color: '#eee'; + + --left-menu-max-width: 200px; + + --left-menu-min-width: 64px; + + --left-menu-bg-color: #001529; + + --left-menu-bg-light-color: #0f2438; + + --left-menu-bg-active-color: var(--el-color-primary); + + --left-menu-text-color: #bfcbd9; + + --left-menu-text-active-color: #fff; + + --left-menu-collapse-bg-active-color: var(--el-color-primary); + /* left menu end */ + + /* logo start */ + --logo-height: 50px; + + --logo-title-text-color: #fff; + + --logo-border-color: 'inherit'; + /* logo end */ + + /* header start */ + --top-header-bg-color: '#fff'; + + --top-header-text-color: 'inherit'; + + --top-header-hover-color: #f6f6f6; + + --top-tool-height: var(--logo-height); + + --top-tool-p-x: 0; + + --top-tool-border-color: #eee; + + --tags-view-height: 35px; + + --tags-view-border-color: #eee; + /* header start */ + + /* tab menu start */ + --tab-menu-max-width: 80px; + + --tab-menu-min-width: 30px; + + --tab-menu-collapse-height: 36px; + + --tab-menu-border-color: #eee; + /* tab menu end */ + + --app-content-padding: 20px; + + --app-contnet-bg-color: #f5f7f9; + + --app-footer-height: 50px; + + --transition-time-02: 0.2s; +} + +html, +body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 00000000..ffa78c9f --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,6 @@ +// 命名空间 +$namespace: v; +// el命名空间 +$elNamespace: el; +// vxe命名空间 +$vxeNamespace: vxe; diff --git a/src/styles/vxe.css b/src/styles/vxe.css new file mode 100644 index 00000000..30bdd58d --- /dev/null +++ b/src/styles/vxe.css @@ -0,0 +1,30 @@ +/*滚动条整体部分*/ +.xtable-scrollbar ::-webkit-scrollbar { + width: 10px; + height: 10px; +} +/*滚动条的轨道*/ +.xtable-scrollbar ::-webkit-scrollbar-track { + background-color: #ffffff; +} +/*滚动条里面的小方块,能向上向下移动*/ +.xtable-scrollbar ::-webkit-scrollbar-thumb { + background-color: #bfbfbf; + border-radius: 5px; + border: 1px solid #f1f1f1; + box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); +} +.xtable-scrollbar ::-webkit-scrollbar-thumb:hover { + background-color: #a8a8a8; +} +.xtable-scrollbar ::-webkit-scrollbar-thumb:active { + background-color: #787878; +} +/*边角,即两个滚动条的交汇处*/ +.xtable-scrollbar ::-webkit-scrollbar-corner { + background-color: #ffffff; +} +/*移除 vxe-grid 右侧外框*/ +.vxe-button.size--medium.type--button.is--circle { + border: 0; +} diff --git a/src/types/auto-components.d.ts b/src/types/auto-components.d.ts new file mode 100644 index 00000000..d4adc459 --- /dev/null +++ b/src/types/auto-components.d.ts @@ -0,0 +1,119 @@ +// generated by unplugin-vue-components +// We suggest you to commit this file into source control +// Read more: https://github.com/vuejs/core/pull/3399 +import '@vue/runtime-core' + +export {} + +declare module '@vue/runtime-core' { + export interface GlobalComponents { + Backtop: typeof import('./../components/Backtop/src/Backtop.vue')['default'] + ConfigGlobal: typeof import('./../components/ConfigGlobal/src/ConfigGlobal.vue')['default'] + ContentDetailWrap: typeof import('./../components/ContentDetailWrap/src/ContentDetailWrap.vue')['default'] + ContentWrap: typeof import('./../components/ContentWrap/src/ContentWrap.vue')['default'] + CopperModal: typeof import('./../components/Cropper/src/CopperModal.vue')['default'] + CountTo: typeof import('./../components/CountTo/src/CountTo.vue')['default'] + Crontab: typeof import('./../components/Crontab/src/Crontab.vue')['default'] + Cropper: typeof import('./../components/Cropper/src/Cropper.vue')['default'] + CropperAvatar: typeof import('./../components/Cropper/src/CropperAvatar.vue')['default'] + Descriptions: typeof import('./../components/Descriptions/src/Descriptions.vue')['default'] + Dialog: typeof import('./../components/Dialog/src/Dialog.vue')['default'] + DictTag: typeof import('./../components/DictTag/src/DictTag.vue')['default'] + Echart: typeof import('./../components/Echart/src/Echart.vue')['default'] + Editor: typeof import('./../components/Editor/src/Editor.vue')['default'] + ElAvatar: typeof import('element-plus/es')['ElAvatar'] + ElBadge: typeof import('element-plus/es')['ElBadge'] + ElButton: typeof import('element-plus/es')['ElButton'] + ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup'] + ElCard: typeof import('element-plus/es')['ElCard'] + ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] + ElCol: typeof import('element-plus/es')['ElCol'] + ElCollapse: typeof import('element-plus/es')['ElCollapse'] + ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] + ElCollapseTransition: typeof import('element-plus/es')['ElCollapseTransition'] + ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] + ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] + ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] + ElDialog: typeof import('element-plus/es')['ElDialog'] + ElDivider: typeof import('element-plus/es')['ElDivider'] + ElDrawer: typeof import('element-plus/es')['ElDrawer'] + ElDropdown: typeof import('element-plus/es')['ElDropdown'] + ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] + ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] + ElementBaseInfo: typeof import('./../components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue')['default'] + ElementForm: typeof import('./../components/bpmnProcessDesigner/package/penal/form/ElementForm.vue')['default'] + ElementListeners: typeof import('./../components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue')['default'] + ElementMultiInstance: typeof import('./../components/bpmnProcessDesigner/package/penal/multi-instance/ElementMultiInstance.vue')['default'] + ElementOtherConfig: typeof import('./../components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue')['default'] + ElementProperties: typeof import('./../components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue')['default'] + ElementTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/ElementTask.vue')['default'] + ElForm: typeof import('element-plus/es')['ElForm'] + ElFormItem: typeof import('element-plus/es')['ElFormItem'] + ElIcon: typeof import('element-plus/es')['ElIcon'] + ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] + ElInput: typeof import('element-plus/es')['ElInput'] + ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] + ElLink: typeof import('element-plus/es')['ElLink'] + ElOption: typeof import('element-plus/es')['ElOption'] + ElPagination: typeof import('element-plus/es')['ElPagination'] + ElPopover: typeof import('element-plus/es')['ElPopover'] + ElRadio: typeof import('element-plus/es')['ElRadio'] + ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] + ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] + ElRow: typeof import('element-plus/es')['ElRow'] + ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] + ElSelect: typeof import('element-plus/es')['ElSelect'] + ElSkeleton: typeof import('element-plus/es')['ElSkeleton'] + ElSpace: typeof import('element-plus/es')['ElSpace'] + ElSwitch: typeof import('element-plus/es')['ElSwitch'] + ElTable: typeof import('element-plus/es')['ElTable'] + ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] + ElTabPane: typeof import('element-plus/es')['ElTabPane'] + ElTabs: typeof import('element-plus/es')['ElTabs'] + ElTag: typeof import('element-plus/es')['ElTag'] + ElTooltip: typeof import('element-plus/es')['ElTooltip'] + ElTransfer: typeof import('element-plus/es')['ElTransfer'] + ElTree: typeof import('element-plus/es')['ElTree'] + ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect'] + ElUpload: typeof import('element-plus/es')['ElUpload'] + Error: typeof import('./../components/Error/src/Error.vue')['default'] + FlowCondition: typeof import('./../components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue')['default'] + Form: typeof import('./../components/Form/src/Form.vue')['default'] + Highlight: typeof import('./../components/Highlight/src/Highlight.vue')['default'] + Icon: typeof import('./../components/Icon/src/Icon.vue')['default'] + IconSelect: typeof import('./../components/Icon/src/IconSelect.vue')['default'] + IFrame: typeof import('./../components/IFrame/src/IFrame.vue')['default'] + ImageViewer: typeof import('./../components/ImageViewer/src/ImageViewer.vue')['default'] + Infotip: typeof import('./../components/Infotip/src/Infotip.vue')['default'] + InputPassword: typeof import('./../components/InputPassword/src/InputPassword.vue')['default'] + ProcessDesigner: typeof import('./../components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue')['default'] + ProcessPalette: typeof import('./../components/bpmnProcessDesigner/package/palette/ProcessPalette.vue')['default'] + ProcessViewer: typeof import('./../components/bpmnProcessDesigner/package/designer/ProcessViewer.vue')['default'] + PropertiesPanel: typeof import('./../components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue')['default'] + Qrcode: typeof import('./../components/Qrcode/src/Qrcode.vue')['default'] + ReceiveTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue')['default'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] + ScriptTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue')['default'] + Search: typeof import('./../components/Search/src/Search.vue')['default'] + SignalAndMessage: typeof import('./../components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue')['default'] + Sticky: typeof import('./../components/Sticky/src/Sticky.vue')['default'] + Table: typeof import('./../components/Table/src/Table.vue')['default'] + Tooltip: typeof import('./../components/Tooltip/src/Tooltip.vue')['default'] + UploadFile: typeof import('./../components/UploadFile/src/UploadFile.vue')['default'] + UploadImg: typeof import('./../components/UploadFile/src/UploadImg.vue')['default'] + UploadImgs: typeof import('./../components/UploadFile/src/UploadImgs.vue')['default'] + UserTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue')['default'] + UserTaskListeners: typeof import('./../components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue')['default'] + Verify: typeof import('./../components/Verifition/src/Verify.vue')['default'] + VerifyPoints: typeof import('./../components/Verifition/src/Verify/VerifyPoints.vue')['default'] + VerifySlide: typeof import('./../components/Verifition/src/Verify/VerifySlide.vue')['default'] + XButton: typeof import('./../components/XButton/src/XButton.vue')['default'] + XModal: typeof import('./../components/XModal/src/XModal.vue')['default'] + XTable: typeof import('./../components/XTable/src/XTable.vue')['default'] + XTextButton: typeof import('./../components/XButton/src/XTextButton.vue')['default'] + } + export interface ComponentCustomProperties { + vLoading: typeof import('element-plus/es')['ElLoadingDirective'] + } +} diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts new file mode 100644 index 00000000..68e0f7a4 --- /dev/null +++ b/src/types/auto-imports.d.ts @@ -0,0 +1,67 @@ +// Generated by 'unplugin-auto-import' +export {} +declare global { + const DICT_TYPE: typeof import('@/utils/dict')['DICT_TYPE'] + const EffectScope: typeof import('vue')['EffectScope'] + const computed: typeof import('vue')['computed'] + const createApp: typeof import('vue')['createApp'] + const customRef: typeof import('vue')['customRef'] + const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] + const defineComponent: typeof import('vue')['defineComponent'] + const effectScope: typeof import('vue')['effectScope'] + const getCurrentInstance: typeof import('vue')['getCurrentInstance'] + const getCurrentScope: typeof import('vue')['getCurrentScope'] + const h: typeof import('vue')['h'] + const inject: typeof import('vue')['inject'] + const isProxy: typeof import('vue')['isProxy'] + const isReactive: typeof import('vue')['isReactive'] + const isReadonly: typeof import('vue')['isReadonly'] + const isRef: typeof import('vue')['isRef'] + const markRaw: typeof import('vue')['markRaw'] + const nextTick: typeof import('vue')['nextTick'] + const onActivated: typeof import('vue')['onActivated'] + const onBeforeMount: typeof import('vue')['onBeforeMount'] + const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] + const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] + const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] + const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] + const onDeactivated: typeof import('vue')['onDeactivated'] + const onErrorCaptured: typeof import('vue')['onErrorCaptured'] + const onMounted: typeof import('vue')['onMounted'] + const onRenderTracked: typeof import('vue')['onRenderTracked'] + const onRenderTriggered: typeof import('vue')['onRenderTriggered'] + const onScopeDispose: typeof import('vue')['onScopeDispose'] + const onServerPrefetch: typeof import('vue')['onServerPrefetch'] + const onUnmounted: typeof import('vue')['onUnmounted'] + const onUpdated: typeof import('vue')['onUpdated'] + const provide: typeof import('vue')['provide'] + const reactive: typeof import('vue')['reactive'] + const readonly: typeof import('vue')['readonly'] + const ref: typeof import('vue')['ref'] + const required: typeof import('@/utils/formRules')['required'] + const resolveComponent: typeof import('vue')['resolveComponent'] + const resolveDirective: typeof import('vue')['resolveDirective'] + const shallowReactive: typeof import('vue')['shallowReactive'] + const shallowReadonly: typeof import('vue')['shallowReadonly'] + const shallowRef: typeof import('vue')['shallowRef'] + const toRaw: typeof import('vue')['toRaw'] + const toRef: typeof import('vue')['toRef'] + const toRefs: typeof import('vue')['toRefs'] + const triggerRef: typeof import('vue')['triggerRef'] + const unref: typeof import('vue')['unref'] + const useAttrs: typeof import('vue')['useAttrs'] + const useCssModule: typeof import('vue')['useCssModule'] + const useCssVars: typeof import('vue')['useCssVars'] + const useI18n: typeof import('@/hooks/web/useI18n')['useI18n'] + const useLink: typeof import('vue-router')['useLink'] + const useMessage: typeof import('@/hooks/web/useMessage')['useMessage'] + const useRoute: typeof import('vue-router')['useRoute'] + const useRouter: typeof import('vue-router')['useRouter'] + const useSlots: typeof import('vue')['useSlots'] + const useVxeCrudSchemas: typeof import('@/hooks/web/useVxeCrudSchemas')['useVxeCrudSchemas'] + const useXTable: typeof import('@/hooks/web/useXTable')['useXTable'] + const watch: typeof import('vue')['watch'] + const watchEffect: typeof import('vue')['watchEffect'] + const watchPostEffect: typeof import('vue')['watchPostEffect'] + const watchSyncEffect: typeof import('vue')['watchSyncEffect'] +} diff --git a/src/types/components.d.ts b/src/types/components.d.ts new file mode 100644 index 00000000..8de1f335 --- /dev/null +++ b/src/types/components.d.ts @@ -0,0 +1,56 @@ +export type ComponentName = + | 'Radio' + | 'RadioButton' + | 'Checkbox' + | 'CheckboxButton' + | 'Input' + | 'Autocomplete' + | 'InputNumber' + | 'Select' + | 'Cascader' + | 'Switch' + | 'Slider' + | 'TimePicker' + | 'DatePicker' + | 'Rate' + | 'ColorPicker' + | 'Transfer' + | 'Divider' + | 'TimeSelect' + | 'SelectV2' + | 'TreeSelect' + | 'InputPassword' + | 'Editor' + | 'UploadImg' + | 'UploadImgs' + | 'UploadFile' + +export type ColProps = { + span?: number + xs?: number + sm?: number + md?: number + lg?: number + xl?: number + tag?: string +} + +export type ComponentOptions = { + label?: string + value?: FormValueType + disabled?: boolean + key?: string | number + children?: ComponentOptions[] + options?: ComponentOptions[] +} & Recordable + +export type ComponentOptionsAlias = { + labelField?: string + valueField?: string +} + +export type ComponentProps = { + optionsAlias?: ComponentOptionsAlias + options?: ComponentOptions[] + optionsSlot?: boolean +} & Recordable diff --git a/src/types/configGlobal.d.ts b/src/types/configGlobal.d.ts new file mode 100644 index 00000000..f6d7b3c3 --- /dev/null +++ b/src/types/configGlobal.d.ts @@ -0,0 +1,4 @@ +import { ElementPlusSize } from './elementPlus' +export interface ConfigGlobalTypes { + size?: ElementPlusSize +} diff --git a/src/types/contextMenu.d.ts b/src/types/contextMenu.d.ts new file mode 100644 index 00000000..0738d0e3 --- /dev/null +++ b/src/types/contextMenu.d.ts @@ -0,0 +1,7 @@ +export type contextMenuSchema = { + disabled?: boolean + divided?: boolean + icon?: string + label: string + command?: (item: contextMenuSchema) => void +} diff --git a/src/types/descriptions.d.ts b/src/types/descriptions.d.ts new file mode 100644 index 00000000..48835e9c --- /dev/null +++ b/src/types/descriptions.d.ts @@ -0,0 +1,13 @@ +export interface DescriptionsSchema { + span?: number // 占多少分 + field: string // 字段名 + label?: string // label名 + width?: string | number + minWidth?: string | number + align?: 'left' | 'center' | 'right' + labelAlign?: 'left' | 'center' | 'right' + className?: string + labelClassName?: string + dateFormat?: string + dictType?: string +} diff --git a/src/types/elementPlus.d.ts b/src/types/elementPlus.d.ts new file mode 100644 index 00000000..2c6b76e7 --- /dev/null +++ b/src/types/elementPlus.d.ts @@ -0,0 +1,3 @@ +export type ElementPlusSize = 'default' | 'small' | 'large' + +export type ElementPlusInfoType = 'success' | 'info' | 'warning' | 'danger' diff --git a/src/types/form.d.ts b/src/types/form.d.ts new file mode 100644 index 00000000..980c8cc6 --- /dev/null +++ b/src/types/form.d.ts @@ -0,0 +1,44 @@ +import type { CSSProperties } from 'vue' +import { ColProps, ComponentProps, ComponentName } from '@/types/components' +import type { AxiosPromise } from 'axios' + +export type FormSetPropsType = { + field: string + path: string + value: any +} + +export type FormValueType = string | number | string[] | number[] | boolean | undefined | null + +export type FormItemProps = { + labelWidth?: string | number + required?: boolean + rules?: Recordable + error?: string + showMessage?: boolean + inlineMessage?: boolean + style?: CSSProperties +} + +export type FormSchema = { + // 唯一值 + field: string + // 标题 + label?: string + // 提示 + labelMessage?: string + // col组件属性 + colProps?: ColProps + // 表单组件属性,slots对应的是表单组件的插槽,规则:${field}-xxx,具体可以查看element-plus文档 + componentProps?: { slots?: Recordable } & ComponentProps + // formItem组件属性 + formItemProps?: FormItemProps + // 渲染的组件 + component?: ComponentName + // 初始值 + value?: FormValueType + // 是否隐藏 + hidden?: boolean + // 远程加载下拉项 + api?: () => AxiosPromise +} diff --git a/src/types/icon.d.ts b/src/types/icon.d.ts new file mode 100644 index 00000000..d1ffcdb5 --- /dev/null +++ b/src/types/icon.d.ts @@ -0,0 +1,5 @@ +export interface IconTypes { + size?: number + color?: string + icon: string +} diff --git a/src/types/infoTip.d.ts b/src/types/infoTip.d.ts new file mode 100644 index 00000000..6eff083d --- /dev/null +++ b/src/types/infoTip.d.ts @@ -0,0 +1,4 @@ +export interface TipSchema { + label: string + keys?: string[] +} diff --git a/src/types/layout.d.ts b/src/types/layout.d.ts new file mode 100644 index 00000000..cad3e2af --- /dev/null +++ b/src/types/layout.d.ts @@ -0,0 +1 @@ +export type LayoutType = 'classic' | 'topLeft' | 'top' | 'cutMenu' diff --git a/src/types/localeDropdown.d.ts b/src/types/localeDropdown.d.ts new file mode 100644 index 00000000..c749dce7 --- /dev/null +++ b/src/types/localeDropdown.d.ts @@ -0,0 +1,10 @@ +export interface Language { + el: Recordable + name: string +} + +export interface LocaleDropdownType { + lang: LocaleType + name?: string + elLocale?: Language +} diff --git a/src/types/qrcode.d.ts b/src/types/qrcode.d.ts new file mode 100644 index 00000000..86cdf0b9 --- /dev/null +++ b/src/types/qrcode.d.ts @@ -0,0 +1,9 @@ +export interface QrcodeLogo { + src?: string + logoSize?: number + bgColor?: string + borderSize?: number + crossOrigin?: string + borderRadius?: number + logoRadius?: number +} diff --git a/src/types/table.d.ts b/src/types/table.d.ts new file mode 100644 index 00000000..3294234b --- /dev/null +++ b/src/types/table.d.ts @@ -0,0 +1,42 @@ +export type TableColumn = { + field: string + label?: string + children?: TableColumn[] +} & Recordable + +export type VxeTableColumn = { + field: string + title?: string + children?: TableColumn[] +} & Recordable + +export type TableSlotDefault = { + row: Recordable + column: TableColumn + $index: number +} & Recordable + +export interface Pagination { + small?: boolean + background?: boolean + pageSize?: number + defaultPageSize?: number + total?: number + pageCount?: number + pagerCount?: number + currentPage?: number + defaultCurrentPage?: number + layout?: string + pageSizes?: number[] + popperClass?: string + prevText?: string + nextText?: string + disabled?: boolean + hideOnSinglePage?: boolean +} + +export interface TableSetPropsType { + field: string + path: string + value: any +} diff --git a/src/types/theme.d.ts b/src/types/theme.d.ts new file mode 100644 index 00000000..ad649b02 --- /dev/null +++ b/src/types/theme.d.ts @@ -0,0 +1,16 @@ +export type ThemeTypes = { + elColorPrimary?: string + leftMenuBorderColor?: string + leftMenuBgColor?: string + leftMenuBgLightColor?: string + leftMenuBgActiveColor?: string + leftMenuCollapseBgActiveColor?: string + leftMenuTextColor?: string + leftMenuTextActiveColor?: string + logoTitleTextColor?: string + logoBorderColor?: string + topHeaderBgColor?: string + topHeaderTextColor?: string + topHeaderHoverColor?: string + topToolBorderColor?: string +} diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts new file mode 100644 index 00000000..ca58df21 --- /dev/null +++ b/src/utils/Logger.ts @@ -0,0 +1,100 @@ +const isArray = function (obj: any): boolean { + return Object.prototype.toString.call(obj) === '[object Array]' +} + +const Logger = () => {} + +Logger.typeColor = function (type: string) { + let color = '' + switch (type) { + case 'primary': + color = '#2d8cf0' + break + case 'success': + color = '#19be6b' + break + case 'info': + color = '#909399' + break + case 'warn': + color = '#ff9900' + break + case 'error': + color = '#f03f14' + break + default: + color = '#35495E' + break + } + return color +} + +Logger.print = function (type = 'default', text: any, back = false) { + if (typeof text === 'object') { + // 如果是對象則調用打印對象方式 + isArray(text) ? console.table(text) : console.dir(text) + return + } + if (back) { + // 如果是打印帶背景圖的 + console.log( + `%c ${text} `, + `background:${Logger.typeColor(type)}; padding: 2px; border-radius: 4px; color: #fff;` + ) + } else { + console.log( + `%c ${text} `, + `border: 1px solid ${Logger.typeColor(type)}; + padding: 2px; border-radius: 4px; + color: ${Logger.typeColor(type)};` + ) + } +} + +Logger.printBack = function (type = 'primary', text) { + this.print(type, text, true) +} + +Logger.pretty = function (type = 'primary', title, text) { + if (typeof text === 'object') { + console.group('Console Group', title) + console.log( + `%c ${title}`, + `background:${Logger.typeColor(type)};border:1px solid ${Logger.typeColor(type)}; + padding: 1px; border-radius: 4px; color: #fff;` + ) + isArray(text) ? console.table(text) : console.dir(text) + console.groupEnd() + return + } + console.log( + `%c ${title} %c ${text} %c`, + `background:${Logger.typeColor(type)};border:1px solid ${Logger.typeColor(type)}; + padding: 1px; border-radius: 4px 0 0 4px; color: #fff;`, + `border:1px solid ${Logger.typeColor(type)}; + padding: 1px; border-radius: 0 4px 4px 0; color: ${Logger.typeColor(type)};`, + 'background:transparent' + ) +} + +Logger.prettyPrimary = function (title, ...text) { + text.forEach((t) => this.pretty('primary', title, t)) +} + +Logger.prettySuccess = function (title, ...text) { + text.forEach((t) => this.pretty('success', title, t)) +} + +Logger.prettyWarn = function (title, ...text) { + text.forEach((t) => this.pretty('warn', title, t)) +} + +Logger.prettyError = function (title, ...text) { + text.forEach((t) => this.pretty('error', title, t)) +} + +Logger.prettyInfo = function (title, ...text) { + text.forEach((t) => this.pretty('info', title, t)) +} + +export default Logger diff --git a/src/utils/auth.ts b/src/utils/auth.ts new file mode 100644 index 00000000..b55d676d --- /dev/null +++ b/src/utils/auth.ts @@ -0,0 +1,92 @@ +import { useCache } from '@/hooks/web/useCache' +import { TokenType } from '@/api/login/types' +import { decrypt, encrypt } from '@/utils/jsencrypt' + +const { wsCache } = useCache() + +const AccessTokenKey = 'ACCESS_TOKEN' +const RefreshTokenKey = 'REFRESH_TOKEN' + +// 获取token +export const getAccessToken = () => { + // 此处与TokenKey相同,此写法解决初始化时Cookies中不存在TokenKey报错 + return wsCache.get(AccessTokenKey) ? wsCache.get(AccessTokenKey) : wsCache.get('ACCESS_TOKEN') +} + +// 刷新token +export const getRefreshToken = () => { + return wsCache.get(RefreshTokenKey) +} + +// 设置token +export const setToken = (token: TokenType) => { + wsCache.set(RefreshTokenKey, token.refreshToken, { exp: token.expiresTime }) + wsCache.set(AccessTokenKey, token.accessToken) +} + +// 删除token +export const removeToken = () => { + wsCache.delete(AccessTokenKey) + wsCache.delete(RefreshTokenKey) +} + +/** 格式化token(jwt格式) */ +export const formatToken = (token: string): string => { + return 'Bearer ' + token +} +// ========== 账号相关 ========== + +const LoginFormKey = 'LOGINFORM' + +export type LoginFormType = { + tenantName: string + username: string + password: string + rememberMe: boolean +} + +export const getLoginForm = () => { + const loginForm: LoginFormType = wsCache.get(LoginFormKey) + if (loginForm) { + loginForm.password = decrypt(loginForm.password) as string + } + return loginForm +} + +export const setLoginForm = (loginForm: LoginFormType) => { + loginForm.password = encrypt(loginForm.password) as string + wsCache.set(LoginFormKey, loginForm, { exp: 30 * 24 * 60 * 60 }) +} + +export const removeLoginForm = () => { + wsCache.delete(LoginFormKey) +} + +// ========== 租户相关 ========== + +const TenantIdKey = 'TENANT_ID' +const TenantNameKey = 'TENANT_NAME' + +export const getTenantName = () => { + return wsCache.get(TenantNameKey) +} + +export const setTenantName = (username: string) => { + wsCache.set(TenantNameKey, username, { exp: 30 * 24 * 60 * 60 }) +} + +export const removeTenantName = () => { + wsCache.delete(TenantNameKey) +} + +export const getTenantId = () => { + return wsCache.get(TenantIdKey) +} + +export const setTenantId = (username: string) => { + wsCache.set(TenantIdKey, username) +} + +export const removeTenantId = () => { + wsCache.delete(TenantIdKey) +} diff --git a/src/utils/color.ts b/src/utils/color.ts new file mode 100644 index 00000000..6888583a --- /dev/null +++ b/src/utils/color.ts @@ -0,0 +1,153 @@ +/** + * 判断是否 十六进制颜色值. + * 输入形式可为 #fff000 #f00 + * + * @param String color 十六进制颜色值 + * @return Boolean + */ +export const 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 const 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 const hexToRGB = (hex: string, opacity?: number) => { + 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 opacity + ? 'RGBA(' + sColorChange.join(',') + ',' + opacity + ')' + : 'RGB(' + sColorChange.join(',') + ')' + } + return sHex +} + +export const 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 const 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 const 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 + */ +const 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 + */ +const 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 + */ +const 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 const 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 + */ +const 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/constants.ts b/src/utils/constants.ts new file mode 100644 index 00000000..901c04e9 --- /dev/null +++ b/src/utils/constants.ts @@ -0,0 +1,218 @@ +/** + * Created by 芋道源码 + * + * 枚举类 + */ + +// 全局通用状态枚举 +export const CommonStatusEnum = { + ENABLE: 0, // 开启 + DISABLE: 1 // 禁用 +} + +/** + * 菜单的类型枚举 + */ +export const SystemMenuTypeEnum = { + DIR: 1, // 目录 + MENU: 2, // 菜单 + BUTTON: 3 // 按钮 +} + +/** + * 角色的类型枚举 + */ +export const SystemRoleTypeEnum = { + SYSTEM: 1, // 内置角色 + CUSTOM: 2 // 自定义角色 +} + +/** + * 数据权限的范围枚举 + */ +export const SystemDataScopeEnum = { + ALL: 1, // 全部数据权限 + DEPT_CUSTOM: 2, // 指定部门数据权限 + DEPT_ONLY: 3, // 部门数据权限 + DEPT_AND_CHILD: 4, // 部门及以下数据权限 + DEPT_SELF: 5 // 仅本人数据权限 +} + +/** + * 代码生成模板类型 + */ +export const InfraCodegenTemplateTypeEnum = { + CRUD: 1, // 基础 CRUD + TREE: 2, // 树形 CRUD + SUB: 3 // 主子表 CRUD +} + +/** + * 任务状态的枚举 + */ +export const InfraJobStatusEnum = { + INIT: 0, // 初始化中 + NORMAL: 1, // 运行中 + STOP: 2 // 暂停运行 +} + +/** + * API 异常数据的处理状态 + */ +export const InfraApiErrorLogProcessStatusEnum = { + INIT: 0, // 未处理 + DONE: 1, // 已处理 + IGNORE: 2 // 已忽略 +} + +/** + * 用户的社交平台的类型枚举 + */ +export const SystemUserSocialTypeEnum = { + DINGTALK: { + title: '钉钉', + type: 20, + source: 'dingtalk', + img: 'https://s1.ax1x.com/2022/05/22/OzMDRs.png' + }, + WECHAT_ENTERPRISE: { + title: '企业微信', + type: 30, + source: 'wechat_enterprise', + img: 'https://s1.ax1x.com/2022/05/22/OzMrzn.png' + } +} + +/** + * 支付渠道枚举 + */ +export const PayChannelEnum = { + WX_PUB: { + code: 'wx_pub', + name: '微信 JSAPI 支付' + }, + WX_LITE: { + code: 'wx_lite', + name: '微信小程序支付' + }, + WX_APP: { + code: 'wx_app', + name: '微信 APP 支付' + }, + ALIPAY_PC: { + code: 'alipay_pc', + name: '支付宝 PC 网站支付' + }, + ALIPAY_WAP: { + code: 'alipay_wap', + name: '支付宝 WAP 网站支付' + }, + ALIPAY_APP: { + code: 'alipay_app', + name: '支付宝 APP 支付' + }, + ALIPAY_QR: { + code: 'alipay_qr', + name: '支付宝扫码支付' + } +} + +/** + * 支付类型枚举 + */ +export const PayType = { + WECHAT: 'WECHAT', + ALIPAY: 'ALIPAY' +} + +/** + * 支付订单状态枚举 + */ +export const PayOrderStatusEnum = { + WAITING: { + status: 0, + name: '未支付' + }, + SUCCESS: { + status: 10, + name: '已支付' + }, + CLOSED: { + status: 20, + name: '未支付' + } +} + +/** + * 支付订单回调状态枚举 + */ +export const PayOrderNotifyStatusEnum = { + NO: { + status: 0, + name: '未通知' + }, + SUCCESS: { + status: 10, + name: '通知成功' + }, + FAILURE: { + status: 20, + name: '通知失败' + } +} + +/** + * 支付订单退款状态枚举 + */ +export const PayOrderRefundStatusEnum = { + NO: { + status: 0, + name: '未退款' + }, + SOME: { + status: 10, + name: '部分退款' + }, + ALL: { + status: 20, + name: '全部退款' + } +} + +/** + * 支付退款订单状态枚举 + */ +export const PayRefundStatusEnum = { + CREATE: { + status: 0, + name: '退款订单生成' + }, + SUCCESS: { + status: 1, + name: '退款成功' + }, + FAILURE: { + status: 2, + name: '退款失败' + }, + PROCESSING_NOTIFY: { + status: 3, + name: '退款中,渠道通知结果' + }, + PROCESSING_QUERY: { + status: 4, + name: '退款中,系统查询结果' + }, + UNKNOWN_RETRY: { + status: 5, + name: '状态未知,请重试' + }, + UNKNOWN_QUERY: { + status: 6, + name: '状态未知,系统查询结果' + }, + CLOSE: { + status: 99, + name: '退款关闭' + } +} diff --git a/src/utils/dict.ts b/src/utils/dict.ts new file mode 100644 index 00000000..b9dfa160 --- /dev/null +++ b/src/utils/dict.ts @@ -0,0 +1,127 @@ +/** + * 数据字典工具类 + */ +import { useDictStoreWithOut } from '@/store/modules/dict' +import { ElementPlusInfoType } from '@/types/elementPlus' + +const dictStore = useDictStoreWithOut() + +/** + * 获取 dictType 对应的数据字典数组 + * + * @param dictType 数据类型 + * @returns {*|Array} 数据字典数组 + */ +export interface DictDataType { + dictType: string + label: string + value: string | number | boolean + colorType: ElementPlusInfoType | '' + cssClass: string +} + +export const getDictOptions = (dictType: string) => { + return dictStore.getDictByType(dictType) +} + +export const getIntDictOptions = (dictType: string) => { + const dictOption: DictDataType[] = [] + const dictOptions: DictDataType[] = getDictOptions(dictType) + dictOptions.forEach((dict: DictDataType) => { + dictOption.push({ + ...dict, + value: parseInt(dict.value + '') + }) + }) + + return dictOption +} + +export const getStrDictOptions = (dictType: string) => { + const dictOption: DictDataType[] = [] + const dictOptions: DictDataType[] = getDictOptions(dictType) + dictOptions.forEach((dict: DictDataType) => { + dictOption.push({ + ...dict, + value: dict.value + '' + }) + }) + return dictOption +} + +export const getBoolDictOptions = (dictType: string) => { + const dictOption: DictDataType[] = [] + const dictOptions: DictDataType[] = getDictOptions(dictType) + dictOptions.forEach((dict: DictDataType) => { + dictOption.push({ + ...dict, + value: dict.value + '' === 'true' ? true : false + }) + }) + return dictOption +} + +export const getDictObj = (dictType: string, value: any) => { + const dictOptions: DictDataType[] = getDictOptions(dictType) + dictOptions.forEach((dict: DictDataType) => { + if (dict.value === value.toString()) { + return dict + } + }) +} + +export enum DICT_TYPE { + USER_TYPE = 'user_type', + COMMON_STATUS = 'common_status', + SYSTEM_TENANT_PACKAGE_ID = 'system_tenant_package_id', + + // ========== SYSTEM 模块 ========== + SYSTEM_USER_SEX = 'system_user_sex', + SYSTEM_MENU_TYPE = 'system_menu_type', + SYSTEM_ROLE_TYPE = 'system_role_type', + SYSTEM_DATA_SCOPE = 'system_data_scope', + SYSTEM_NOTICE_TYPE = 'system_notice_type', + SYSTEM_OPERATE_TYPE = 'system_operate_type', + SYSTEM_LOGIN_TYPE = 'system_login_type', + SYSTEM_LOGIN_RESULT = 'system_login_result', + SYSTEM_SMS_CHANNEL_CODE = 'system_sms_channel_code', + SYSTEM_SMS_TEMPLATE_TYPE = 'system_sms_template_type', + SYSTEM_SMS_SEND_STATUS = 'system_sms_send_status', + SYSTEM_SMS_RECEIVE_STATUS = 'system_sms_receive_status', + SYSTEM_ERROR_CODE_TYPE = 'system_error_code_type', + SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type', + SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status', + SYSTEM_NOTIFY_TEMPLATE_TYPE = 'system_notify_template_type', + + // ========== INFRA 模块 ========== + INFRA_BOOLEAN_STRING = 'infra_boolean_string', + INFRA_REDIS_TIMEOUT_TYPE = 'infra_redis_timeout_type', + INFRA_JOB_STATUS = 'infra_job_status', + INFRA_JOB_LOG_STATUS = 'infra_job_log_status', + INFRA_API_ERROR_LOG_PROCESS_STATUS = 'infra_api_error_log_process_status', + INFRA_CONFIG_TYPE = 'infra_config_type', + INFRA_CODEGEN_TEMPLATE_TYPE = 'infra_codegen_template_type', + INFRA_CODEGEN_SCENE = 'infra_codegen_scene', + INFRA_FILE_STORAGE = 'infra_file_storage', + + // ========== BPM 模块 ========== + BPM_MODEL_CATEGORY = 'bpm_model_category', + BPM_MODEL_FORM_TYPE = 'bpm_model_form_type', + BPM_TASK_ASSIGN_RULE_TYPE = 'bpm_task_assign_rule_type', + BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status', + BPM_PROCESS_INSTANCE_RESULT = 'bpm_process_instance_result', + BPM_TASK_ASSIGN_SCRIPT = 'bpm_task_assign_script', + BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type', + + // ========== PAY 模块 ========== + PAY_CHANNEL_WECHAT_VERSION = 'pay_channel_wechat_version', // 微信渠道版本 + PAY_CHANNEL_ALIPAY_SIGN_TYPE = 'pay_channel_alipay_sign_type', // 支付渠道支付宝算法类型 + PAY_CHANNEL_ALIPAY_MODE = 'pay_channel_alipay_mode', // 支付宝公钥类型 + PAY_CHANNEL_ALIPAY_SERVER_TYPE = 'pay_channel_alipay_server_type', // 支付宝网关地址 + PAY_CHANNEL_CODE_TYPE = 'pay_channel_code_type', // 支付渠道编码类型 + PAY_ORDER_NOTIFY_STATUS = 'pay_order_notify_status', // 商户支付订单回调状态 + PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态 + PAY_ORDER_REFUND_STATUS = 'pay_order_refund_status', // 商户支付订单退款状态 + PAY_REFUND_ORDER_STATUS = 'pay_refund_order_status', // 退款订单状态 + PAY_REFUND_ORDER_TYPE = 'pay_refund_order_type' // 退款订单类别 +} diff --git a/src/utils/domUtils.ts b/src/utils/domUtils.ts new file mode 100644 index 00000000..dbc1989c --- /dev/null +++ b/src/utils/domUtils.ts @@ -0,0 +1,289 @@ +import { isServer } from './is' +const ieVersion = isServer ? 0 : Number((document as any).documentMode) +const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g +const MOZ_HACK_REGEXP = /^moz([A-Z])/ + +export interface ViewportOffsetResult { + left: number + top: number + right: number + bottom: number + rightIncludeBody: number + bottomIncludeBody: number +} + +/* istanbul ignore next */ +const trim = function (string: string) { + return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '') +} + +/* istanbul ignore next */ +const camelCase = function (name: string) { + return name + .replace(SPECIAL_CHARS_REGEXP, function (_, __, letter, offset) { + return offset ? letter.toUpperCase() : letter + }) + .replace(MOZ_HACK_REGEXP, 'Moz$1') +} + +/* 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) + } +} + +export function getBoundingClientRect(element: Element): DOMRect | number { + if (!element || !element.getBoundingClientRect) { + return 0 + } + return element.getBoundingClientRect() +} + +/** + * 获取当前元素的left、top偏移 + * left:元素最左侧距离文档左侧的距离 + * top:元素最顶端距离文档顶端的距离 + * right:元素最右侧距离文档右侧的距离 + * bottom:元素最底端距离文档底端的距离 + * rightIncludeBody:元素最左侧距离文档右侧的距离 + * bottomIncludeBody:元素最底端距离文档最底部的距离 + * + * @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 + } +} + +/* istanbul ignore next */ +export const on = function ( + element: HTMLElement | Document | Window, + event: string, + handler: EventListenerOrEventListenerObject +): void { + if (element && event && handler) { + element.addEventListener(event, handler, false) + } +} + +/* istanbul ignore next */ +export const off = function ( + element: HTMLElement | Document | Window, + event: string, + handler: any +): void { + if (element && event && handler) { + element.removeEventListener(event, handler, false) + } +} + +/* istanbul ignore next */ +export const once = function (el: HTMLElement, event: string, fn: EventListener): void { + const listener = function (this: any, ...args: unknown[]) { + if (fn) { + // @ts-ignore + fn.apply(this, args) + } + off(el, event, listener) + } + on(el, event, listener) +} + +/* istanbul ignore next */ +export const getStyle = + ieVersion < 9 + ? function (element: Element | any, styleName: string) { + if (isServer) return + if (!element || !styleName) return null + styleName = camelCase(styleName) + if (styleName === 'float') { + styleName = 'styleFloat' + } + try { + switch (styleName) { + case 'opacity': + try { + return element.filters.item('alpha').opacity / 100 + } catch (e) { + return 1.0 + } + default: + return element.style[styleName] || element.currentStyle + ? element.currentStyle[styleName] + : null + } + } catch (e) { + return element.style[styleName] + } + } + : function (element: Element | any, styleName: string) { + if (isServer) return + if (!element || !styleName) return null + styleName = camelCase(styleName) + if (styleName === 'float') { + styleName = 'cssFloat' + } + try { + const computed = (document as any).defaultView.getComputedStyle(element, '') + return element.style[styleName] || computed ? computed[styleName] : null + } catch (e) { + return element.style[styleName] + } + } + +/* istanbul ignore next */ +export function setStyle(element: Element | any, styleName: any, value: any) { + if (!element || !styleName) return + + if (typeof styleName === 'object') { + for (const prop in styleName) { + if (Object.prototype.hasOwnProperty.call(styleName, prop)) { + setStyle(element, prop, styleName[prop]) + } + } + } else { + styleName = camelCase(styleName) + if (styleName === 'opacity' && ieVersion < 9) { + element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')' + } else { + element.style[styleName] = value + } + } +} + +/* istanbul ignore next */ +export const isScroll = (el: Element, vertical: any) => { + if (isServer) return + + const determinedDirection = vertical !== null || vertical !== undefined + const overflow = determinedDirection + ? vertical + ? getStyle(el, 'overflow-y') + : getStyle(el, 'overflow-x') + : getStyle(el, 'overflow') + + return overflow.match(/(scroll|auto)/) +} + +/* istanbul ignore next */ +export const getScrollContainer = (el: Element, vertical?: any) => { + if (isServer) return + + let parent: any = el + while (parent) { + if ([window, document, document.documentElement].includes(parent)) { + return window + } + if (isScroll(parent, vertical)) { + return parent + } + parent = parent.parentNode + } + + return parent +} + +/* istanbul ignore next */ +export const isInContainer = (el: Element, container: any) => { + if (isServer || !el || !container) return false + + const elRect = el.getBoundingClientRect() + let containerRect + + if ([window, document, document.documentElement, null, undefined].includes(container)) { + containerRect = { + top: 0, + right: window.innerWidth, + bottom: window.innerHeight, + left: 0 + } + } else { + containerRect = container.getBoundingClientRect() + } + + return ( + elRect.top < containerRect.bottom && + elRect.bottom > containerRect.top && + elRect.right > containerRect.left && + elRect.left < containerRect.right + ) +} diff --git a/src/utils/download.ts b/src/utils/download.ts new file mode 100644 index 00000000..ab200149 --- /dev/null +++ b/src/utils/download.ts @@ -0,0 +1,38 @@ +const download0 = (data: Blob, fileName: string, mineType: string) => { + // 创建 blob + const blob = new Blob([data], { type: mineType }) + // 创建 href 超链接,点击进行下载 + window.URL = window.URL || window.webkitURL + const href = URL.createObjectURL(blob) + const downA = document.createElement('a') + downA.href = href + downA.download = fileName + downA.click() + // 销毁超连接 + window.URL.revokeObjectURL(href) +} + +const download = { + // 下载 Excel 方法 + excel: (data: Blob, fileName: string) => { + download0(data, fileName, 'application/vnd.ms-excel') + }, + // 下载 Word 方法 + word: (data: Blob, fileName: string) => { + download0(data, fileName, 'application/msword') + }, + // 下载 Zip 方法 + zip: (data: Blob, fileName: string) => { + download0(data, fileName, 'application/zip') + }, + // 下载 Html 方法 + html: (data: Blob, fileName: string) => { + download0(data, fileName, 'text/html') + }, + // 下载 Markdown 方法 + markdown: (data: Blob, fileName: string) => { + download0(data, fileName, 'text/markdown') + } +} + +export default download diff --git a/src/utils/env.ts b/src/utils/env.ts new file mode 100644 index 00000000..4a45f622 --- /dev/null +++ b/src/utils/env.ts @@ -0,0 +1,8 @@ +export const isDevMode = () => { + const dev = import.meta.env.VITE_DEV + if (dev && dev === true) { + return true + } else { + return false + } +} diff --git a/src/utils/filt.ts b/src/utils/filt.ts new file mode 100644 index 00000000..b1a7b2c7 --- /dev/null +++ b/src/utils/filt.ts @@ -0,0 +1,157 @@ +export const openWindow = ( + url: string, + opt?: { + target?: '_self' | '_blank' | 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(',')) +} + +/** + * @description: base64 to blob + */ +export const 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 const 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 + }) +} + +/** + * Download online pictures + * @param url + * @param filename + * @param mime + * @param bom + */ +export const 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 const 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 const 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 const downloadByUrl = ({ + url, + target = '_blank', + fileName +}: { + url: string + target?: '_self' | '_blank' + 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/formCreate.ts b/src/utils/formCreate.ts new file mode 100644 index 00000000..6d7dbc7f --- /dev/null +++ b/src/utils/formCreate.ts @@ -0,0 +1,54 @@ +/** + * 针对 https://github.com/xaboy/form-create-designer 封装的工具类 + */ + +// 编码表单 Conf +export const encodeConf = (designerRef: object) => { + // @ts-ignore + return JSON.stringify(designerRef.value.getOption()) +} + +// 编码表单 Fields +export const encodeFields = (designerRef: object) => { + // @ts-ignore + const rule = designerRef.value.getRule() + const fields: string[] = [] + rule.forEach((item) => { + fields.push(JSON.stringify(item)) + }) + return fields +} + +// 解码表单 Fields +export const decodeFields = (fields: string[]) => { + const rule: object[] = [] + fields.forEach((item) => { + rule.push(JSON.parse(item)) + }) + return rule +} + +// 设置表单的 Conf 和 Fields +export const setConfAndFields = (designerRef: object, conf: string, fields: string) => { + // @ts-ignore + designerRef.value.setOption(JSON.parse(conf)) + // @ts-ignore + designerRef.value.setRule(decodeFields(fields)) +} + +// 设置表单的 Conf 和 Fields +export const setConfAndFields2 = ( + detailPreview: object, + conf: string, + fields: string, + value?: object +) => { + // @ts-ignore + detailPreview.value.option = JSON.parse(conf) + // @ts-ignore + detailPreview.value.rule = decodeFields(fields) + if (value) { + // @ts-ignore + detailPreview.value.value = value + } +} diff --git a/src/utils/formRules.ts b/src/utils/formRules.ts new file mode 100644 index 00000000..2989867f --- /dev/null +++ b/src/utils/formRules.ts @@ -0,0 +1,7 @@ +const { t } = useI18n() + +// 必填项 +export const required = { + required: true, + message: t('common.required') +} diff --git a/src/utils/formatTime.ts b/src/utils/formatTime.ts new file mode 100644 index 00000000..9b0e4465 --- /dev/null +++ b/src/utils/formatTime.ts @@ -0,0 +1,176 @@ +/** + * 时间日期转换 + * @param date 当前时间,new Date() 格式 + * @param format 需要转换的时间格式字符串 + * @description format 字符串随意,如 `YYYY-mm、YYYY-mm-dd` + * @description format 季度:"YYYY-mm-dd HH:MM:SS QQQQ" + * @description format 星期:"YYYY-mm-dd HH:MM:SS WWW" + * @description format 几周:"YYYY-mm-dd HH:MM:SS ZZZ" + * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ" + * @returns 返回拼接后的时间字符串 + */ +export function formatDate(date: Date, format: string): string { + const we = date.getDay() // 星期 + const z = getWeek(date) // 周 + const qut = Math.floor((date.getMonth() + 3) / 3).toString() // 季度 + const opt: { [key: string]: string } = { + 'Y+': date.getFullYear().toString(), // 年 + 'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1) + 'd+': date.getDate().toString(), // 日 + 'H+': date.getHours().toString(), // 时 + 'M+': date.getMinutes().toString(), // 分 + 'S+': date.getSeconds().toString(), // 秒 + 'q+': qut // 季度 + } + // 中文数字 (星期) + const week: { [key: string]: string } = { + '0': '日', + '1': '一', + '2': '二', + '3': '三', + '4': '四', + '5': '五', + '6': '六' + } + // 中文数字(季度) + const quarter: { [key: string]: string } = { + '1': '一', + '2': '二', + '3': '三', + '4': '四' + } + if (/(W+)/.test(format)) + format = format.replace( + RegExp.$1, + RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we] + ) + if (/(Q+)/.test(format)) + format = format.replace( + RegExp.$1, + RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut] + ) + if (/(Z+)/.test(format)) + format = format.replace(RegExp.$1, RegExp.$1.length == 3 ? '第' + z + '周' : z + '') + for (const k in opt) { + const r = new RegExp('(' + k + ')').exec(format) + // 若输入的长度不为1,则前面补零 + if (r) + format = format.replace( + r[1], + RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0') + ) + } + return format +} + +/** + * 获取当前日期是第几周 + * @param dateTime 当前传入的日期值 + * @returns 返回第几周数字值 + */ +export function getWeek(dateTime: Date): number { + const temptTime = new Date(dateTime.getTime()) + // 周几 + const weekday = temptTime.getDay() || 7 + // 周1+5天=周六 + temptTime.setDate(temptTime.getDate() - weekday + 1 + 5) + let firstDay = new Date(temptTime.getFullYear(), 0, 1) + const dayOfWeek = firstDay.getDay() + let spendDay = 1 + if (dayOfWeek != 0) spendDay = 7 - dayOfWeek + 1 + firstDay = new Date(temptTime.getFullYear(), 0, 1 + spendDay) + const d = Math.ceil((temptTime.valueOf() - firstDay.valueOf()) / 86400000) + const result = Math.ceil(d / 7) + return result +} + +/** + * 将时间转换为 `几秒前`、`几分钟前`、`几小时前`、`几天前` + * @param param 当前时间,new Date() 格式或者字符串时间格式 + * @param format 需要转换的时间格式字符串 + * @description param 10秒: 10 * 1000 + * @description param 1分: 60 * 1000 + * @description param 1小时: 60 * 60 * 1000 + * @description param 24小时:60 * 60 * 24 * 1000 + * @description param 3天: 60 * 60* 24 * 1000 * 3 + * @returns 返回拼接后的时间字符串 + */ +export function formatPast(param: string | Date, format = 'YYYY-mm-dd HH:MM:SS'): string { + // 传入格式处理、存储转换值 + let t: any, s: number + // 获取js 时间戳 + let time: number = new Date().getTime() + // 是否是对象 + typeof param === 'string' || 'object' ? (t = new Date(param).getTime()) : (t = param) + // 当前时间戳 - 传入时间戳 + time = Number.parseInt(`${time - t}`) + if (time < 10000) { + // 10秒内 + return '刚刚' + } else if (time < 60000 && time >= 10000) { + // 超过10秒少于1分钟内 + s = Math.floor(time / 1000) + return `${s}秒前` + } else if (time < 3600000 && time >= 60000) { + // 超过1分钟少于1小时 + s = Math.floor(time / 60000) + return `${s}分钟前` + } else if (time < 86400000 && time >= 3600000) { + // 超过1小时少于24小时 + s = Math.floor(time / 3600000) + return `${s}小时前` + } else if (time < 259200000 && time >= 86400000) { + // 超过1天少于3天内 + s = Math.floor(time / 86400000) + return `${s}天前` + } else { + // 超过3天 + const date = typeof param === 'string' || 'object' ? new Date(param) : param + return formatDate(date, format) + } +} + +/** + * 时间问候语 + * @param param 当前时间,new Date() 格式 + * @description param 调用 `formatAxis(new Date())` 输出 `上午好` + * @returns 返回拼接后的时间字符串 + */ +export function formatAxis(param: Date): string { + const hour: number = new Date(param).getHours() + if (hour < 6) return '凌晨好' + else if (hour < 9) return '早上好' + else if (hour < 12) return '上午好' + else if (hour < 14) return '中午好' + else if (hour < 17) return '下午好' + else if (hour < 19) return '傍晚好' + else if (hour < 22) return '晚上好' + else return '夜里好' +} + +/** + * 将毫秒,转换成时间字符串。例如说,xx 分钟 + * + * @param ms 毫秒 + * @returns {string} 字符串 + */ +export function formatPast2(ms) { + const day = Math.floor(ms / (24 * 60 * 60 * 1000)) + const hour = Math.floor(ms / (60 * 60 * 1000) - day * 24) + const minute = Math.floor(ms / (60 * 1000) - day * 24 * 60 - hour * 60) + const second = Math.floor(ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60) + if (day > 0) { + return day + '天' + hour + '小时' + minute + '分钟' + } + if (hour > 0) { + return hour + '小时' + minute + '分钟' + } + if (minute > 0) { + return minute + '分钟' + } + if (second > 0) { + return second + '秒' + } else { + return 0 + '秒' + } +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 00000000..d3058473 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,139 @@ +/** + * + * @param component 需要注册的组件 + * @param alias 组件别名 + * @returns any + */ +export const withInstall = (component: T, alias?: string) => { + const comp = component as any + comp.install = (app: any) => { + app.component(comp.name || comp.displayName, component) + if (alias) { + app.config.globalProperties[alias] = component + } + } + return component as T & Plugin +} + +/** + * @param str 需要转下划线的驼峰字符串 + * @returns 字符串下划线 + */ +export const humpToUnderline = (str: string): string => { + return str.replace(/([A-Z])/g, '-$1').toLowerCase() +} + +/** + * @param str 需要转驼峰的下划线字符串 + * @returns 字符串驼峰 + */ +export const underlineToHump = (str: string): string => { + if (!str) return '' + return str.replace(/\-(\w)/g, (_, letter: string) => { + return letter.toUpperCase() + }) +} + +export const setCssVar = (prop: string, val: any, dom = document.documentElement) => { + dom.style.setProperty(prop, val) +} + +/** + * 查找数组对象的某个下标 + * @param {Array} ary 查找的数组 + * @param {Functon} fn 判断的方法 + */ +// eslint-disable-next-line +export const findIndex = (ary: Array, fn: Fn): number => { + if (ary.findIndex) { + return ary.findIndex(fn) + } + let index = -1 + ary.some((item: T, i: number, ary: Array) => { + const ret: T = fn(item, i, ary) + if (ret) { + index = i + return ret + } + }) + return index +} + +export const trim = (str: string) => { + return str.replace(/(^\s*)|(\s*$)/g, '') +} + +/** + * @param {Date | number | string} time 需要转换的时间 + * @param {String} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss + */ +export const formatTime = (time: Date | number | string, fmt: string) => { + if (!time) return '' + else { + const date = new Date(time) + const o = { + 'M+': date.getMonth() + 1, + 'd+': date.getDate(), + 'H+': date.getHours(), + 'm+': date.getMinutes(), + 's+': date.getSeconds(), + 'q+': Math.floor((date.getMonth() + 3) / 3), + S: date.getMilliseconds() + } + if (/(y+)/.test(fmt)) { + fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) + } + for (const k in o) { + if (new RegExp('(' + k + ')').test(fmt)) { + fmt = fmt.replace( + RegExp.$1, + RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length) + ) + } + } + return fmt + } +} + +/** + * 生成随机字符串 + */ +export const toAnyString = () => { + const str: string = 'xxxxx-xxxxx-4xxxx-yxxxx-xxxxx'.replace(/[xy]/g, (c: string) => { + const r: number = (Math.random() * 16) | 0 + const v: number = c === 'x' ? r : (r & 0x3) | 0x8 + return v.toString() + }) + return str +} + +export const generateUUID = () => { + if (typeof crypto === 'object') { + if (typeof crypto.randomUUID === 'function') { + return crypto.randomUUID() + } + if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') { + const callback = (c: any) => { + const num = Number(c) + return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString( + 16 + ) + } + return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, callback) + } + } + let timestamp = new Date().getTime() + let performanceNow = + (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0 + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + let random = Math.random() * 16 + if (timestamp > 0) { + random = (timestamp + random) % 16 | 0 + timestamp = Math.floor(timestamp / 16) + } else { + random = (performanceNow + random) % 16 | 0 + performanceNow = Math.floor(performanceNow / 16) + } + return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16) + }) +} diff --git a/src/utils/is.ts b/src/utils/is.ts new file mode 100644 index 00000000..37529859 --- /dev/null +++ b/src/utils/is.ts @@ -0,0 +1,105 @@ +// copy to vben-admin + +const toString = Object.prototype.toString + +export const is = (val: unknown, type: string) => { + return toString.call(val) === `[object ${type}]` +} + +export const isDef = (val?: T): val is T => { + return typeof val !== 'undefined' +} + +export const isUnDef = (val?: T): val is T => { + return !isDef(val) +} + +export const isObject = (val: any): val is Record => { + return val !== null && is(val, 'Object') +} + +export const 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 const isDate = (val: unknown): val is Date => { + return is(val, 'Date') +} + +export const isNull = (val: unknown): val is null => { + return val === null +} + +export const isNullAndUnDef = (val: unknown): val is null | undefined => { + return isUnDef(val) && isNull(val) +} + +export const isNullOrUnDef = (val: unknown): val is null | undefined => { + return isUnDef(val) || isNull(val) +} + +export const isNumber = (val: unknown): val is number => { + return is(val, 'Number') +} + +export const isPromise = (val: unknown): val is Promise => { + return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch) +} + +export const isString = (val: unknown): val is string => { + return is(val, 'String') +} + +export const isFunction = (val: unknown): val is Function => { + return typeof val === 'function' +} + +export const isBoolean = (val: unknown): val is boolean => { + return is(val, 'Boolean') +} + +export const isRegExp = (val: unknown): val is RegExp => { + return is(val, 'RegExp') +} + +export const isArray = (val: any): val is Array => { + return val && Array.isArray(val) +} + +export const isWindow = (val: any): val is Window => { + return typeof window !== 'undefined' && is(val, 'Window') +} + +export const isElement = (val: unknown): val is Element => { + return isObject(val) && !!val.tagName +} + +export const isMap = (val: unknown): val is Map => { + return is(val, 'Map') +} + +export const isServer = typeof window === 'undefined' + +export const isClient = !isServer + +export const isUrl = (path: string): boolean => { + const reg = + /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/ + return reg.test(path) +} + +export const isDark = (): boolean => { + return window.matchMedia('(prefers-color-scheme: dark)').matches +} diff --git a/src/utils/jsencrypt.ts b/src/utils/jsencrypt.ts new file mode 100644 index 00000000..374d5f64 --- /dev/null +++ b/src/utils/jsencrypt.ts @@ -0,0 +1,31 @@ +import { JSEncrypt } from 'jsencrypt' + +// 密钥对生成 http://web.chacuo.net/netrsakeypair + +const publicKey = + 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' + +const privateKey = + 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + + '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' + + 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' + + 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' + + 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' + + 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' + + 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' + + 'UP8iWi1Qw0Y=' + +// 加密 +export const encrypt = (txt: string) => { + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) // 设置公钥 + return encryptor.encrypt(txt) // 对数据进行加密 +} + +// 解密 +export const decrypt = (txt: string) => { + const encryptor = new JSEncrypt() + encryptor.setPrivateKey(privateKey) // 设置私钥 + return encryptor.decrypt(txt) // 对数据进行解密 +} diff --git a/src/utils/propTypes.ts b/src/utils/propTypes.ts new file mode 100644 index 00000000..fb8f84e7 --- /dev/null +++ b/src/utils/propTypes.ts @@ -0,0 +1,28 @@ +import { createTypes, VueTypesInterface, VueTypeValidableDef } from 'vue-types' +import { CSSProperties } from 'vue' + +// 自定义扩展vue-types +type PropTypes = VueTypesInterface & { + readonly style: VueTypeValidableDef +} + +const propTypes = createTypes({ + func: undefined, + bool: undefined, + string: undefined, + number: undefined, + object: undefined, + integer: undefined +}) as PropTypes + +// 需要自定义扩展的类型 +// see: https://dwightjack.github.io/vue-types/advanced/extending-vue-types.html#the-extend-method +// propTypes.extend([ +// { +// name: 'style', +// getter: true, +// type: [String, Object], +// default: undefined +// } +// ]) +export { propTypes } diff --git a/src/utils/routerHelper.ts b/src/utils/routerHelper.ts new file mode 100644 index 00000000..0ae8e1e20 --- /dev/null +++ b/src/utils/routerHelper.ts @@ -0,0 +1,224 @@ +import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' +import type { Router, RouteLocationNormalized, RouteRecordNormalized } from 'vue-router' +import { isUrl } from '@/utils/is' +import { omit, cloneDeep } from 'lodash-es' + +const modules = import.meta.glob('../views/**/*.{vue,tsx}') + +/* Layout */ +export const Layout = () => import('@/layout/Layout.vue') + +export const getParentLayout = () => { + return () => + new Promise((resolve) => { + resolve({ + name: 'ParentLayout' + }) + }) +} + +// 按照路由中meta下的rank等级升序来排序路由 +export const ascending = (arr: any[]) => { + arr.forEach((v) => { + if (v?.meta?.rank === null) v.meta.rank = undefined + if (v?.meta?.rank === 0) { + if (v.name !== 'home' && v.path !== '/') { + console.warn('rank only the home page can be 0') + } + } + }) + return arr.sort((a: { meta: { rank: number } }, b: { meta: { rank: number } }) => { + return a?.meta?.rank - b?.meta?.rank + }) +} + +export const 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[] + } +} + +// 后端控制路由生成 +export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecordRaw[] => { + const res: AppRouteRecordRaw[] = [] + const modulesRoutesKeys = Object.keys(modules) + for (const route of routes) { + const meta = { + title: route.name, + icon: route.icon, + hidden: !route.visible, + noCache: !route.keepAlive, + alwaysShow: + route.children && + route.children.length === 1 && + (route.alwaysShow !== undefined ? route.alwaysShow : true) + } + // 路由地址转首字母大写驼峰,作为路由名称,适配keepAlive + let data: AppRouteRecordRaw = { + path: route.path, + name: + route.componentName && route.componentName.length > 0 + ? route.componentName + : toCamelCase(route.path, true), + redirect: route.redirect, + meta: meta + } + //处理顶级非目录路由 + if (!route.children && route.parentId == 0 && route.component) { + data.component = Layout + data.meta = {} + data.name = toCamelCase(route.path, true) + 'Parent' + data.redirect = '' + meta.alwaysShow = true + const childrenData: AppRouteRecordRaw = { + path: '', + name: toCamelCase(route.path, true), + redirect: route.redirect, + meta: meta + } + const index = route?.component + ? modulesRoutesKeys.findIndex((ev) => ev.includes(route.component)) + : modulesRoutesKeys.findIndex((ev) => ev.includes(route.path)) + childrenData.component = modules[modulesRoutesKeys[index]] + data.children = [childrenData] + } else { + // 目录 + if (route.children) { + data.component = Layout + data.redirect = getRedirect(route.path, route.children) + // 外链 + } else if (isUrl(route.path)) { + data = { + path: '/external-link', + component: Layout, + meta: { + name: route.name + }, + children: [data] + } as AppRouteRecordRaw + // 菜单 + } else { + // 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会根path保持一致) + const index = route?.component + ? modulesRoutesKeys.findIndex((ev) => ev.includes(route.component)) + : modulesRoutesKeys.findIndex((ev) => ev.includes(route.path)) + data.component = modules[modulesRoutesKeys[index]] + } + if (route.children) { + data.children = generateRoute(route.children) + } + } + res.push(data as AppRouteRecordRaw) + } + return res +} +export const getRedirect = (parentPath: string, children: AppCustomRouteRecordRaw[]) => { + if (!children || children.length == 0) { + return parentPath + } + const path = generateRoutePath(parentPath, children[0].path) + // 递归子节点 + if (children[0].children) return getRedirect(path, children[0].children) +} +const generateRoutePath = (parentPath: string, path: string) => { + if (parentPath.endsWith('/')) { + parentPath = parentPath.slice(0, -1) // 移除默认的 / + } + if (!path.startsWith('/')) { + path = '/' + path + } + return parentPath + path +} +export const pathResolve = (parentPath: string, path: string) => { + if (isUrl(path)) return path + const childPath = path.startsWith('/') || !path ? path : `/${path}` + return `${parentPath}${childPath}`.replace(/\/\//g, '/') +} + +// 路由降级 +export const flatMultiLevelRoutes = (routes: AppRouteRecordRaw[]) => { + const modules: AppRouteRecordRaw[] = cloneDeep(routes) + for (let index = 0; index < modules.length; index++) { + const route = modules[index] + if (!isMultipleRoute(route)) { + continue + } + promoteRouteLevel(route) + } + return modules +} + +// 层级是否大于2 +const isMultipleRoute = (route: AppRouteRecordRaw) => { + if (!route || !Reflect.has(route, 'children') || !route.children?.length) { + return false + } + + const children = route.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 +} + +// 生成二级路由 +const promoteRouteLevel = (route: AppRouteRecordRaw) => { + let router: Router | null = createRouter({ + routes: [route as RouteRecordRaw], + history: createWebHashHistory() + }) + + const routes = router.getRoutes() + addToChildren(routes, route.children || [], route) + router = null + + route.children = route.children?.map((item) => omit(item, 'children')) +} + +// 添加所有子菜单 +const addToChildren = ( + routes: RouteRecordNormalized[], + children: AppRouteRecordRaw[], + routeModule: AppRouteRecordRaw +) => { + 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 AppRouteRecordRaw) + } + if (child.children?.length) { + addToChildren(routes, child.children, routeModule) + } + } +} +const toCamelCase = (str: string, upperCaseFirst: boolean) => { + str = (str || '').toLowerCase().replace(/-(.)/g, function (group1: string) { + return group1.toUpperCase() + }) + + if (upperCaseFirst && str) { + str = str.charAt(0).toUpperCase() + str.slice(1) + } + + return str +} diff --git a/src/utils/tree.ts b/src/utils/tree.ts new file mode 100644 index 00000000..8f1f92cc --- /dev/null +++ b/src/utils/tree.ts @@ -0,0 +1,301 @@ +interface TreeHelperConfig { + id: string + children: string + pid: string +} +const DEFAULT_CONFIG: TreeHelperConfig = { + id: 'id', + children: 'children', + pid: 'pid' +} +export const defaultProps = { + children: 'children', + label: 'name', + value: 'id' +} + +const getConfig = (config: Partial) => Object.assign({}, DEFAULT_CONFIG, config) + +// tree from list +export const 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 const 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 const 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 const 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 const 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 const 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 const filter = ( + tree: T[], + func: (n: T) => boolean, + config: Partial = {} +): T[] => { + config = getConfig(config) + const children = config.children as string + function listFilter(list: T[]) { + return list + .map((node: any) => ({ ...node })) + .filter((node) => { + node[children] = node[children] && listFilter(node[children]) + return func(node) || (node[children] && node[children].length) + }) + } + return listFilter(tree) +} + +export const 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 + */ +export const treeMap = ( + treeData: T[], + opt: { children?: string; conversion: Fn } +): T[] => { + return treeData.map((item) => treeMapEach(item, opt)) +} + +/** + * @description: Extract tree specified structure + */ +export const 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 const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => { + treeDatas.forEach((element) => { + const newNode = callBack(element, parentNode) || element + if (element.children) { + eachTree(element.children, callBack, newNode) + } + }) +} + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + */ +export const handleTree = (data: any[], id?: string, parentId?: string, children?: string) => { + if (!Array.isArray(data)) { + console.warn('data must be an array') + return [] + } + const config = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children' + } + + const childrenListMap = {} + const nodeIds = {} + const tree: any[] = [] + + for (const d of data) { + const parentId = d[config.parentId] + if (childrenListMap[parentId] == null) { + childrenListMap[parentId] = [] + } + nodeIds[d[config.id]] = d + childrenListMap[parentId].push(d) + } + + for (const d of data) { + const parentId = d[config.parentId] + if (nodeIds[parentId] == null) { + tree.push(d) + } + } + + for (const t of tree) { + adaptToChildrenList(t) + } + + function adaptToChildrenList(o) { + if (childrenListMap[o[config.id]] !== null) { + o[config.childrenList] = childrenListMap[o[config.id]] + } + if (o[config.childrenList]) { + for (const c of o[config.childrenList]) { + adaptToChildrenList(c) + } + } + } + return tree +} +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + * @param {*} rootId 根Id 默认 0 + */ +export const handleTree2 = (data, id, parentId, children, rootId) => { + id = id || 'id' + parentId = parentId || 'parentId' + children = children || 'children' + rootId = + rootId || + Math.min( + ...data.map((item) => { + return item[parentId] + }) + ) || + 0 + //对源数据深度克隆 + const cloneData = JSON.parse(JSON.stringify(data)) + //循环所有项 + const treeData = cloneData.filter((father) => { + const branchArr = cloneData.filter((child) => { + //返回每一项的子级数组 + return father[id] === child[parentId] + }) + branchArr.length > 0 ? (father.children = branchArr) : '' + //返回第一层 + return father[parentId] === rootId + }) + return treeData !== '' ? treeData : data +} diff --git a/src/utils/tsxHelper.ts b/src/utils/tsxHelper.ts new file mode 100644 index 00000000..6087fa30 --- /dev/null +++ b/src/utils/tsxHelper.ts @@ -0,0 +1,16 @@ +import { Slots } from 'vue' +import { isFunction } from '@/utils/is' + +export const getSlot = (slots: Slots, slot = 'default', data?: Recordable) => { + // Reflect.has 判断一个对象是否存在某个属性 + 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) +} diff --git a/src/views/Error/403.vue b/src/views/Error/403.vue new file mode 100644 index 00000000..d477304b --- /dev/null +++ b/src/views/Error/403.vue @@ -0,0 +1,6 @@ + + diff --git a/src/views/Error/404.vue b/src/views/Error/404.vue new file mode 100644 index 00000000..d1ccb62e --- /dev/null +++ b/src/views/Error/404.vue @@ -0,0 +1,6 @@ + + diff --git a/src/views/Error/500.vue b/src/views/Error/500.vue new file mode 100644 index 00000000..9f784fd2 --- /dev/null +++ b/src/views/Error/500.vue @@ -0,0 +1,6 @@ + + diff --git a/src/views/Home/Index.vue b/src/views/Home/Index.vue new file mode 100644 index 00000000..6c586f0b --- /dev/null +++ b/src/views/Home/Index.vue @@ -0,0 +1,379 @@ + + diff --git a/src/views/Home/Index2.vue b/src/views/Home/Index2.vue new file mode 100644 index 00000000..0f588510 --- /dev/null +++ b/src/views/Home/Index2.vue @@ -0,0 +1,312 @@ + + + + diff --git a/src/views/Home/echarts-data.ts b/src/views/Home/echarts-data.ts new file mode 100644 index 00000000..56093f4b --- /dev/null +++ b/src/views/Home/echarts-data.ts @@ -0,0 +1,308 @@ +import { EChartsOption } from 'echarts' + +const { t } = useI18n() + +export const lineOptions: EChartsOption = { + title: { + text: t('analysis.monthlySales'), + left: 'center' + }, + xAxis: { + data: [ + t('analysis.january'), + t('analysis.february'), + t('analysis.march'), + t('analysis.april'), + t('analysis.may'), + t('analysis.june'), + t('analysis.july'), + t('analysis.august'), + t('analysis.september'), + t('analysis.october'), + t('analysis.november'), + t('analysis.december') + ], + boundaryGap: false, + axisTick: { + show: false + } + }, + grid: { + left: 20, + right: 20, + bottom: 20, + top: 80, + containLabel: true + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross' + }, + padding: [5, 10] + }, + yAxis: { + axisTick: { + show: false + } + }, + legend: { + data: [t('analysis.estimate'), t('analysis.actual')], + top: 50 + }, + series: [ + { + name: t('analysis.estimate'), + smooth: true, + type: 'line', + data: [100, 120, 161, 134, 105, 160, 165, 114, 163, 185, 118, 123], + animationDuration: 2800, + animationEasing: 'cubicInOut' + }, + { + name: t('analysis.actual'), + smooth: true, + type: 'line', + itemStyle: {}, + data: [120, 82, 91, 154, 162, 140, 145, 250, 134, 56, 99, 123], + animationDuration: 2800, + animationEasing: 'quadraticOut' + } + ] +} + +export const pieOptions: EChartsOption = { + title: { + text: t('analysis.userAccessSource'), + left: 'center' + }, + tooltip: { + trigger: 'item', + formatter: '{a}
{b} : {c} ({d}%)' + }, + legend: { + orient: 'vertical', + left: 'left', + data: [ + t('analysis.directAccess'), + t('analysis.mailMarketing'), + t('analysis.allianceAdvertising'), + t('analysis.videoAdvertising'), + t('analysis.searchEngines') + ] + }, + series: [ + { + name: t('analysis.userAccessSource'), + type: 'pie', + radius: '55%', + center: ['50%', '60%'], + data: [ + { value: 335, name: t('analysis.directAccess') }, + { value: 310, name: t('analysis.mailMarketing') }, + { value: 234, name: t('analysis.allianceAdvertising') }, + { value: 135, name: t('analysis.videoAdvertising') }, + { value: 1548, name: t('analysis.searchEngines') } + ] + } + ] +} + +export const barOptions: EChartsOption = { + title: { + text: t('analysis.weeklyUserActivity'), + left: 'center' + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } + }, + grid: { + left: 50, + right: 20, + bottom: 20 + }, + xAxis: { + type: 'category', + data: [ + t('analysis.monday'), + t('analysis.tuesday'), + t('analysis.wednesday'), + t('analysis.thursday'), + t('analysis.friday'), + t('analysis.saturday'), + t('analysis.sunday') + ], + axisTick: { + alignWithLabel: true + } + }, + yAxis: { + type: 'value' + }, + series: [ + { + name: t('analysis.activeQuantity'), + data: [13253, 34235, 26321, 12340, 24643, 1322, 1324], + type: 'bar' + } + ] +} + +export const radarOption: EChartsOption = { + legend: { + data: [t('workplace.personal'), t('workplace.team')] + }, + radar: { + // shape: 'circle', + indicator: [ + { name: t('workplace.quote'), max: 65 }, + { name: t('workplace.contribution'), max: 160 }, + { name: t('workplace.hot'), max: 300 }, + { name: t('workplace.yield'), max: 130 }, + { name: t('workplace.follow'), max: 100 } + ] + }, + series: [ + { + name: `xxx${t('workplace.index')}`, + type: 'radar', + data: [ + { + value: [42, 30, 20, 35, 80], + name: t('workplace.personal') + }, + { + value: [50, 140, 290, 100, 90], + name: t('workplace.team') + } + ] + } + ] +} + +export const wordOptions = { + series: [ + { + type: 'wordCloud', + gridSize: 2, + sizeRange: [12, 50], + rotationRange: [-90, 90], + shape: 'pentagon', + width: 600, + height: 400, + drawOutOfBound: true, + textStyle: { + color: function () { + return ( + 'rgb(' + + [ + Math.round(Math.random() * 160), + Math.round(Math.random() * 160), + Math.round(Math.random() * 160) + ].join(',') + + ')' + ) + } + }, + emphasis: { + textStyle: { + shadowBlur: 10, + shadowColor: '#333' + } + }, + data: [ + { + name: 'Sam S Club', + value: 10000, + textStyle: { + color: 'black' + }, + emphasis: { + textStyle: { + color: 'red' + } + } + }, + { + name: 'Macys', + value: 6181 + }, + { + name: 'Amy Schumer', + value: 4386 + }, + { + name: 'Jurassic World', + value: 4055 + }, + { + name: 'Charter Communications', + value: 2467 + }, + { + name: 'Chick Fil A', + value: 2244 + }, + { + name: 'Planet Fitness', + value: 1898 + }, + { + name: 'Pitch Perfect', + value: 1484 + }, + { + name: 'Express', + value: 1112 + }, + { + name: 'Home', + value: 965 + }, + { + name: 'Johnny Depp', + value: 847 + }, + { + name: 'Lena Dunham', + value: 582 + }, + { + name: 'Lewis Hamilton', + value: 555 + }, + { + name: 'KXAN', + value: 550 + }, + { + name: 'Mary Ellen Mark', + value: 462 + }, + { + name: 'Farrah Abraham', + value: 366 + }, + { + name: 'Rita Ora', + value: 360 + }, + { + name: 'Serena Williams', + value: 282 + }, + { + name: 'NCAA baseball tournament', + value: 273 + }, + { + name: 'Point Break', + value: 265 + } + ] + } + ] +} diff --git a/src/views/Home/types.ts b/src/views/Home/types.ts new file mode 100644 index 00000000..e6313d36 --- /dev/null +++ b/src/views/Home/types.ts @@ -0,0 +1,55 @@ +export type WorkplaceTotal = { + project: number + access: number + todo: number +} + +export type Project = { + name: string + icon: string + message: string + personal: string + time: Date | number | string +} + +export type Notice = { + title: string + type: string + keys: string[] + date: Date | number | string +} + +export type Shortcut = { + name: string + icon: string + url: string +} + +export type RadarData = { + personal: number + team: number + max: number + name: string +} +export type AnalysisTotalTypes = { + users: number + messages: number + moneys: number + shoppings: number +} + +export type UserAccessSource = { + value: number + name: string +} + +export type WeeklyUserActivity = { + value: number + name: string +} + +export type MonthlySales = { + name: string + estimate: number + actual: number +} diff --git a/src/views/Login/Login.vue b/src/views/Login/Login.vue new file mode 100644 index 00000000..a513b0ca --- /dev/null +++ b/src/views/Login/Login.vue @@ -0,0 +1,95 @@ + + + + diff --git a/src/views/Login/components/LoginForm.vue b/src/views/Login/components/LoginForm.vue new file mode 100644 index 00000000..1e41b994 --- /dev/null +++ b/src/views/Login/components/LoginForm.vue @@ -0,0 +1,307 @@ + + + + diff --git a/src/views/Login/components/LoginFormTitle.vue b/src/views/Login/components/LoginFormTitle.vue new file mode 100644 index 00000000..693d622a --- /dev/null +++ b/src/views/Login/components/LoginFormTitle.vue @@ -0,0 +1,23 @@ + + diff --git a/src/views/Login/components/MobileForm.vue b/src/views/Login/components/MobileForm.vue new file mode 100644 index 00000000..0aa1e035 --- /dev/null +++ b/src/views/Login/components/MobileForm.vue @@ -0,0 +1,213 @@ + + + + diff --git a/src/views/Login/components/QrCodeForm.vue b/src/views/Login/components/QrCodeForm.vue new file mode 100644 index 00000000..bdf7e2a5 --- /dev/null +++ b/src/views/Login/components/QrCodeForm.vue @@ -0,0 +1,28 @@ + + diff --git a/src/views/Login/components/RegisterForm.vue b/src/views/Login/components/RegisterForm.vue new file mode 100644 index 00000000..7d048506 --- /dev/null +++ b/src/views/Login/components/RegisterForm.vue @@ -0,0 +1,140 @@ + + diff --git a/src/views/Login/components/index.ts b/src/views/Login/components/index.ts new file mode 100644 index 00000000..903b1723 --- /dev/null +++ b/src/views/Login/components/index.ts @@ -0,0 +1,7 @@ +import LoginForm from './LoginForm.vue' +import MobileForm from './MobileForm.vue' +import LoginFormTitle from './LoginFormTitle.vue' +import RegisterForm from './RegisterForm.vue' +import QrCodeForm from './QrCodeForm.vue' + +export { LoginForm, MobileForm, LoginFormTitle, RegisterForm, QrCodeForm } diff --git a/src/views/Login/components/useLogin.ts b/src/views/Login/components/useLogin.ts new file mode 100644 index 00000000..dc46e097 --- /dev/null +++ b/src/views/Login/components/useLogin.ts @@ -0,0 +1,41 @@ +import { Ref } from 'vue' + +export enum LoginStateEnum { + LOGIN, + REGISTER, + RESET_PASSWORD, + MOBILE, + QR_CODE +} + +const currentState = ref(LoginStateEnum.LOGIN) + +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) { + async function validForm() { + const form = unref(formRef) + if (!form) return + const data = await form.validate() + return data as T + } + + return { + validForm + } +} diff --git a/src/views/Profile/Index.vue b/src/views/Profile/Index.vue new file mode 100644 index 00000000..6a31f157 --- /dev/null +++ b/src/views/Profile/Index.vue @@ -0,0 +1,60 @@ + + + diff --git a/src/views/Profile/components/BasicInfo.vue b/src/views/Profile/components/BasicInfo.vue new file mode 100644 index 00000000..ccb7d6f3 --- /dev/null +++ b/src/views/Profile/components/BasicInfo.vue @@ -0,0 +1,89 @@ + + diff --git a/src/views/Profile/components/ProfileUser.vue b/src/views/Profile/components/ProfileUser.vue new file mode 100644 index 00000000..2f5a77b2 --- /dev/null +++ b/src/views/Profile/components/ProfileUser.vue @@ -0,0 +1,88 @@ + + + + diff --git a/src/views/Profile/components/ResetPwd.vue b/src/views/Profile/components/ResetPwd.vue new file mode 100644 index 00000000..0b37807f --- /dev/null +++ b/src/views/Profile/components/ResetPwd.vue @@ -0,0 +1,68 @@ + + diff --git a/src/views/Profile/components/UserAvatar.vue b/src/views/Profile/components/UserAvatar.vue new file mode 100644 index 00000000..81259410 --- /dev/null +++ b/src/views/Profile/components/UserAvatar.vue @@ -0,0 +1,38 @@ + + + + diff --git a/src/views/Profile/components/UserSocial.vue b/src/views/Profile/components/UserSocial.vue new file mode 100644 index 00000000..35cce076 --- /dev/null +++ b/src/views/Profile/components/UserSocial.vue @@ -0,0 +1,67 @@ + + diff --git a/src/views/Profile/components/index.ts b/src/views/Profile/components/index.ts new file mode 100644 index 00000000..9e1883cf --- /dev/null +++ b/src/views/Profile/components/index.ts @@ -0,0 +1,7 @@ +import BasicInfo from './BasicInfo.vue' +import ProfileUser from './ProfileUser.vue' +import ResetPwd from './ResetPwd.vue' +import UserAvatarVue from './UserAvatar.vue' +import UserSocial from './UserSocial.vue' + +export { BasicInfo, ProfileUser, ResetPwd, UserAvatarVue, UserSocial } diff --git a/src/views/Redirect/Redirect.vue b/src/views/Redirect/Redirect.vue new file mode 100644 index 00000000..8be6c739 --- /dev/null +++ b/src/views/Redirect/Redirect.vue @@ -0,0 +1,26 @@ + + diff --git a/src/views/bpm/definition/definition.data.ts b/src/views/bpm/definition/definition.data.ts new file mode 100644 index 00000000..919cbd9c --- /dev/null +++ b/src/views/bpm/definition/definition.data.ts @@ -0,0 +1,73 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +// CrudSchema +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: null, + action: true, + columns: [ + { + title: '定义编号', + field: 'id', + table: { + width: 360 + } + }, + { + title: '定义名称', + field: 'name', + table: { + width: 120, + slots: { + default: 'name_default' + } + } + }, + { + title: '流程分类', + field: 'category', + dictType: DICT_TYPE.BPM_MODEL_CATEGORY, + dictClass: 'number' + }, + { + title: '表单信息', + field: 'formId', + table: { + width: 120, + slots: { + default: 'formId_default' + } + } + }, + { + title: '流程版本', + field: 'version', + table: { + width: 80, + slots: { + default: 'version_default' + } + } + }, + { + title: '激活状态', + field: 'suspensionState', + table: { + width: 80, + slots: { + default: 'suspensionState_default' + } + } + }, + { + title: '部署时间', + field: 'deploymentTime', + isForm: false, + formatter: 'formatDate', + table: { + width: 180 + } + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/bpm/definition/index.vue b/src/views/bpm/definition/index.vue new file mode 100644 index 00000000..19a7cf06 --- /dev/null +++ b/src/views/bpm/definition/index.vue @@ -0,0 +1,104 @@ + + diff --git a/src/views/bpm/form/form.data.ts b/src/views/bpm/form/form.data.ts new file mode 100644 index 00000000..d367da13 --- /dev/null +++ b/src/views/bpm/form/form.data.ts @@ -0,0 +1,43 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +const { t } = useI18n() // 国际化 + +// 表单校验 +export const rules = reactive({ + name: [required] +}) + +// CrudSchema +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: 'seq', + primaryTitle: '表单编号', + action: true, + columns: [ + { + title: '表单名', + field: 'name', + isSearch: true + }, + { + title: t('common.status'), + field: 'status', + dictType: DICT_TYPE.COMMON_STATUS, + dictClass: 'number' + }, + { + title: '备注', + field: 'remark' + }, + { + title: t('common.createTime'), + field: 'createTime', + formatter: 'formatDate', + isForm: false, + table: { + width: 180 + } + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/bpm/form/formEditor.vue b/src/views/bpm/form/formEditor.vue new file mode 100644 index 00000000..52b3709d --- /dev/null +++ b/src/views/bpm/form/formEditor.vue @@ -0,0 +1,115 @@ + + diff --git a/src/views/bpm/form/index.vue b/src/views/bpm/form/index.vue new file mode 100644 index 00000000..b4b208a9 --- /dev/null +++ b/src/views/bpm/form/index.vue @@ -0,0 +1,93 @@ + + + diff --git a/src/views/bpm/group/group.data.ts b/src/views/bpm/group/group.data.ts new file mode 100644 index 00000000..9d30f3b2 --- /dev/null +++ b/src/views/bpm/group/group.data.ts @@ -0,0 +1,63 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +const { t } = useI18n() // 国际化 + +// 表单校验 +export const rules = reactive({ + name: [required], + description: [required], + memberUserIds: [required], + status: [required] +}) + +// CrudSchema +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: 'id', + primaryTitle: '编号', + action: true, + columns: [ + { + title: '组名', + field: 'name', + isSearch: true + }, + { + title: '成员', + field: 'memberUserIds', + table: { + slots: { + default: 'memberUserIds_default' + } + } + }, + { + title: '描述', + field: 'description' + }, + { + title: t('common.status'), + field: 'status', + dictType: DICT_TYPE.COMMON_STATUS, + dictClass: 'number', + isSearch: true + }, + { + title: t('common.createTime'), + field: 'createTime', + formatter: 'formatDate', + isForm: false, + isSearch: true, + search: { + show: true, + itemRender: { + name: 'XDataTimePicker' + } + }, + table: { + width: 180 + } + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/bpm/group/index.vue b/src/views/bpm/group/index.vue new file mode 100644 index 00000000..7aa2ca4a --- /dev/null +++ b/src/views/bpm/group/index.vue @@ -0,0 +1,182 @@ + + + diff --git a/src/views/bpm/model/index.vue b/src/views/bpm/model/index.vue new file mode 100644 index 00000000..5fafb960 --- /dev/null +++ b/src/views/bpm/model/index.vue @@ -0,0 +1,586 @@ + + + diff --git a/src/views/bpm/model/model.data.ts b/src/views/bpm/model/model.data.ts new file mode 100644 index 00000000..ebbc5b87 --- /dev/null +++ b/src/views/bpm/model/model.data.ts @@ -0,0 +1,101 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +const { t } = useI18n() // 国际化 + +// 表单校验 +export const rules = reactive({ + key: [required], + name: [required], + category: [required], + formType: [required], + formId: [required], + formCustomCreatePath: [required], + formCustomViewPath: [required] +}) + +// CrudSchema +const crudSchemas = reactive({ + primaryKey: 'key', + primaryType: null, + action: true, + actionWidth: '540px', + columns: [ + { + title: '流程标识', + field: 'key', + isSearch: true, + table: { + width: 120 + } + }, + { + title: '流程名称', + field: 'name', + isSearch: true, + table: { + width: 120, + slots: { + default: 'name_default' + } + } + }, + { + title: '流程分类', + field: 'category', + dictType: DICT_TYPE.BPM_MODEL_CATEGORY, + dictClass: 'number', + isSearch: true + }, + { + title: '表单信息', + field: 'formId', + table: { + width: 180, + slots: { + default: 'formId_default' + } + } + }, + { + title: '最新部署的流程定义', + field: 'processDefinition', + isForm: false, + table: { + children: [ + { + title: '流程版本', + field: 'version', + slots: { + default: 'version_default' + }, + width: 80 + }, + { + title: '激活状态', + field: 'status', + slots: { + default: 'status_default' + }, + width: 80 + }, + { + title: '部署时间', + field: 'processDefinition.deploymentTime', + formatter: 'formatDate', + width: 180 + } + ] + } + }, + { + title: t('common.createTime'), + field: 'createTime', + isForm: false, + formatter: 'formatDate', + table: { + width: 180 + } + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/bpm/model/modelEditor.vue b/src/views/bpm/model/modelEditor.vue new file mode 100644 index 00000000..b5a41344 --- /dev/null +++ b/src/views/bpm/model/modelEditor.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/src/views/bpm/oa/leave/create.vue b/src/views/bpm/oa/leave/create.vue new file mode 100644 index 00000000..0d645c7d --- /dev/null +++ b/src/views/bpm/oa/leave/create.vue @@ -0,0 +1,56 @@ + + diff --git a/src/views/bpm/oa/leave/detail.vue b/src/views/bpm/oa/leave/detail.vue new file mode 100644 index 00000000..bb30d075 --- /dev/null +++ b/src/views/bpm/oa/leave/detail.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/views/bpm/oa/leave/index.vue b/src/views/bpm/oa/leave/index.vue new file mode 100644 index 00000000..91e7ccda --- /dev/null +++ b/src/views/bpm/oa/leave/index.vue @@ -0,0 +1,83 @@ + + + diff --git a/src/views/bpm/oa/leave/leave.data.ts b/src/views/bpm/oa/leave/leave.data.ts new file mode 100644 index 00000000..da113baf --- /dev/null +++ b/src/views/bpm/oa/leave/leave.data.ts @@ -0,0 +1,90 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +const { t } = useI18n() // 国际化 + +// 表单校验 +export const rules = reactive({ + startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }], + endTime: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }], + type: [{ required: true, message: '请假类型不能为空', trigger: 'change' }] +}) + +// crudSchemas +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: 'id', + primaryTitle: '申请编号', + action: true, + actionWidth: '260', + columns: [ + { + title: t('common.status'), + field: 'result', + dictType: DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, + dictClass: 'number', + isSearch: true, + isForm: false + }, + { + title: t('common.startTimeText'), + field: 'startTime', + formatter: 'formatDay', + table: { + width: 180 + }, + detail: { + dateFormat: 'YYYY-MM-DD' + }, + form: { + component: 'DatePicker' + } + }, + { + title: t('common.endTimeText'), + field: 'endTime', + formatter: 'formatDay', + table: { + width: 180 + }, + detail: { + dateFormat: 'YYYY-MM-DD' + }, + form: { + component: 'DatePicker' + } + }, + { + title: '请假类型', + field: 'type', + dictType: DICT_TYPE.BPM_OA_LEAVE_TYPE, + dictClass: 'number', + isSearch: true + }, + { + title: '原因', + field: 'reason', + isSearch: true, + componentProps: { + type: 'textarea', + rows: 4 + } + }, + { + title: '申请时间', + field: 'createTime', + formatter: 'formatDate', + table: { + width: 180 + }, + isSearch: true, + search: { + show: true, + itemRender: { + name: 'XDataTimePicker' + } + }, + isForm: false + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/bpm/processInstance/create.vue b/src/views/bpm/processInstance/create.vue new file mode 100644 index 00000000..c0542b22 --- /dev/null +++ b/src/views/bpm/processInstance/create.vue @@ -0,0 +1,149 @@ + + + + diff --git a/src/views/bpm/processInstance/detail.vue b/src/views/bpm/processInstance/detail.vue new file mode 100644 index 00000000..5dd05adc --- /dev/null +++ b/src/views/bpm/processInstance/detail.vue @@ -0,0 +1,490 @@ + + + + diff --git a/src/views/bpm/processInstance/index.vue b/src/views/bpm/processInstance/index.vue new file mode 100644 index 00000000..d2f81753 --- /dev/null +++ b/src/views/bpm/processInstance/index.vue @@ -0,0 +1,88 @@ + + diff --git a/src/views/bpm/processInstance/process.create.ts b/src/views/bpm/processInstance/process.create.ts new file mode 100644 index 00000000..b2282406 --- /dev/null +++ b/src/views/bpm/processInstance/process.create.ts @@ -0,0 +1,34 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +// crudSchemas +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: null, + action: true, + columns: [ + { + title: '流程名称', + field: 'name' + }, + { + title: '流程分类', + field: 'category', + dictType: DICT_TYPE.BPM_MODEL_CATEGORY, + dictClass: 'number' + }, + { + title: '流程版本', + field: 'version', + table: { + slots: { + default: 'version_default' + } + } + }, + { + title: '流程描述', + field: 'description' + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/bpm/processInstance/process.data.ts b/src/views/bpm/processInstance/process.data.ts new file mode 100644 index 00000000..883f4687 --- /dev/null +++ b/src/views/bpm/processInstance/process.data.ts @@ -0,0 +1,89 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +const { t } = useI18n() // 国际化 + +// CrudSchema +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: null, + primaryTitle: '编号', + action: true, + actionWidth: '200px', + columns: [ + { + title: '编号', + field: 'id', + table: { + width: 320 + } + }, + { + title: '流程名', + field: 'name', + isSearch: true + }, + { + title: '所属流程', + field: 'processDefinitionId', + isSearch: true, + isTable: false + }, + { + title: '流程分类', + field: 'category', + dictType: DICT_TYPE.BPM_MODEL_CATEGORY, + dictClass: 'number', + isSearch: true + }, + { + title: '当前审批任务', + field: 'tasks', + table: { + width: 140, + slots: { + default: 'tasks_default' + } + } + }, + { + title: t('common.status'), + field: 'status', + dictType: DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, + dictClass: 'number', + isSearch: true + }, + { + title: '结果', + field: 'result', + dictType: DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, + dictClass: 'number', + isSearch: true + }, + { + title: '提交时间', + field: 'createTime', + formatter: 'formatDate', + table: { + width: 180 + }, + isForm: false, + isSearch: true, + search: { + show: true, + itemRender: { + name: 'XDataTimePicker' + } + } + }, + { + title: '结束时间', + field: 'endTime', + formatter: 'formatDate', + table: { + width: 180 + }, + isForm: false + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/bpm/task/done/done.data.ts b/src/views/bpm/task/done/done.data.ts new file mode 100644 index 00000000..5944671f --- /dev/null +++ b/src/views/bpm/task/done/done.data.ts @@ -0,0 +1,52 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +const { t } = useI18n() // 国际化 + +// crudSchemas +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: null, + action: true, + columns: [ + { + title: '任务编号', + field: 'id', + table: { + width: 320 + } + }, + { + title: '任务名称', + field: 'name', + isSearch: true + }, + { + title: '所属流程', + field: 'processInstance.name' + }, + { + title: '流程发起人', + field: 'processInstance.startUserNickname' + }, + { + title: t('common.status'), + field: 'result', + dictType: DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, + dictClass: 'number', + isSearch: true + }, + { + title: '原因', + field: 'reason' + }, + { + title: t('common.createTime'), + field: 'createTime', + formatter: 'formatDate', + table: { + width: 180 + } + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/bpm/task/done/index.vue b/src/views/bpm/task/done/index.vue new file mode 100644 index 00000000..31df7bd9 --- /dev/null +++ b/src/views/bpm/task/done/index.vue @@ -0,0 +1,36 @@ + + diff --git a/src/views/bpm/task/todo/index.vue b/src/views/bpm/task/todo/index.vue new file mode 100644 index 00000000..86208602 --- /dev/null +++ b/src/views/bpm/task/todo/index.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/views/bpm/task/todo/todo.data.ts b/src/views/bpm/task/todo/todo.data.ts new file mode 100644 index 00000000..85c58ce9 --- /dev/null +++ b/src/views/bpm/task/todo/todo.data.ts @@ -0,0 +1,57 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +const { t } = useI18n() // 国际化 + +// crudSchemas +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: null, + action: true, + columns: [ + { + title: '任务编号', + field: 'id', + table: { + width: 320 + } + }, + { + title: '任务名称', + field: 'name', + isSearch: true + }, + { + title: '所属流程', + field: 'processInstance.name' + }, + { + title: '流程发起人', + field: 'processInstance.startUserNickname' + }, + { + title: t('common.createTime'), + field: 'createTime', + formatter: 'formatDate', + table: { + width: 180 + }, + isSearch: true, + search: { + show: true, + itemRender: { + name: 'XDataTimePicker' + } + } + }, + { + title: '任务状态', + field: 'suspensionState', + table: { + slots: { + default: 'suspensionState_default' + } + } + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/bpm/taskAssignRule/index.vue b/src/views/bpm/taskAssignRule/index.vue new file mode 100644 index 00000000..f93e4591 --- /dev/null +++ b/src/views/bpm/taskAssignRule/index.vue @@ -0,0 +1,350 @@ + + diff --git a/src/views/bpm/taskAssignRule/taskAssignRule.data.ts b/src/views/bpm/taskAssignRule/taskAssignRule.data.ts new file mode 100644 index 00000000..1ced3425 --- /dev/null +++ b/src/views/bpm/taskAssignRule/taskAssignRule.data.ts @@ -0,0 +1,46 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +// 表单校验 +export const rules = reactive({ + type: [{ required: true, message: '规则类型不能为空', trigger: 'change' }], + roleIds: [{ required: true, message: '指定角色不能为空', trigger: 'change' }], + deptIds: [{ required: true, message: '指定部门不能为空', trigger: 'change' }], + postIds: [{ required: true, message: '指定岗位不能为空', trigger: 'change' }], + userIds: [{ required: true, message: '指定用户不能为空', trigger: 'change' }], + userGroupIds: [{ required: true, message: '指定用户组不能为空', trigger: 'change' }], + scripts: [{ required: true, message: '指定脚本不能为空', trigger: 'change' }] +}) + +// CrudSchema +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: null, + action: true, + actionWidth: '200px', + columns: [ + { + title: '任务名', + field: 'taskDefinitionName' + }, + { + title: '任务标识', + field: 'taskDefinitionKey' + }, + { + title: '规则类型', + field: 'type', + dictType: DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE, + dictClass: 'number' + }, + { + title: '规则范围', + field: 'options', + table: { + slots: { + default: 'options_default' + } + } + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/infra/apiAccessLog/apiAccessLog.data.ts b/src/views/infra/apiAccessLog/apiAccessLog.data.ts new file mode 100644 index 00000000..927e2330 --- /dev/null +++ b/src/views/infra/apiAccessLog/apiAccessLog.data.ts @@ -0,0 +1,73 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +// CrudSchema +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: 'seq', + primaryTitle: '日志编号', + action: true, + actionWidth: '80px', + columns: [ + { + title: '链路追踪', + field: 'traceId' + }, + { + title: '用户编号', + field: 'userId', + isSearch: true + }, + { + title: '用户类型', + field: 'userType', + dictType: DICT_TYPE.USER_TYPE, + dictClass: 'number', + isSearch: true + }, + { + title: '应用名', + field: 'applicationName', + isSearch: true + }, + { + title: '请求方法名', + field: 'requestMethod' + }, + { + title: '请求地址', + field: 'requestUrl', + isSearch: true + }, + { + title: '请求时间', + field: 'beginTime', + formatter: 'formatDate', + search: { + show: true, + itemRender: { + name: 'XDataTimePicker' + } + } + }, + { + title: '执行时长', + field: 'duration', + table: { + slots: { + default: 'duration_default' + } + } + }, + { + title: '操作结果', + field: 'resultCode', + isSearch: true, + table: { + slots: { + default: 'resultCode_default' + } + } + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/infra/apiAccessLog/index.vue b/src/views/infra/apiAccessLog/index.vue new file mode 100644 index 00000000..6a09927d --- /dev/null +++ b/src/views/infra/apiAccessLog/index.vue @@ -0,0 +1,62 @@ + + diff --git a/src/views/infra/apiErrorLog/apiErrorLog.data.ts b/src/views/infra/apiErrorLog/apiErrorLog.data.ts new file mode 100644 index 00000000..6471f64d --- /dev/null +++ b/src/views/infra/apiErrorLog/apiErrorLog.data.ts @@ -0,0 +1,76 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' + +// CrudSchema +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: 'seq', + primaryTitle: '日志编号', + action: true, + actionWidth: '300', + columns: [ + { + title: '链路追踪', + field: 'traceId', + isTable: false + }, + { + title: '用户编号', + field: 'userId', + isSearch: true + }, + { + title: '用户类型', + field: 'userType', + dictType: DICT_TYPE.USER_TYPE, + isSearch: true + }, + { + title: '应用名', + field: 'applicationName', + isSearch: true + }, + { + title: '请求方法名', + field: 'requestMethod' + }, + { + title: '请求地址', + field: 'requestUrl', + isSearch: true + }, + { + title: '异常发生时间', + field: 'exceptionTime', + formatter: 'formatDate', + search: { + show: true, + itemRender: { + name: 'XDataTimePicker' + } + } + }, + { + title: '异常名', + field: 'exceptionName' + }, + { + title: '处理状态', + field: 'processStatus', + dictType: DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS, + dictClass: 'number', + isSearch: true + }, + { + title: '处理人', + field: 'processUserId', + isTable: false + }, + { + title: '处理时间', + field: 'processTime', + formatter: 'formatDate', + isTable: false + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/infra/apiErrorLog/index.vue b/src/views/infra/apiErrorLog/index.vue new file mode 100644 index 00000000..4193351a --- /dev/null +++ b/src/views/infra/apiErrorLog/index.vue @@ -0,0 +1,99 @@ + + diff --git a/src/views/infra/build/index.vue b/src/views/infra/build/index.vue new file mode 100644 index 00000000..7da88f8c --- /dev/null +++ b/src/views/infra/build/index.vue @@ -0,0 +1,115 @@ + + diff --git a/src/views/infra/codegen/EditTable.vue b/src/views/infra/codegen/EditTable.vue new file mode 100644 index 00000000..820d23ca --- /dev/null +++ b/src/views/infra/codegen/EditTable.vue @@ -0,0 +1,67 @@ + + diff --git a/src/views/infra/codegen/codegen.data.ts b/src/views/infra/codegen/codegen.data.ts new file mode 100644 index 00000000..f3723a35 --- /dev/null +++ b/src/views/infra/codegen/codegen.data.ts @@ -0,0 +1,53 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' +const { t } = useI18n() // 国际化 + +// 表单校验 +export const rules = reactive({ + title: [required], + type: [required], + status: [required] +}) + +// CrudSchema +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: 'seq', + action: true, + actionWidth: '400px', + columns: [ + { + title: '表名称', + field: 'tableName', + isSearch: true + }, + { + title: '表描述', + field: 'tableComment', + isSearch: true + }, + { + title: '实体', + field: 'className', + isSearch: true + }, + { + title: t('common.createTime'), + field: 'createTime', + formatter: 'formatDate', + isForm: false, + search: { + show: true, + itemRender: { + name: 'XDataTimePicker' + } + } + }, + { + title: t('common.updateTime'), + field: 'updateTime', + formatter: 'formatDate', + isForm: false + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/infra/codegen/components/BasicInfoForm.vue b/src/views/infra/codegen/components/BasicInfoForm.vue new file mode 100644 index 00000000..2009553f --- /dev/null +++ b/src/views/infra/codegen/components/BasicInfoForm.vue @@ -0,0 +1,183 @@ + + diff --git a/src/views/infra/codegen/components/CloumInfoForm.vue b/src/views/infra/codegen/components/CloumInfoForm.vue new file mode 100644 index 00000000..0b6ea697 --- /dev/null +++ b/src/views/infra/codegen/components/CloumInfoForm.vue @@ -0,0 +1,137 @@ + + diff --git a/src/views/infra/codegen/components/ImportTable.vue b/src/views/infra/codegen/components/ImportTable.vue new file mode 100644 index 00000000..38a81541 --- /dev/null +++ b/src/views/infra/codegen/components/ImportTable.vue @@ -0,0 +1,125 @@ + + diff --git a/src/views/infra/codegen/components/Preview.vue b/src/views/infra/codegen/components/Preview.vue new file mode 100644 index 00000000..2d9482ff --- /dev/null +++ b/src/views/infra/codegen/components/Preview.vue @@ -0,0 +1,145 @@ + + diff --git a/src/views/infra/codegen/components/index.ts b/src/views/infra/codegen/components/index.ts new file mode 100644 index 00000000..b84c5a03 --- /dev/null +++ b/src/views/infra/codegen/components/index.ts @@ -0,0 +1,5 @@ +import BasicInfoForm from './BasicInfoForm.vue' +import CloumInfoForm from './CloumInfoForm.vue' +import ImportTable from './ImportTable.vue' +import Preview from './Preview.vue' +export { BasicInfoForm, CloumInfoForm, ImportTable, Preview } diff --git a/src/views/infra/codegen/index.vue b/src/views/infra/codegen/index.vue new file mode 100644 index 00000000..8337d2d4 --- /dev/null +++ b/src/views/infra/codegen/index.vue @@ -0,0 +1,107 @@ + + diff --git a/src/views/infra/config/config.data.ts b/src/views/infra/config/config.data.ts new file mode 100644 index 00000000..41acfa15 --- /dev/null +++ b/src/views/infra/config/config.data.ts @@ -0,0 +1,90 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' +const { t } = useI18n() // 国际化 + +// 表单校验 +export const rules = reactive({ + category: [required], + name: [required], + key: [required], + value: [required] +}) + +// CrudSchema +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: null, + action: true, + columns: [ + { + title: '参数分类', + field: 'category' + }, + { + title: '参数名称', + field: 'name', + isSearch: true + }, + { + title: '参数键名', + field: 'key', + isSearch: true + }, + { + title: '参数键值', + field: 'value' + }, + { + title: '系统内置', + field: 'type', + dictType: DICT_TYPE.INFRA_CONFIG_TYPE, + dictClass: 'number', + isSearch: true + }, + { + title: '是否可见', + field: 'visible', + table: { + slots: { + default: 'visible_default' + } + }, + form: { + component: 'RadioButton', + componentProps: { + options: [ + { label: '是', value: true }, + { label: '否', value: false } + ] + } + } + }, + { + title: t('form.remark'), + field: 'remark', + isTable: false, + form: { + component: 'Input', + componentProps: { + type: 'textarea', + rows: 4 + }, + colProps: { + span: 24 + } + } + }, + { + title: t('common.createTime'), + field: 'createTime', + formatter: 'formatDate', + isForm: false, + search: { + show: true, + itemRender: { + name: 'XDataTimePicker' + } + } + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/infra/config/index.vue b/src/views/infra/config/index.vue new file mode 100644 index 00000000..73f0f8ad --- /dev/null +++ b/src/views/infra/config/index.vue @@ -0,0 +1,162 @@ + + diff --git a/src/views/infra/dataSourceConfig/dataSourceConfig.data.ts b/src/views/infra/dataSourceConfig/dataSourceConfig.data.ts new file mode 100644 index 00000000..a790ad17 --- /dev/null +++ b/src/views/infra/dataSourceConfig/dataSourceConfig.data.ts @@ -0,0 +1,52 @@ +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas' +// 国际化 +const { t } = useI18n() +// 表单校验 +export const rules = reactive({ + name: [required], + url: [required], + username: [required], + password: [required] +}) +// 新增 + 修改 +const crudSchemas = reactive({ + primaryKey: 'id', + primaryType: 'seq', + action: true, + columns: [ + { + title: '数据源名称', + field: 'name' + }, + { + title: '数据源连接', + field: 'url', + form: { + component: 'Input', + componentProps: { + type: 'textarea', + rows: 4 + }, + colProps: { + span: 24 + } + } + }, + { + title: '用户名', + field: 'username' + }, + { + title: '密码', + field: 'password', + isTable: false + }, + { + title: t('common.createTime'), + field: 'createTime', + formatter: 'formatDate', + isForm: false + } + ] +}) +export const { allSchemas } = useVxeCrudSchemas(crudSchemas) diff --git a/src/views/infra/dataSourceConfig/index.vue b/src/views/infra/dataSourceConfig/index.vue new file mode 100644 index 00000000..1e3db49d --- /dev/null +++ b/src/views/infra/dataSourceConfig/index.vue @@ -0,0 +1,145 @@ + + diff --git a/src/views/infra/dbDoc/index.vue b/src/views/infra/dbDoc/index.vue new file mode 100644 index 00000000..18db3e73 --- /dev/null +++ b/src/views/infra/dbDoc/index.vue @@ -0,0 +1,61 @@ +