From 34b73590c9f615fd4adae48c55e28d9e2441e607 Mon Sep 17 00:00:00 2001 From: ccongli <1441652193@qq.com> Date: Fri, 13 Oct 2023 08:54:04 +0800 Subject: [PATCH] =?UTF-8?q?vben=E6=A1=86=E6=9E=B6=E5=BC=83=E7=94=A8,=20?= =?UTF-8?q?=E6=94=B9vue3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yunxi-ui-admin-vue3/.editorconfig | 12 + yunxi-ui-admin-vue3/.env | 17 + yunxi-ui-admin-vue3/.env.base | 19 + yunxi-ui-admin-vue3/.env.dev | 31 + yunxi-ui-admin-vue3/.env.front | 34 + yunxi-ui-admin-vue3/.env.pro | 31 + yunxi-ui-admin-vue3/.env.stage | 31 + yunxi-ui-admin-vue3/.env.static | 31 + yunxi-ui-admin-vue3/.eslintignore | 8 + .../.eslintrc-auto-import.json | 259 +++ yunxi-ui-admin-vue3/.eslintrc.js | 72 + yunxi-ui-admin-vue3/.gitignore | 14 + yunxi-ui-admin-vue3/.prettierignore | 11 + yunxi-ui-admin-vue3/.stylelintignore | 6 + yunxi-ui-admin-vue3/LICENSE | 21 + yunxi-ui-admin-vue3/README.md | 235 ++ yunxi-ui-admin-vue3/build/vite/index.ts | 107 + yunxi-ui-admin-vue3/build/vite/optimize.ts | 112 + yunxi-ui-admin-vue3/index.html | 151 ++ yunxi-ui-admin-vue3/package.json | 146 ++ yunxi-ui-admin-vue3/postcss.config.js | 5 + yunxi-ui-admin-vue3/prettier.config.js | 22 + yunxi-ui-admin-vue3/public/favicon.ico | Bin 0 -> 4286 bytes yunxi-ui-admin-vue3/public/home.png | Bin 0 -> 74352 bytes yunxi-ui-admin-vue3/public/logo.gif | Bin 0 -> 6334 bytes yunxi-ui-admin-vue3/src/App.vue | 57 + .../src/api/bpm/activity/index.ts | 8 + .../src/api/bpm/definition/index.ts | 21 + yunxi-ui-admin-vue3/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 | 41 + yunxi-ui-admin-vue3/src/api/bpm/task/index.ts | 60 + .../src/api/bpm/taskAssignRule/index.ts | 29 + .../src/api/bpm/userGroup/index.ts | 47 + .../src/api/infra/apiAccessLog/index.ts | 30 + .../src/api/infra/apiErrorLog/index.ts | 48 + .../src/api/infra/codegen/index.ts | 123 ++ .../src/api/infra/config/index.ts | 48 + .../src/api/infra/dataSourceConfig/index.ts | 35 + .../src/api/infra/dbDoc/index.ts | 16 + .../src/api/infra/file/index.ts | 17 + .../src/api/infra/fileConfig/index.ts | 61 + .../src/api/infra/job/index.ts | 63 + .../src/api/infra/jobLog/index.ts | 33 + .../src/api/infra/redis/index.ts | 8 + .../src/api/infra/redis/types.ts | 176 ++ yunxi-ui-admin-vue3/src/api/login/index.ts | 64 + .../src/api/login/oauth2/index.ts | 41 + yunxi-ui-admin-vue3/src/api/login/types.ts | 28 + .../src/api/mall/product/brand.ts | 61 + .../src/api/mall/product/category.ts | 60 + .../src/api/mall/product/comment.ts | 49 + .../src/api/mall/product/property.ts | 103 + .../src/api/mall/product/spu.ts | 108 + .../mall/promotion/bargain/bargainActivity.ts | 68 + .../api/mall/promotion/bargain/bargainHelp.ts | 14 + .../mall/promotion/bargain/bargainRecord.ts | 19 + .../combination/combinationActivity.ts | 66 + .../combination/combinationRecord.ts | 33 + .../src/api/mall/promotion/coupon/coupon.ts | 26 + .../mall/promotion/coupon/couponTemplate.ts | 83 + .../mall/promotion/seckill/seckillActivity.ts | 68 + .../mall/promotion/seckill/seckillConfig.ts | 49 + .../src/api/mall/statistics/member.ts | 91 + .../src/api/mall/statistics/trade.ts | 70 + .../src/api/mall/trade/afterSale/index.ts | 75 + .../api/mall/trade/brokerage/record/index.ts | 11 + .../api/mall/trade/brokerage/user/index.ts | 39 + .../mall/trade/brokerage/withdraw/index.ts | 39 + .../src/api/mall/trade/config/index.ts | 24 + .../api/mall/trade/delivery/express/index.ts | 45 + .../trade/delivery/expressTemplate/index.ts | 54 + .../mall/trade/delivery/pickUpStore/index.ts | 46 + .../src/api/mall/trade/order/index.ts | 146 ++ .../src/api/member/address/index.ts | 15 + .../src/api/member/config/index.ts | 19 + .../src/api/member/experience-record/index.ts | 22 + .../src/api/member/group/index.ts | 38 + .../src/api/member/level/index.ts | 42 + .../src/api/member/point/record/index.ts | 18 + .../src/api/member/signin/config/index.ts | 34 + .../src/api/member/signin/record/index.ts | 13 + .../src/api/member/tag/index.ts | 36 + .../src/api/member/user/index.ts | 53 + .../src/api/mp/account/index.ts | 46 + .../src/api/mp/autoReply/index.ts | 39 + yunxi-ui-admin-vue3/src/api/mp/draft/index.ts | 35 + .../src/api/mp/freePublish/index.ts | 23 + .../src/api/mp/material/index.ts | 16 + yunxi-ui-admin-vue3/src/api/mp/menu/index.ts | 26 + .../src/api/mp/message/index.ts | 17 + .../src/api/mp/statistics/index.ts | 33 + yunxi-ui-admin-vue3/src/api/mp/tag/index.ts | 60 + yunxi-ui-admin-vue3/src/api/mp/user/index.ts | 31 + yunxi-ui-admin-vue3/src/api/pay/app/index.ts | 65 + .../src/api/pay/channel/index.ts | 46 + yunxi-ui-admin-vue3/src/api/pay/demo/index.ts | 36 + .../src/api/pay/notify/index.ts | 16 + .../src/api/pay/order/index.ts | 104 + .../src/api/pay/refund/index.ts | 116 + .../src/api/pay/wallet/index.ts | 22 + .../src/api/system/area/index.ts | 11 + .../src/api/system/dept/index.ts | 43 + .../src/api/system/dict/dict.data.ts | 49 + .../src/api/system/dict/dict.type.ts | 44 + .../src/api/system/errorCode/index.ts | 40 + .../src/api/system/loginLog/index.ts | 24 + .../src/api/system/mail/account/index.ts | 41 + .../src/api/system/mail/log/index.ts | 30 + .../src/api/system/mail/template/index.ts | 50 + .../src/api/system/menu/index.ts | 49 + .../src/api/system/notice/index.ts | 37 + .../src/api/system/notify/message/index.ts | 48 + .../src/api/system/notify/template/index.ts | 49 + .../src/api/system/oauth2/client.ts | 47 + .../src/api/system/oauth2/token.ts | 22 + .../src/api/system/operatelog/index.ts | 33 + .../src/api/system/permission/index.ts | 42 + .../src/api/system/post/index.ts | 46 + .../src/api/system/role/index.ts | 61 + .../src/api/system/sensitiveWord/index.ts | 58 + .../src/api/system/sms/smsChannel/index.ts | 43 + .../src/api/system/sms/smsLog/index.ts | 39 + .../src/api/system/sms/smsTemplate/index.ts | 60 + .../src/api/system/tenant/index.ts | 62 + .../src/api/system/tenantPackage/index.ts | 42 + .../src/api/system/user/index.ts | 76 + .../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 yunxi-ui-admin-vue3/src/assets/imgs/logo.png | Bin 0 -> 2801 bytes .../src/assets/imgs/profile.jpg | Bin 0 -> 7885 bytes .../src/assets/imgs/wechat.png | Bin 0 -> 1881 bytes .../src/assets/map/json/china.json | 1 + yunxi-ui-admin-vue3/src/assets/svgs/403.svg | 1 + yunxi-ui-admin-vue3/src/assets/svgs/404.svg | 1 + yunxi-ui-admin-vue3/src/assets/svgs/500.svg | 1 + yunxi-ui-admin-vue3/src/assets/svgs/icon.svg | 1 + .../src/assets/svgs/login-bg.svg | 1 + .../src/assets/svgs/login-box-bg.svg | 1 + .../src/assets/svgs/member_balance.svg | 1 + .../svgs/member_expenditure_balance.svg | 1 + .../src/assets/svgs/member_level.svg | 1 + .../src/assets/svgs/member_point.svg | 1 + .../assets/svgs/member_recharge_balance.svg | 1 + .../src/assets/svgs/message.svg | 1 + yunxi-ui-admin-vue3/src/assets/svgs/money.svg | 1 + .../src/assets/svgs/pay/icon/alipay_app.svg | 1 + .../src/assets/svgs/pay/icon/alipay_bar.svg | 2 + .../src/assets/svgs/pay/icon/alipay_pc.svg | 1 + .../src/assets/svgs/pay/icon/alipay_qr.svg | 2 + .../src/assets/svgs/pay/icon/alipay_wap.svg | 1 + .../src/assets/svgs/pay/icon/mock.svg | 1 + .../src/assets/svgs/pay/icon/wx_app.svg | 2 + .../src/assets/svgs/pay/icon/wx_bar.svg | 1 + .../src/assets/svgs/pay/icon/wx_lite.svg | 1 + .../src/assets/svgs/pay/icon/wx_native.svg | 1 + .../src/assets/svgs/pay/icon/wx_pub.svg | 2 + .../src/assets/svgs/peoples.svg | 1 + .../src/assets/svgs/shopping.svg | 1 + .../src/components/Backtop/index.ts | 3 + .../src/components/Backtop/src/Backtop.vue | 17 + .../src/components/Card/index.ts | 3 + .../src/components/Card/src/CardTitle.vue | 37 + .../src/components/ConfigGlobal/index.ts | 3 + .../ConfigGlobal/src/ConfigGlobal.vue | 63 + .../src/components/ContentDetailWrap/index.ts | 3 + .../src/ContentDetailWrap.vue | 58 + .../src/components/ContentWrap/index.ts | 3 + .../ContentWrap/src/ContentWrap.vue | 34 + .../src/components/CountTo/index.ts | 3 + .../src/components/CountTo/src/CountTo.vue | 182 ++ .../src/components/Crontab/index.ts | 2 + .../src/components/Crontab/src/Crontab.vue | 1011 +++++++++ .../src/components/Cropper/index.ts | 4 + .../components/Cropper/src/CopperModal.vue | 261 +++ .../src/components/Cropper/src/Cropper.vue | 183 ++ .../components/Cropper/src/CropperAvatar.vue | 142 ++ .../src/components/Cropper/src/types.ts | 8 + .../src/components/Descriptions/index.ts | 4 + .../Descriptions/src/Descriptions.vue | 163 ++ .../src/DescriptionsItemLabel.vue | 29 + .../src/components/Dialog/index.ts | 3 + .../src/components/Dialog/src/Dialog.vue | 140 ++ .../src/components/DictTag/index.ts | 3 + .../src/components/DictTag/src/DictTag.vue | 60 + .../src/components/DocAlert/index.vue | 34 + .../src/components/Echart/index.ts | 3 + .../src/components/Echart/src/Echart.vue | 115 + .../src/components/Editor/index.ts | 8 + .../src/components/Editor/src/Editor.vue | 202 ++ .../src/components/Error/index.ts | 3 + .../src/components/Error/src/Error.vue | 58 + .../src/components/Form/index.ts | 15 + .../src/components/Form/src/Form.vue | 307 +++ .../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 + .../components/Highlight/src/Highlight.vue | 65 + .../src/components/IFrame/index.ts | 3 + .../src/components/IFrame/src/IFrame.vue | 32 + .../src/components/Icon/index.ts | 4 + .../src/components/Icon/src/Icon.vue | 86 + .../src/components/Icon/src/IconSelect.vue | 229 ++ .../src/components/Icon/src/data.ts | 1961 +++++++++++++++++ .../src/components/ImageViewer/index.ts | 33 + .../ImageViewer/src/ImageViewer.vue | 35 + .../src/components/ImageViewer/src/types.ts | 9 + .../src/components/Infotip/index.ts | 3 + .../src/components/Infotip/src/Infotip.vue | 54 + .../src/components/InputPassword/index.ts | 3 + .../InputPassword/src/InputPassword.vue | 152 ++ .../src/components/Pagination/index.vue | 87 + .../src/components/Qrcode/index.ts | 3 + .../src/components/Qrcode/src/Qrcode.vue | 253 +++ .../src/components/RouterSearch/index.vue | 76 + .../src/components/Search/index.ts | 3 + .../src/components/Search/src/Search.vue | 157 ++ .../src/components/Sticky/index.ts | 3 + .../src/components/Sticky/src/Sticky.vue | 143 ++ .../src/components/Table/index.ts | 12 + .../src/components/Table/src/Table.vue | 311 +++ .../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 | 17 + .../src/components/UploadFile/index.ts | 5 + .../components/UploadFile/src/UploadFile.vue | 170 ++ .../components/UploadFile/src/UploadImg.vue | 271 +++ .../components/UploadFile/src/UploadImgs.vue | 309 +++ .../src/components/Verifition/index.ts | 3 + .../src/components/Verifition/src/Verify.vue | 441 ++++ .../Verifition/src/Verify/VerifyPoints.vue | 250 +++ .../Verifition/src/Verify/VerifySlide.vue | 376 ++++ .../components/Verifition/src/Verify/index.ts | 4 + .../components/Verifition/src/utils/ase.ts | 14 + .../components/Verifition/src/utils/util.ts | 97 + .../src/components/XButton/index.ts | 4 + .../src/components/XButton/src/XButton.vue | 50 + .../components/XButton/src/XTextButton.vue | 49 + .../package/designer/ProcessDesigner.vue | 704 ++++++ .../package/designer/ProcessViewer.vue | 635 ++++++ .../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 | 11 + .../package/palette/ProcessPalette.vue | 45 + .../package/penal/PropertiesPanel.vue | 211 ++ .../package/penal/base/ElementBaseInfo.vue | 184 ++ .../penal/flow-condition/FlowCondition.vue | 191 ++ .../package/penal/form/ElementForm.vue | 465 ++++ .../package/penal/index.js | 7 + .../penal/listeners/ElementListeners.vue | 403 ++++ .../penal/listeners/UserTaskListeners.vue | 451 ++++ .../package/penal/listeners/template.js | 178 ++ .../package/penal/listeners/utilSelf.ts | 62 + .../multi-instance/ElementMultiInstance.vue | 254 +++ .../penal/other/ElementOtherConfig.vue | 55 + .../penal/properties/ElementProperties.vue | 169 ++ .../penal/signal-message/SignalAndMessage.vue | 113 + .../package/penal/task/ElementTask.vue | 86 + .../task/task-components/ReceiveTask.vue | 125 ++ .../penal/task/task-components/ScriptTask.vue | 99 + .../penal/task/task-components/UserTask.vue | 98 + .../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 | 77 + .../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 + yunxi-ui-admin-vue3/src/components/index.ts | 6 + .../src/config/axios/config.ts | 28 + .../src/config/axios/errorCode.ts | 6 + yunxi-ui-admin-vue3/src/config/axios/index.ts | 51 + .../src/config/axios/service.ts | 239 ++ yunxi-ui-admin-vue3/src/directives/index.ts | 13 + .../src/directives/permission/hasPermi.ts | 27 + .../src/directives/permission/hasRole.ts | 27 + .../src/hooks/event/useScrollTo.ts | 60 + yunxi-ui-admin-vue3/src/hooks/web/useCache.ts | 27 + .../src/hooks/web/useConfigGlobal.ts | 9 + .../src/hooks/web/useCrudSchemas.ts | 326 +++ .../src/hooks/web/useDesign.ts | 18 + yunxi-ui-admin-vue3/src/hooks/web/useEmitt.ts | 22 + yunxi-ui-admin-vue3/src/hooks/web/useForm.ts | 94 + yunxi-ui-admin-vue3/src/hooks/web/useI18n.ts | 53 + yunxi-ui-admin-vue3/src/hooks/web/useIcon.ts | 8 + yunxi-ui-admin-vue3/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 + yunxi-ui-admin-vue3/src/hooks/web/useTable.ts | 223 ++ .../src/hooks/web/useTimeAgo.ts | 49 + yunxi-ui-admin-vue3/src/hooks/web/useTitle.ts | 24 + .../src/hooks/web/useValidator.ts | 62 + .../src/hooks/web/useWatermark.ts | 55 + yunxi-ui-admin-vue3/src/layout/Layout.vue | 78 + .../src/layout/components/AppView.vue | 61 + .../src/layout/components/Breadcrumb/index.ts | 3 + .../components/Breadcrumb/src/Breadcrumb.vue | 129 ++ .../components/Breadcrumb/src/helper.ts | 31 + .../src/layout/components/Collapse/index.ts | 3 + .../components/Collapse/src/Collapse.vue | 36 + .../layout/components/ContextMenu/index.ts | 10 + .../ContextMenu/src/ContextMenu.vue | 76 + .../src/layout/components/Footer/index.ts | 3 + .../layout/components/Footer/src/Footer.vue | 24 + .../layout/components/LocaleDropdown/index.ts | 3 + .../LocaleDropdown/src/LocaleDropdown.vue | 52 + .../src/layout/components/Logo/index.ts | 3 + .../src/layout/components/Logo/src/Logo.vue | 88 + .../src/layout/components/Menu/index.ts | 3 + .../src/layout/components/Menu/src/Menu.vue | 290 +++ .../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 + .../layout/components/Message/src/Message.vue | 125 ++ .../src/layout/components/Screenfull/index.ts | 3 + .../components/Screenfull/src/Screenfull.vue | 32 + .../src/layout/components/Setting/index.ts | 3 + .../layout/components/Setting/src/Setting.vue | 299 +++ .../src/components/ColorRadioPicker.vue | 67 + .../src/components/InterfaceDisplay.vue | 224 ++ .../src/components/LayoutRadioPicker.vue | 172 ++ .../layout/components/SizeDropdown/index.ts | 3 + .../SizeDropdown/src/SizeDropdown.vue | 40 + .../src/layout/components/TabMenu/index.ts | 3 + .../layout/components/TabMenu/src/TabMenu.vue | 240 ++ .../layout/components/TabMenu/src/helper.ts | 51 + .../src/layout/components/TagsView/index.ts | 3 + .../components/TagsView/src/TagsView.vue | 585 +++++ .../layout/components/TagsView/src/helper.ts | 21 + .../layout/components/ThemeSwitch/index.ts | 3 + .../ThemeSwitch/src/ThemeSwitch.vue | 46 + .../src/layout/components/ToolHeader.vue | 90 + .../src/layout/components/UserInfo/index.ts | 3 + .../components/UserInfo/src/UserInfo.vue | 78 + .../src/layout/components/useRenderLayout.tsx | 306 +++ yunxi-ui-admin-vue3/src/locales/en.ts | 447 ++++ yunxi-ui-admin-vue3/src/locales/zh-CN.ts | 440 ++++ yunxi-ui-admin-vue3/src/main.ts | 72 + yunxi-ui-admin-vue3/src/permission.ts | 70 + .../src/plugins/animate.css/index.ts | 1 + .../src/plugins/echarts/index.ts | 47 + .../src/plugins/elementPlus/index.ts | 17 + .../src/plugins/formCreate/index.ts | 43 + .../src/plugins/svgIcon/index.ts | 3 + .../src/plugins/tongji/index.ts | 23 + .../src/plugins/unocss/index.ts | 1 + .../src/plugins/vueI18n/helper.ts | 3 + .../src/plugins/vueI18n/index.ts | 42 + yunxi-ui-admin-vue3/src/router/index.ts | 28 + .../src/router/modules/remaining.ts | 453 ++++ yunxi-ui-admin-vue3/src/store/index.ts | 10 + yunxi-ui-admin-vue3/src/store/modules/app.ts | 274 +++ yunxi-ui-admin-vue3/src/store/modules/dict.ts | 104 + .../src/store/modules/locale.ts | 59 + .../src/store/modules/permission.ts | 67 + .../src/store/modules/tagsView.ts | 140 ++ yunxi-ui-admin-vue3/src/store/modules/user.ts | 84 + .../src/styles/global.module.scss | 6 + yunxi-ui-admin-vue3/src/styles/index.scss | 35 + yunxi-ui-admin-vue3/src/styles/theme.scss | 6 + yunxi-ui-admin-vue3/src/styles/var.css | 66 + yunxi-ui-admin-vue3/src/styles/variables.scss | 4 + yunxi-ui-admin-vue3/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 + yunxi-ui-admin-vue3/src/types/form.d.ts | 44 + yunxi-ui-admin-vue3/src/types/icon.d.ts | 5 + yunxi-ui-admin-vue3/src/types/infoTip.d.ts | 4 + yunxi-ui-admin-vue3/src/types/layout.d.ts | 1 + .../src/types/localeDropdown.d.ts | 10 + yunxi-ui-admin-vue3/src/types/qrcode.d.ts | 9 + yunxi-ui-admin-vue3/src/types/table.d.ts | 44 + yunxi-ui-admin-vue3/src/types/theme.d.ts | 16 + yunxi-ui-admin-vue3/src/utils/Logger.ts | 100 + yunxi-ui-admin-vue3/src/utils/auth.ts | 92 + yunxi-ui-admin-vue3/src/utils/color.ts | 153 ++ yunxi-ui-admin-vue3/src/utils/constants.ts | 416 ++++ yunxi-ui-admin-vue3/src/utils/dict.ts | 186 ++ yunxi-ui-admin-vue3/src/utils/domUtils.ts | 289 +++ yunxi-ui-admin-vue3/src/utils/download.ts | 38 + yunxi-ui-admin-vue3/src/utils/filt.ts | 157 ++ yunxi-ui-admin-vue3/src/utils/formCreate.ts | 54 + yunxi-ui-admin-vue3/src/utils/formRules.ts | 7 + yunxi-ui-admin-vue3/src/utils/formatTime.ts | 339 +++ yunxi-ui-admin-vue3/src/utils/formatter.ts | 7 + yunxi-ui-admin-vue3/src/utils/index.ts | 235 ++ yunxi-ui-admin-vue3/src/utils/is.ts | 105 + yunxi-ui-admin-vue3/src/utils/jsencrypt.ts | 31 + yunxi-ui-admin-vue3/src/utils/permission.ts | 45 + yunxi-ui-admin-vue3/src/utils/propTypes.ts | 28 + yunxi-ui-admin-vue3/src/utils/routerHelper.ts | 241 ++ yunxi-ui-admin-vue3/src/utils/tree.ts | 400 ++++ yunxi-ui-admin-vue3/src/utils/tsxHelper.ts | 16 + yunxi-ui-admin-vue3/src/views/Error/403.vue | 8 + yunxi-ui-admin-vue3/src/views/Error/404.vue | 7 + yunxi-ui-admin-vue3/src/views/Error/500.vue | 7 + yunxi-ui-admin-vue3/src/views/Home/Index.vue | 381 ++++ yunxi-ui-admin-vue3/src/views/Home/Index2.vue | 319 +++ .../src/views/Home/echarts-data.ts | 308 +++ yunxi-ui-admin-vue3/src/views/Home/types.ts | 55 + yunxi-ui-admin-vue3/src/views/Login/Login.vue | 104 + .../src/views/Login/components/LoginForm.vue | 328 +++ .../views/Login/components/LoginFormTitle.vue | 26 + .../src/views/Login/components/MobileForm.vue | 225 ++ .../src/views/Login/components/QrCodeForm.vue | 30 + .../views/Login/components/RegisterForm.vue | 142 ++ .../src/views/Login/components/SSOLogin.vue | 199 ++ .../src/views/Login/components/index.ts | 8 + .../src/views/Login/components/useLogin.ts | 42 + .../src/views/Profile/Index.vue | 64 + .../views/Profile/components/BasicInfo.vue | 92 + .../views/Profile/components/ProfileUser.vue | 99 + .../src/views/Profile/components/ResetPwd.vue | 73 + .../views/Profile/components/UserAvatar.vue | 39 + .../views/Profile/components/UserSocial.vue | 94 + .../src/views/Profile/components/index.ts | 7 + .../src/views/Redirect/Redirect.vue | 28 + .../src/views/bpm/definition/index.vue | 174 ++ .../src/views/bpm/form/editor/index.vue | 119 + .../src/views/bpm/form/index.vue | 193 ++ .../src/views/bpm/group/UserGroupForm.vue | 132 ++ .../src/views/bpm/group/index.vue | 189 ++ .../src/views/bpm/model/ModelForm.vue | 230 ++ .../src/views/bpm/model/ModelImportForm.vue | 140 ++ .../src/views/bpm/model/editor/index.vue | 105 + .../src/views/bpm/model/index.vue | 404 ++++ .../src/views/bpm/oa/leave/create.vue | 89 + .../src/views/bpm/oa/leave/detail.vue | 51 + .../src/views/bpm/oa/leave/index.vue | 235 ++ .../bpm/processInstance/create/index.vue | 133 ++ .../detail/ProcessInstanceBpmnViewer.vue | 57 + .../detail/ProcessInstanceTaskList.vue | 100 + .../detail/TaskDelegateForm.vue | 86 + .../detail/TaskReturnDialogForm.vue | 90 + .../detail/TaskUpdateAssigneeForm.vue | 83 + .../bpm/processInstance/detail/index.vue | 288 +++ .../src/views/bpm/processInstance/index.vue | 250 +++ .../src/views/bpm/task/done/TaskDetail.vue | 51 + .../src/views/bpm/task/done/index.vue | 148 ++ .../src/views/bpm/task/todo/index.vue | 137 ++ .../bpm/taskAssignRule/TaskAssignRuleForm.vue | 250 +++ .../src/views/bpm/taskAssignRule/index.vue | 136 ++ .../infra/apiAccessLog/ApiAccessLogDetail.vue | 67 + .../src/views/infra/apiAccessLog/index.vue | 219 ++ .../infra/apiErrorLog/ApiErrorLogDetail.vue | 81 + .../src/views/infra/apiErrorLog/index.vue | 252 +++ .../src/views/infra/build/index.vue | 143 ++ .../src/views/infra/codegen/EditTable.vue | 83 + .../src/views/infra/codegen/ImportTable.vue | 151 ++ .../src/views/infra/codegen/PreviewCode.vue | 222 ++ .../codegen/components/BasicInfoForm.vue | 87 + .../codegen/components/ColumInfoForm.vue | 153 ++ .../codegen/components/GenerateInfoForm.vue | 391 ++++ .../views/infra/codegen/components/index.ts | 4 + .../src/views/infra/codegen/index.vue | 256 +++ .../src/views/infra/config/ConfigForm.vue | 131 ++ .../src/views/infra/config/index.vue | 228 ++ .../dataSourceConfig/DataSourceConfigForm.vue | 111 + .../views/infra/dataSourceConfig/index.vue | 106 + .../src/views/infra/dbDoc/index.vue | 59 + .../src/views/infra/druid/index.vue | 28 + .../src/views/infra/file/FileForm.vue | 104 + .../src/views/infra/file/index.vue | 164 ++ .../views/infra/fileConfig/FileConfigForm.vue | 195 ++ .../src/views/infra/fileConfig/index.vue | 217 ++ .../src/views/infra/job/JobDetail.vue | 73 + .../src/views/infra/job/JobForm.vue | 131 ++ .../src/views/infra/job/index.vue | 307 +++ .../views/infra/job/logger/JobLogDetail.vue | 59 + .../src/views/infra/job/logger/index.vue | 200 ++ .../src/views/infra/redis/index.vue | 268 +++ .../src/views/infra/server/index.vue | 30 + .../src/views/infra/skywalking/index.vue | 27 + .../src/views/infra/swagger/index.vue | 28 + .../src/views/infra/testDemo/index.vue | 4 + .../src/views/infra/webSocket/index.vue | 118 + .../views/mall/product/brand/BrandForm.vue | 123 ++ .../src/views/mall/product/brand/index.vue | 180 ++ .../mall/product/category/CategoryForm.vue | 141 ++ .../components/ProductCategorySelect.vue | 47 + .../src/views/mall/product/category/index.vue | 147 ++ .../mall/product/comment/CommentForm.vue | 193 ++ .../views/mall/product/comment/ReplyForm.vue | 76 + .../src/views/mall/product/comment/index.vue | 242 ++ .../mall/product/property/PropertyForm.vue | 96 + .../src/views/mall/product/property/index.vue | 175 ++ .../mall/product/property/value/ValueForm.vue | 105 + .../mall/product/property/value/index.vue | 163 ++ .../mall/product/spu/components/SkuList.vue | 534 +++++ .../product/spu/components/SkuTableSelect.vue | 95 + .../product/spu/components/SpuTableSelect.vue | 251 +++ .../mall/product/spu/components/index.ts | 54 + .../mall/product/spu/form/BasicInfoForm.vue | 375 ++++ .../mall/product/spu/form/DescriptionForm.vue | 109 + .../product/spu/form/OtherSettingsForm.vue | 183 ++ .../product/spu/form/ProductAttributes.vue | 119 + .../spu/form/ProductPropertyAddForm.vue | 101 + .../src/views/mall/product/spu/form/index.vue | 187 ++ .../views/mall/product/spu/form/spu.data.ts | 105 + .../src/views/mall/product/spu/index.vue | 438 ++++ .../bargain/activity/BargainActivityForm.vue | 233 ++ .../bargain/activity/bargainActivity.data.ts | 146 ++ .../mall/promotion/bargain/activity/index.vue | 231 ++ .../record/BargainRecordListDialog.vue | 90 + .../mall/promotion/bargain/record/index.vue | 195 ++ .../activity/CombinationActivityForm.vue | 187 ++ .../activity/combinationActivity.data.ts | 140 ++ .../promotion/combination/activity/index.vue | 234 ++ .../record/CombinationRecordListDialog.vue | 98 + .../promotion/combination/record/index.vue | 272 +++ .../promotion/components/SpuAndSkuList.vue | 112 + .../mall/promotion/components/SpuSelect.vue | 317 +++ .../views/mall/promotion/components/index.ts | 14 + .../coupon/components/CouponSendForm.vue | 162 ++ .../views/mall/promotion/coupon/formatter.ts | 44 + .../src/views/mall/promotion/coupon/index.vue | 201 ++ .../coupon/template/CouponTemplateForm.vue | 446 ++++ .../mall/promotion/coupon/template/index.vue | 265 +++ .../seckill/activity/SeckillActivityForm.vue | 196 ++ .../mall/promotion/seckill/activity/index.vue | 254 +++ .../seckill/activity/seckillActivity.data.ts | 163 ++ .../seckill/config/SeckillConfigForm.vue | 80 + .../mall/promotion/seckill/config/index.vue | 132 ++ .../seckill/config/seckillConfig.data.ts | 82 + .../views/mall/statistics/member/index.vue | 484 ++++ .../trade/components/TradeStatisticValue.vue | 36 + .../trade/components/TradeTrendValue.vue | 52 + .../src/views/mall/statistics/trade/index.vue | 428 ++++ .../mall/trade/afterSale/detail/index.vue | 354 +++ .../afterSale/form/AfterSaleDisagreeForm.vue | 70 + .../src/views/mall/trade/afterSale/index.vue | 267 +++ .../mall/trade/brokerage/record/index.vue | 169 ++ .../user/BrokerageOrderListDialog.vue | 152 ++ .../user/BrokerageUserListDialog.vue | 137 ++ .../brokerage/user/UpdateBindUserForm.vue | 127 ++ .../views/mall/trade/brokerage/user/index.vue | 305 +++ .../withdraw/BrokerageWithdrawRejectForm.vue | 73 + .../mall/trade/brokerage/withdraw/index.vue | 266 +++ .../src/views/mall/trade/config/index.vue | 306 +++ .../trade/delivery/express/ExpressForm.vue | 127 ++ .../mall/trade/delivery/express/index.vue | 187 ++ .../expressTemplate/ExpressTemplateForm.vue | 321 +++ .../trade/delivery/expressTemplate/index.vue | 163 ++ .../delivery/pickUpStore/PickUpStoreForm.vue | 273 +++ .../mall/trade/delivery/pickUpStore/index.vue | 188 ++ .../views/mall/trade/order/detail/index.vue | 421 ++++ .../trade/order/form/OrderDeliveryForm.vue | 99 + .../order/form/OrderUpdateAddressForm.vue | 98 + .../trade/order/form/OrderUpdatePriceForm.vue | 92 + .../order/form/OrderUpdateRemarkForm.vue | 70 + .../src/views/mall/trade/order/index.vue | 537 +++++ .../src/views/member/config/index.vue | 119 + .../src/views/member/group/GroupForm.vue | 112 + .../group/components/MemberGroupSelect.vue | 45 + .../src/views/member/group/index.vue | 174 ++ .../src/views/member/level/LevelForm.vue | 175 ++ .../level/components/MemberLevelSelect.vue | 45 + .../src/views/member/level/index.vue | 169 ++ .../src/views/member/point/record/index.vue | 159 ++ .../member/signin/config/SignInConfigForm.vue | 132 ++ .../src/views/member/signin/config/index.vue | 104 + .../src/views/member/signin/record/index.vue | 132 ++ .../src/views/member/tag/TagForm.vue | 91 + .../member/tag/components/MemberTagSelect.vue | 68 + .../src/views/member/tag/index.vue | 153 ++ .../src/views/member/user/UserForm.vue | 179 ++ .../views/member/user/UserLevelUpdateForm.vue | 101 + .../views/member/user/UserPointUpdateForm.vue | 128 ++ .../member/user/components/balance-list.vue | 14 + .../member/user/detail/UserAccountInfo.vue | 87 + .../member/user/detail/UserAddressList.vue | 54 + .../member/user/detail/UserBasicInfo.vue | 85 + .../member/user/detail/UserBrokerageList.vue | 125 ++ .../member/user/detail/UserCouponList.vue | 190 ++ .../user/detail/UserExperienceRecordList.vue | 158 ++ .../member/user/detail/UserOrderList.vue | 403 ++++ .../member/user/detail/UserPointList.vue | 152 ++ .../views/member/user/detail/UserSignList.vue | 135 ++ .../src/views/member/user/detail/index.vue | 132 ++ .../src/views/member/user/index.vue | 302 +++ .../src/views/mp/account/AccountForm.vue | 160 ++ .../src/views/mp/account/index.vue | 195 ++ .../mp/autoReply/components/ReplyForm.vue | 80 + .../mp/autoReply/components/ReplyTable.vue | 115 + .../views/mp/autoReply/components/types.ts | 7 + .../src/views/mp/autoReply/index.vue | 241 ++ .../mp/components/wx-account-select/index.ts | 3 + .../mp/components/wx-account-select/main.vue | 47 + .../views/mp/components/wx-location/index.ts | 3 + .../views/mp/components/wx-location/main.vue | 73 + .../mp/components/wx-material-select/index.ts | 6 + .../mp/components/wx-material-select/main.vue | 279 +++ .../mp/components/wx-material-select/types.ts | 11 + .../src/views/mp/components/wx-msg/card.scss | 116 + .../views/mp/components/wx-msg/comment.scss | 126 ++ .../mp/components/wx-msg/components/Msg.vue | 69 + .../components/wx-msg/components/MsgEvent.vue | 49 + .../components/wx-msg/components/MsgList.vue | 62 + .../src/views/mp/components/wx-msg/index.ts | 6 + .../src/views/mp/components/wx-msg/main.vue | 192 ++ .../src/views/mp/components/wx-msg/types.ts | 17 + .../src/views/mp/components/wx-music/index.ts | 3 + .../src/views/mp/components/wx-music/main.vue | 62 + .../src/views/mp/components/wx-news/index.ts | 3 + .../src/views/mp/components/wx-news/main.vue | 119 + .../wx-reply/components/TabImage.vue | 171 ++ .../wx-reply/components/TabMusic.vue | 116 + .../wx-reply/components/TabNews.vue | 76 + .../wx-reply/components/TabText.vue | 22 + .../wx-reply/components/TabVideo.vue | 128 ++ .../wx-reply/components/TabVoice.vue | 160 ++ .../components/wx-reply/components/types.ts | 54 + .../src/views/mp/components/wx-reply/index.ts | 7 + .../src/views/mp/components/wx-reply/main.vue | 208 ++ .../mp/components/wx-video-play/index.ts | 3 + .../mp/components/wx-video-play/main.vue | 73 + .../mp/components/wx-voice-play/index.ts | 3 + .../mp/components/wx-voice-play/main.vue | 105 + .../views/mp/draft/components/CoverSelect.vue | 166 ++ .../views/mp/draft/components/DraftTable.vue | 87 + .../views/mp/draft/components/NewsForm.vue | 304 +++ .../src/views/mp/draft/components/index.ts | 7 + .../src/views/mp/draft/components/types.ts | 40 + .../src/views/mp/draft/editor-config.ts | 75 + .../src/views/mp/draft/index.vue | 202 ++ .../src/views/mp/draft/mock.js | 151 ++ .../src/views/mp/freePublish/index.vue | 336 +++ .../src/views/mp/hooks/useUpload.ts | 50 + .../mp/material/components/ImageTable.vue | 83 + .../mp/material/components/UploadFile.vue | 74 + .../mp/material/components/UploadVideo.vue | 126 ++ .../mp/material/components/VideoTable.vue | 59 + .../mp/material/components/VoiceTable.vue | 51 + .../views/mp/material/components/upload.ts | 31 + .../src/views/mp/material/index.vue | 154 ++ .../views/mp/menu/assets/iphone_backImg.png | Bin 0 -> 34272 bytes .../src/views/mp/menu/assets/menu_foot.png | Bin 0 -> 1348 bytes .../src/views/mp/menu/assets/menu_head.png | Bin 0 -> 12673 bytes .../views/mp/menu/components/MenuEditor.vue | 244 ++ .../mp/menu/components/MenuPreviewer.vue | 226 ++ .../views/mp/menu/components/menuOptions.ts | 42 + .../src/views/mp/menu/components/types.ts | 73 + .../src/views/mp/menu/index.vue | 401 ++++ .../src/views/mp/message/MessageTable.vue | 145 ++ .../src/views/mp/message/index.vue | 152 ++ .../src/views/mp/statistics/index.vue | 368 ++++ .../src/views/mp/tag/TagForm.vue | 98 + .../src/views/mp/tag/index.vue | 154 ++ .../src/views/mp/user/UserForm.vue | 102 + .../src/views/mp/user/index.vue | 181 ++ .../src/views/pay/app/components/AppForm.vue | 130 ++ .../components/channel/AlipayChannelForm.vue | 316 +++ .../components/channel/MockChannelForm.vue | 122 + .../components/channel/WeixinChannelForm.vue | 342 +++ .../src/views/pay/app/index.vue | 429 ++++ .../src/views/pay/cashier/index.vue | 482 ++++ .../src/views/pay/demo/index.vue | 240 ++ .../src/views/pay/notify/NotifyDetail.vue | 86 + .../src/views/pay/notify/index.vue | 224 ++ .../src/views/pay/order/OrderDetail.vue | 111 + .../src/views/pay/order/index.vue | 273 +++ .../src/views/pay/refund/RefundDetail.vue | 93 + .../src/views/pay/refund/index.vue | 298 +++ .../src/views/report/goview/index.vue | 10 + .../src/views/report/jmreport/index.vue | 13 + .../src/views/system/area/AreaForm.vue | 72 + .../src/views/system/area/index.vue | 75 + .../src/views/system/dept/DeptForm.vue | 173 ++ .../src/views/system/dept/index.vue | 190 ++ .../src/views/system/dict/DictTypeForm.vue | 124 ++ .../views/system/dict/data/DictDataForm.vue | 183 ++ .../src/views/system/dict/data/index.vue | 210 ++ .../src/views/system/dict/index.vue | 231 ++ .../views/system/errorCode/ErrorCodeForm.vue | 112 + .../src/views/system/errorCode/index.vue | 228 ++ .../views/system/loginlog/LoginLogDetail.vue | 51 + .../src/views/system/loginlog/index.vue | 180 ++ .../system/mail/account/MailAccountDetail.vue | 28 + .../system/mail/account/MailAccountForm.vue | 68 + .../views/system/mail/account/account.data.ts | 76 + .../src/views/system/mail/account/index.vue | 106 + .../views/system/mail/log/MailLogDetail.vue | 33 + .../src/views/system/mail/log/index.vue | 63 + .../src/views/system/mail/log/log.data.ts | 133 ++ .../system/mail/template/MailTemplateForm.vue | 74 + .../mail/template/MailTemplateSendForm.vue | 115 + .../src/views/system/mail/template/index.vue | 107 + .../system/mail/template/template.data.ts | 113 + .../src/views/system/menu/MenuForm.vue | 256 +++ .../src/views/system/menu/index.vue | 214 ++ .../src/views/system/notice/NoticeForm.vue | 132 ++ .../src/views/system/notice/index.vue | 175 ++ .../notify/message/NotifyMessageDetail.vue | 66 + .../src/views/system/notify/message/index.vue | 212 ++ .../notify/my/MyNotifyMessageDetail.vue | 48 + .../src/views/system/notify/my/index.vue | 218 ++ .../notify/template/NotifyTemplateForm.vue | 141 ++ .../template/NotifyTemplateSendForm.vue | 129 ++ .../views/system/notify/template/index.vue | 235 ++ .../views/system/oauth2/client/ClientForm.vue | 261 +++ .../src/views/system/oauth2/client/index.vue | 191 ++ .../src/views/system/oauth2/token/index.vue | 164 ++ .../system/operatelog/OperateLogDetail.vue | 82 + .../src/views/system/operatelog/index.vue | 213 ++ .../src/views/system/post/PostForm.vue | 125 ++ .../src/views/system/post/index.vue | 201 ++ .../views/system/role/RoleAssignMenuForm.vue | 160 ++ .../system/role/RoleDataPermissionForm.vue | 167 ++ .../src/views/system/role/RoleForm.vue | 126 ++ .../src/views/system/role/index.vue | 265 +++ .../sensitiveWord/SensitiveWordForm.vue | 131 ++ .../sensitiveWord/SensitiveWordTestForm.vue | 91 + .../src/views/system/sensitiveWord/index.vue | 253 +++ .../system/sms/channel/SmsChannelForm.vue | 144 ++ .../src/views/system/sms/channel/index.vue | 202 ++ .../src/views/system/sms/log/SmsLogDetail.vue | 89 + .../src/views/system/sms/log/index.vue | 268 +++ .../system/sms/template/SmsTemplateForm.vue | 163 ++ .../sms/template/SmsTemplateSendForm.vue | 120 + .../src/views/system/sms/template/index.vue | 316 +++ .../src/views/system/tenant/TenantForm.vue | 178 ++ .../src/views/system/tenant/index.vue | 266 +++ .../tenantPackage/TenantPackageForm.vue | 194 ++ .../src/views/system/tenantPackage/index.vue | 180 ++ .../src/views/system/user/DeptTree.vue | 63 + .../views/system/user/UserAssignRoleForm.vue | 96 + .../src/views/system/user/UserForm.vue | 218 ++ .../src/views/system/user/UserImportForm.vue | 133 ++ .../src/views/system/user/index.vue | 362 +++ yunxi-ui-admin-vue3/stylelint.config.js | 233 ++ yunxi-ui-admin-vue3/tsconfig.json | 44 + yunxi-ui-admin-vue3/types/components.d.ts | 8 + yunxi-ui-admin-vue3/types/custom-types.d.ts | 27 + yunxi-ui-admin-vue3/types/env.d.ts | 32 + yunxi-ui-admin-vue3/types/global.d.ts | 50 + yunxi-ui-admin-vue3/types/router.d.ts | 81 + yunxi-ui-admin-vue3/uno.config.ts | 105 + yunxi-ui-admin-vue3/vite.config.ts | 81 + 775 files changed, 87005 insertions(+) create mode 100644 yunxi-ui-admin-vue3/.editorconfig create mode 100644 yunxi-ui-admin-vue3/.env create mode 100644 yunxi-ui-admin-vue3/.env.base create mode 100644 yunxi-ui-admin-vue3/.env.dev create mode 100644 yunxi-ui-admin-vue3/.env.front create mode 100644 yunxi-ui-admin-vue3/.env.pro create mode 100644 yunxi-ui-admin-vue3/.env.stage create mode 100644 yunxi-ui-admin-vue3/.env.static create mode 100644 yunxi-ui-admin-vue3/.eslintignore create mode 100644 yunxi-ui-admin-vue3/.eslintrc-auto-import.json create mode 100644 yunxi-ui-admin-vue3/.eslintrc.js create mode 100644 yunxi-ui-admin-vue3/.gitignore create mode 100644 yunxi-ui-admin-vue3/.prettierignore create mode 100644 yunxi-ui-admin-vue3/.stylelintignore create mode 100644 yunxi-ui-admin-vue3/LICENSE create mode 100644 yunxi-ui-admin-vue3/README.md create mode 100644 yunxi-ui-admin-vue3/build/vite/index.ts create mode 100644 yunxi-ui-admin-vue3/build/vite/optimize.ts create mode 100644 yunxi-ui-admin-vue3/index.html create mode 100644 yunxi-ui-admin-vue3/package.json create mode 100644 yunxi-ui-admin-vue3/postcss.config.js create mode 100644 yunxi-ui-admin-vue3/prettier.config.js create mode 100644 yunxi-ui-admin-vue3/public/favicon.ico create mode 100644 yunxi-ui-admin-vue3/public/home.png create mode 100644 yunxi-ui-admin-vue3/public/logo.gif create mode 100644 yunxi-ui-admin-vue3/src/App.vue create mode 100644 yunxi-ui-admin-vue3/src/api/bpm/activity/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/bpm/definition/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/bpm/form/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/bpm/leave/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/bpm/model/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/bpm/processInstance/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/bpm/task/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/bpm/taskAssignRule/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/bpm/userGroup/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/apiAccessLog/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/apiErrorLog/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/codegen/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/config/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/dataSourceConfig/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/dbDoc/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/file/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/fileConfig/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/job/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/jobLog/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/redis/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/infra/redis/types.ts create mode 100644 yunxi-ui-admin-vue3/src/api/login/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/login/oauth2/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/login/types.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/product/brand.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/product/category.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/product/comment.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/product/property.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/product/spu.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainActivity.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainHelp.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainRecord.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/promotion/combination/combinationActivity.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/promotion/combination/combinationRecord.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/promotion/coupon/coupon.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/promotion/coupon/couponTemplate.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/promotion/seckill/seckillActivity.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/promotion/seckill/seckillConfig.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/statistics/member.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/statistics/trade.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/trade/afterSale/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/record/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/user/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/withdraw/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/trade/config/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/trade/delivery/express/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/trade/delivery/expressTemplate/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/trade/delivery/pickUpStore/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mall/trade/order/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/member/address/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/member/config/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/member/experience-record/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/member/group/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/member/level/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/member/point/record/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/member/signin/config/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/member/signin/record/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/member/tag/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/member/user/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mp/account/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mp/autoReply/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mp/draft/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mp/freePublish/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mp/material/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mp/menu/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mp/message/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mp/statistics/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mp/tag/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/mp/user/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/pay/app/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/pay/channel/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/pay/demo/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/pay/notify/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/pay/order/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/pay/refund/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/pay/wallet/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/area/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/dept/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/dict/dict.data.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/dict/dict.type.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/errorCode/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/loginLog/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/mail/account/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/mail/log/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/mail/template/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/menu/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/notice/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/notify/message/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/notify/template/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/oauth2/client.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/oauth2/token.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/operatelog/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/permission/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/post/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/role/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/sensitiveWord/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/sms/smsChannel/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/sms/smsLog/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/sms/smsTemplate/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/tenant/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/tenantPackage/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/user/index.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/user/profile.ts create mode 100644 yunxi-ui-admin-vue3/src/api/system/user/socialUser.ts create mode 100644 yunxi-ui-admin-vue3/src/assets/imgs/avatar.gif create mode 100644 yunxi-ui-admin-vue3/src/assets/imgs/avatar.jpg create mode 100644 yunxi-ui-admin-vue3/src/assets/imgs/logo.png create mode 100644 yunxi-ui-admin-vue3/src/assets/imgs/profile.jpg create mode 100644 yunxi-ui-admin-vue3/src/assets/imgs/wechat.png create mode 100644 yunxi-ui-admin-vue3/src/assets/map/json/china.json create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/403.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/404.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/500.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/icon.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/login-bg.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/login-box-bg.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/member_balance.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/member_expenditure_balance.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/member_level.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/member_point.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/member_recharge_balance.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/message.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/money.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_app.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_bar.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_pc.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_qr.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_wap.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/mock.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_app.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_bar.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_lite.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_native.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_pub.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/peoples.svg create mode 100644 yunxi-ui-admin-vue3/src/assets/svgs/shopping.svg create mode 100644 yunxi-ui-admin-vue3/src/components/Backtop/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Backtop/src/Backtop.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Card/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Card/src/CardTitle.vue create mode 100644 yunxi-ui-admin-vue3/src/components/ConfigGlobal/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/ConfigGlobal/src/ConfigGlobal.vue create mode 100644 yunxi-ui-admin-vue3/src/components/ContentDetailWrap/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/ContentDetailWrap/src/ContentDetailWrap.vue create mode 100644 yunxi-ui-admin-vue3/src/components/ContentWrap/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/ContentWrap/src/ContentWrap.vue create mode 100644 yunxi-ui-admin-vue3/src/components/CountTo/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/CountTo/src/CountTo.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Crontab/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Crontab/src/Crontab.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Cropper/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Cropper/src/CopperModal.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Cropper/src/Cropper.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Cropper/src/CropperAvatar.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Cropper/src/types.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Descriptions/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Descriptions/src/Descriptions.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Descriptions/src/DescriptionsItemLabel.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Dialog/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Dialog/src/Dialog.vue create mode 100644 yunxi-ui-admin-vue3/src/components/DictTag/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/DictTag/src/DictTag.vue create mode 100644 yunxi-ui-admin-vue3/src/components/DocAlert/index.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Echart/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Echart/src/Echart.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Editor/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Editor/src/Editor.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Error/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Error/src/Error.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Form/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Form/src/Form.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Form/src/componentMap.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Form/src/components/useRenderCheckbox.tsx create mode 100644 yunxi-ui-admin-vue3/src/components/Form/src/components/useRenderRadio.tsx create mode 100644 yunxi-ui-admin-vue3/src/components/Form/src/components/useRenderSelect.tsx create mode 100644 yunxi-ui-admin-vue3/src/components/Form/src/helper.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Form/src/types.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Highlight/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Highlight/src/Highlight.vue create mode 100644 yunxi-ui-admin-vue3/src/components/IFrame/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/IFrame/src/IFrame.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Icon/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Icon/src/Icon.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Icon/src/IconSelect.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Icon/src/data.ts create mode 100644 yunxi-ui-admin-vue3/src/components/ImageViewer/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/ImageViewer/src/ImageViewer.vue create mode 100644 yunxi-ui-admin-vue3/src/components/ImageViewer/src/types.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Infotip/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Infotip/src/Infotip.vue create mode 100644 yunxi-ui-admin-vue3/src/components/InputPassword/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/InputPassword/src/InputPassword.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Pagination/index.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Qrcode/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Qrcode/src/Qrcode.vue create mode 100644 yunxi-ui-admin-vue3/src/components/RouterSearch/index.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Search/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Search/src/Search.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Sticky/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Sticky/src/Sticky.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Table/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Table/src/Table.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Table/src/helper.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Table/src/types.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Tooltip/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Tooltip/src/Tooltip.vue create mode 100644 yunxi-ui-admin-vue3/src/components/UploadFile/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadFile.vue create mode 100644 yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadImg.vue create mode 100644 yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadImgs.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Verifition/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Verifition/src/Verify.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/VerifyPoints.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue create mode 100644 yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Verifition/src/utils/ase.ts create mode 100644 yunxi-ui-admin-vue3/src/components/Verifition/src/utils/util.ts create mode 100644 yunxi-ui-admin-vue3/src/components/XButton/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/XButton/src/XButton.vue create mode 100644 yunxi-ui-admin-vue3/src/components/XButton/src/XTextButton.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/index2.ts create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/contentPadProvider.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/index.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/defaultEmpty.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/activitiDescriptor.json create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/camundaDescriptor.json create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/activitiExtension.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/index.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/extension.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/index.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/flowableExtension.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/index.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/palette/index.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/translate/customTranslate.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/translate/zh.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/index.ts create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/palette/ProcessPalette.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/form/ElementForm.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/index.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/template.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/utilSelf.ts create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/multi-instance/ElementMultiInstance.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/ElementTask.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/element-variables.scss create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/index.scss create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/process-designer.scss create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/process-panel.scss create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/utils.ts create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/highlight/index.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/custom-renderer/CustomRenderer.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/custom-renderer/index.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/rules/CustomRules.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/rules/index.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/translations.ts create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/utils/directive/clickOutSide.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/utils/index.js create mode 100644 yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/utils/xml2json.js create mode 100644 yunxi-ui-admin-vue3/src/components/index.ts create mode 100644 yunxi-ui-admin-vue3/src/config/axios/config.ts create mode 100644 yunxi-ui-admin-vue3/src/config/axios/errorCode.ts create mode 100644 yunxi-ui-admin-vue3/src/config/axios/index.ts create mode 100644 yunxi-ui-admin-vue3/src/config/axios/service.ts create mode 100644 yunxi-ui-admin-vue3/src/directives/index.ts create mode 100644 yunxi-ui-admin-vue3/src/directives/permission/hasPermi.ts create mode 100644 yunxi-ui-admin-vue3/src/directives/permission/hasRole.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/event/useScrollTo.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useCache.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useConfigGlobal.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useCrudSchemas.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useDesign.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useEmitt.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useForm.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useI18n.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useIcon.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useIntro.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useLocale.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useMessage.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useNProgress.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/usePageLoading.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useTable.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useTimeAgo.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useTitle.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useValidator.ts create mode 100644 yunxi-ui-admin-vue3/src/hooks/web/useWatermark.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/Layout.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/AppView.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/src/Breadcrumb.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/src/helper.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Collapse/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Collapse/src/Collapse.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/ContextMenu/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/ContextMenu/src/ContextMenu.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Footer/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Footer/src/Footer.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/LocaleDropdown/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/LocaleDropdown/src/LocaleDropdown.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Logo/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Logo/src/Logo.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Menu/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Menu/src/Menu.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Menu/src/components/useRenderMenuItem.tsx create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Menu/src/components/useRenderMenuTitle.tsx create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Menu/src/helper.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Message/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Message/src/Message.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Screenfull/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Screenfull/src/Screenfull.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Setting/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Setting/src/Setting.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/ColorRadioPicker.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/InterfaceDisplay.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/LayoutRadioPicker.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/SizeDropdown/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/SizeDropdown/src/SizeDropdown.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/TabMenu/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/TabMenu/src/TabMenu.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/TabMenu/src/helper.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/TagsView/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/TagsView/src/TagsView.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/TagsView/src/helper.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/ThemeSwitch/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/ThemeSwitch/src/ThemeSwitch.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/ToolHeader.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/UserInfo/index.ts create mode 100644 yunxi-ui-admin-vue3/src/layout/components/UserInfo/src/UserInfo.vue create mode 100644 yunxi-ui-admin-vue3/src/layout/components/useRenderLayout.tsx create mode 100644 yunxi-ui-admin-vue3/src/locales/en.ts create mode 100644 yunxi-ui-admin-vue3/src/locales/zh-CN.ts create mode 100644 yunxi-ui-admin-vue3/src/main.ts create mode 100644 yunxi-ui-admin-vue3/src/permission.ts create mode 100644 yunxi-ui-admin-vue3/src/plugins/animate.css/index.ts create mode 100644 yunxi-ui-admin-vue3/src/plugins/echarts/index.ts create mode 100644 yunxi-ui-admin-vue3/src/plugins/elementPlus/index.ts create mode 100644 yunxi-ui-admin-vue3/src/plugins/formCreate/index.ts create mode 100644 yunxi-ui-admin-vue3/src/plugins/svgIcon/index.ts create mode 100644 yunxi-ui-admin-vue3/src/plugins/tongji/index.ts create mode 100644 yunxi-ui-admin-vue3/src/plugins/unocss/index.ts create mode 100644 yunxi-ui-admin-vue3/src/plugins/vueI18n/helper.ts create mode 100644 yunxi-ui-admin-vue3/src/plugins/vueI18n/index.ts create mode 100644 yunxi-ui-admin-vue3/src/router/index.ts create mode 100644 yunxi-ui-admin-vue3/src/router/modules/remaining.ts create mode 100644 yunxi-ui-admin-vue3/src/store/index.ts create mode 100644 yunxi-ui-admin-vue3/src/store/modules/app.ts create mode 100644 yunxi-ui-admin-vue3/src/store/modules/dict.ts create mode 100644 yunxi-ui-admin-vue3/src/store/modules/locale.ts create mode 100644 yunxi-ui-admin-vue3/src/store/modules/permission.ts create mode 100644 yunxi-ui-admin-vue3/src/store/modules/tagsView.ts create mode 100644 yunxi-ui-admin-vue3/src/store/modules/user.ts create mode 100644 yunxi-ui-admin-vue3/src/styles/global.module.scss create mode 100644 yunxi-ui-admin-vue3/src/styles/index.scss create mode 100644 yunxi-ui-admin-vue3/src/styles/theme.scss create mode 100644 yunxi-ui-admin-vue3/src/styles/var.css create mode 100644 yunxi-ui-admin-vue3/src/styles/variables.scss create mode 100644 yunxi-ui-admin-vue3/src/types/components.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/configGlobal.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/contextMenu.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/descriptions.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/elementPlus.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/form.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/icon.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/infoTip.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/layout.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/localeDropdown.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/qrcode.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/table.d.ts create mode 100644 yunxi-ui-admin-vue3/src/types/theme.d.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/Logger.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/auth.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/color.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/constants.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/dict.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/domUtils.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/download.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/filt.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/formCreate.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/formRules.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/formatTime.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/formatter.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/index.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/is.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/jsencrypt.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/permission.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/propTypes.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/routerHelper.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/tree.ts create mode 100644 yunxi-ui-admin-vue3/src/utils/tsxHelper.ts create mode 100644 yunxi-ui-admin-vue3/src/views/Error/403.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Error/404.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Error/500.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Home/Index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Home/Index2.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Home/echarts-data.ts create mode 100644 yunxi-ui-admin-vue3/src/views/Home/types.ts create mode 100644 yunxi-ui-admin-vue3/src/views/Login/Login.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Login/components/LoginForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Login/components/LoginFormTitle.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Login/components/MobileForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Login/components/QrCodeForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Login/components/RegisterForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Login/components/SSOLogin.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Login/components/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/Login/components/useLogin.ts create mode 100644 yunxi-ui-admin-vue3/src/views/Profile/Index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Profile/components/BasicInfo.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Profile/components/ProfileUser.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Profile/components/ResetPwd.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Profile/components/UserAvatar.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Profile/components/UserSocial.vue create mode 100644 yunxi-ui-admin-vue3/src/views/Profile/components/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/Redirect/Redirect.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/definition/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/form/editor/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/form/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/group/UserGroupForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/group/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/model/ModelForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/model/ModelImportForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/model/editor/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/model/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/oa/leave/create.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/oa/leave/detail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/oa/leave/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/processInstance/create/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/ProcessInstanceBpmnViewer.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/ProcessInstanceTaskList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskDelegateForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskReturnDialogForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskUpdateAssigneeForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/processInstance/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/task/done/TaskDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/task/done/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/task/todo/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/taskAssignRule/TaskAssignRuleForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/bpm/taskAssignRule/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/apiAccessLog/ApiAccessLogDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/apiAccessLog/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/apiErrorLog/ApiErrorLogDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/apiErrorLog/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/build/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/codegen/EditTable.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/codegen/ImportTable.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/codegen/PreviewCode.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/codegen/components/BasicInfoForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/codegen/components/ColumInfoForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/codegen/components/GenerateInfoForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/codegen/components/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/infra/codegen/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/config/ConfigForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/config/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/dataSourceConfig/DataSourceConfigForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/dataSourceConfig/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/dbDoc/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/druid/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/file/FileForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/file/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/fileConfig/FileConfigForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/fileConfig/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/job/JobDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/job/JobForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/job/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/job/logger/JobLogDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/job/logger/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/redis/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/server/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/skywalking/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/swagger/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/testDemo/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/infra/webSocket/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/brand/BrandForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/brand/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/category/CategoryForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/category/components/ProductCategorySelect.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/category/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/comment/CommentForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/comment/ReplyForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/comment/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/property/PropertyForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/property/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/property/value/ValueForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/property/value/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/components/SkuList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/components/SkuTableSelect.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/components/SpuTableSelect.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/components/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/form/BasicInfoForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/form/DescriptionForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/form/OtherSettingsForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/form/ProductAttributes.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/form/ProductPropertyAddForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/form/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/form/spu.data.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mall/product/spu/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/bargain/activity/BargainActivityForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/bargain/activity/bargainActivity.data.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/bargain/activity/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/bargain/record/BargainRecordListDialog.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/bargain/record/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/combination/activity/CombinationActivityForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/combination/activity/combinationActivity.data.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/combination/activity/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/combination/record/CombinationRecordListDialog.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/combination/record/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/components/SpuAndSkuList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/components/SpuSelect.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/components/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/coupon/components/CouponSendForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/coupon/formatter.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/coupon/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/coupon/template/CouponTemplateForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/coupon/template/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/seckill/activity/SeckillActivityForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/seckill/activity/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/seckill/activity/seckillActivity.data.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/seckill/config/SeckillConfigForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/seckill/config/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/promotion/seckill/config/seckillConfig.data.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mall/statistics/member/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/statistics/trade/components/TradeStatisticValue.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/statistics/trade/components/TradeTrendValue.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/statistics/trade/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/afterSale/detail/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/afterSale/form/AfterSaleDisagreeForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/afterSale/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/brokerage/record/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/brokerage/user/BrokerageOrderListDialog.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/brokerage/user/BrokerageUserListDialog.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/brokerage/user/UpdateBindUserForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/brokerage/user/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/brokerage/withdraw/BrokerageWithdrawRejectForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/brokerage/withdraw/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/config/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/delivery/express/ExpressForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/delivery/express/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/delivery/expressTemplate/ExpressTemplateForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/delivery/expressTemplate/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/delivery/pickUpStore/PickUpStoreForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/delivery/pickUpStore/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/order/detail/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderDeliveryForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdateAddressForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdatePriceForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdateRemarkForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mall/trade/order/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/config/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/group/GroupForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/group/components/MemberGroupSelect.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/group/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/level/LevelForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/level/components/MemberLevelSelect.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/level/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/point/record/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/signin/config/SignInConfigForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/signin/config/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/signin/record/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/tag/TagForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/tag/components/MemberTagSelect.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/tag/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/UserForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/UserLevelUpdateForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/UserPointUpdateForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/components/balance-list.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/detail/UserAccountInfo.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/detail/UserAddressList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/detail/UserBasicInfo.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/detail/UserBrokerageList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/detail/UserCouponList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/detail/UserExperienceRecordList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/detail/UserOrderList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/detail/UserPointList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/detail/UserSignList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/detail/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/member/user/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/account/AccountForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/account/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/autoReply/components/ReplyForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/autoReply/components/ReplyTable.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/autoReply/components/types.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/autoReply/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-account-select/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-account-select/main.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-location/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-location/main.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/main.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/types.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/card.scss create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/comment.scss create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/Msg.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/MsgEvent.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/MsgList.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/main.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/types.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-music/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-music/main.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-news/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-news/main.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabImage.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabMusic.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabNews.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabText.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabVideo.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabVoice.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/types.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/main.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-video-play/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-video-play/main.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-voice-play/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/components/wx-voice-play/main.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/draft/components/CoverSelect.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/draft/components/DraftTable.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/draft/components/NewsForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/draft/components/index.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/draft/components/types.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/draft/editor-config.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/draft/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/draft/mock.js create mode 100644 yunxi-ui-admin-vue3/src/views/mp/freePublish/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/hooks/useUpload.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/material/components/ImageTable.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/material/components/UploadFile.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/material/components/UploadVideo.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/material/components/VideoTable.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/material/components/VoiceTable.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/material/components/upload.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/material/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/menu/assets/iphone_backImg.png create mode 100644 yunxi-ui-admin-vue3/src/views/mp/menu/assets/menu_foot.png create mode 100644 yunxi-ui-admin-vue3/src/views/mp/menu/assets/menu_head.png create mode 100644 yunxi-ui-admin-vue3/src/views/mp/menu/components/MenuEditor.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/menu/components/MenuPreviewer.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/menu/components/menuOptions.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/menu/components/types.ts create mode 100644 yunxi-ui-admin-vue3/src/views/mp/menu/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/message/MessageTable.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/message/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/statistics/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/tag/TagForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/tag/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/user/UserForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/mp/user/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/app/components/AppForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/app/components/channel/AlipayChannelForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/app/components/channel/MockChannelForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/app/components/channel/WeixinChannelForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/app/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/cashier/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/demo/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/notify/NotifyDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/notify/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/order/OrderDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/order/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/refund/RefundDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/pay/refund/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/report/goview/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/report/jmreport/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/area/AreaForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/area/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/dept/DeptForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/dept/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/dict/DictTypeForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/dict/data/DictDataForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/dict/data/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/dict/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/errorCode/ErrorCodeForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/errorCode/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/loginlog/LoginLogDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/loginlog/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/account/MailAccountDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/account/MailAccountForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/account/account.data.ts create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/account/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/log/MailLogDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/log/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/log/log.data.ts create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/template/MailTemplateForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/template/MailTemplateSendForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/template/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/mail/template/template.data.ts create mode 100644 yunxi-ui-admin-vue3/src/views/system/menu/MenuForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/menu/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/notice/NoticeForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/notice/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/notify/message/NotifyMessageDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/notify/message/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/notify/my/MyNotifyMessageDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/notify/my/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/notify/template/NotifyTemplateForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/notify/template/NotifyTemplateSendForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/notify/template/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/oauth2/client/ClientForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/oauth2/client/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/oauth2/token/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/operatelog/OperateLogDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/operatelog/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/post/PostForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/post/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/role/RoleAssignMenuForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/role/RoleDataPermissionForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/role/RoleForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/role/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/sensitiveWord/SensitiveWordForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/sensitiveWord/SensitiveWordTestForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/sensitiveWord/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/sms/channel/SmsChannelForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/sms/channel/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/sms/log/SmsLogDetail.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/sms/log/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/sms/template/SmsTemplateForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/sms/template/SmsTemplateSendForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/sms/template/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/tenant/TenantForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/tenant/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/tenantPackage/TenantPackageForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/tenantPackage/index.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/user/DeptTree.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/user/UserAssignRoleForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/user/UserForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/user/UserImportForm.vue create mode 100644 yunxi-ui-admin-vue3/src/views/system/user/index.vue create mode 100644 yunxi-ui-admin-vue3/stylelint.config.js create mode 100644 yunxi-ui-admin-vue3/tsconfig.json create mode 100644 yunxi-ui-admin-vue3/types/components.d.ts create mode 100644 yunxi-ui-admin-vue3/types/custom-types.d.ts create mode 100644 yunxi-ui-admin-vue3/types/env.d.ts create mode 100644 yunxi-ui-admin-vue3/types/global.d.ts create mode 100644 yunxi-ui-admin-vue3/types/router.d.ts create mode 100644 yunxi-ui-admin-vue3/uno.config.ts create mode 100644 yunxi-ui-admin-vue3/vite.config.ts diff --git a/yunxi-ui-admin-vue3/.editorconfig b/yunxi-ui-admin-vue3/.editorconfig new file mode 100644 index 00000000..79a12ffa --- /dev/null +++ b/yunxi-ui-admin-vue3/.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/yunxi-ui-admin-vue3/.env b/yunxi-ui-admin-vue3/.env new file mode 100644 index 00000000..3c5303b5 --- /dev/null +++ b/yunxi-ui-admin-vue3/.env @@ -0,0 +1,17 @@ +# 标题 +VITE_APP_TITLE=芋道管理系统 + +# 项目本地运行端口号 +VITE_PORT=80 + +# open 运行 npm run dev 时自动打开浏览器 +VITE_OPEN=true + +# 租户开关 +VITE_APP_TENANT_ENABLE=true + +# 验证码的开关 +VITE_APP_CAPTCHA_ENABLE=true + +# 百度统计 +VITE_APP_BAIDU_CODE = a1ff8825baa73c3a78eb96aa40325abc diff --git a/yunxi-ui-admin-vue3/.env.base b/yunxi-ui-admin-vue3/.env.base new file mode 100644 index 00000000..fdb26d85 --- /dev/null +++ b/yunxi-ui-admin-vue3/.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/yunxi-ui-admin-vue3/.env.dev b/yunxi-ui-admin-vue3/.env.dev new file mode 100644 index 00000000..9200ece2 --- /dev/null +++ b/yunxi-ui-admin-vue3/.env.dev @@ -0,0 +1,31 @@ +# 开发环境 +NODE_ENV=development + +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=/ + +# 是否删除debugger +VITE_DROP_DEBUGGER=true + +# 是否删除console.log +VITE_DROP_CONSOLE=false + +# 是否sourcemap +VITE_SOURCEMAP=false + +# 输出路径 +VITE_OUT_DIR=dist diff --git a/yunxi-ui-admin-vue3/.env.front b/yunxi-ui-admin-vue3/.env.front new file mode 100644 index 00000000..1629ff9f --- /dev/null +++ b/yunxi-ui-admin-vue3/.env.front @@ -0,0 +1,34 @@ +# 本地开发环境 +NODE_ENV=development + +VITE_DEV=true + +# 请求路径 +VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn' + +# 上传路径 +VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload' + +# 接口前缀 +VITE_API_BASEPATH=/dev-api + +# 接口地址 +VITE_API_URL=/admin-api + +# 打包路径 +VITE_BASE_PATH=/ + +# 项目本地运行端口号, 与.vscode/launch.json配合 +VITE_PORT=80 + +# 是否删除debugger +VITE_DROP_DEBUGGER=false + +# 是否删除console.log +VITE_DROP_CONSOLE=false + +# 是否sourcemap +VITE_SOURCEMAP=true + +# 验证码的开关 +VITE_APP_CAPTCHA_ENABLE=false diff --git a/yunxi-ui-admin-vue3/.env.pro b/yunxi-ui-admin-vue3/.env.pro new file mode 100644 index 00000000..8348e02e --- /dev/null +++ b/yunxi-ui-admin-vue3/.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/yunxi-ui-admin-vue3/.env.stage b/yunxi-ui-admin-vue3/.env.stage new file mode 100644 index 00000000..d7157fbb --- /dev/null +++ b/yunxi-ui-admin-vue3/.env.stage @@ -0,0 +1,31 @@ +# 生产环境 +NODE_ENV=production + +VITE_DEV=false + +# 请求路径 +VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn' + +# 上传路径 +VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/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='http://static-vue3.yudao.iocoder.cn/' + +# 输出路径 +VITE_OUT_DIR=dist-stage diff --git a/yunxi-ui-admin-vue3/.env.static b/yunxi-ui-admin-vue3/.env.static new file mode 100644 index 00000000..034a7f4d --- /dev/null +++ b/yunxi-ui-admin-vue3/.env.static @@ -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=/admin-ui-vue3/ + +# 输出路径 +VITE_OUT_DIR=dist-dev diff --git a/yunxi-ui-admin-vue3/.eslintignore b/yunxi-ui-admin-vue3/.eslintignore new file mode 100644 index 00000000..1e85c0fb --- /dev/null +++ b/yunxi-ui-admin-vue3/.eslintignore @@ -0,0 +1,8 @@ +/build/ +/config/ +/dist/ +/*.js +/test/unit/coverage/ +/node_modules/* +/dist* +/src/main.ts diff --git a/yunxi-ui-admin-vue3/.eslintrc-auto-import.json b/yunxi-ui-admin-vue3/.eslintrc-auto-import.json new file mode 100644 index 00000000..024c96a3 --- /dev/null +++ b/yunxi-ui-admin-vue3/.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/yunxi-ui-admin-vue3/.eslintrc.js b/yunxi-ui-admin-vue3/.eslintrc.js new file mode 100644 index 00000000..72d4664b --- /dev/null +++ b/yunxi-ui-admin-vue3/.eslintrc.js @@ -0,0 +1,72 @@ +// @ts-check +const { defineConfig } = require('eslint-define-config') +module.exports = defineConfig({ + root: true, + env: { + browser: true, + node: true, + es6: true + }, + parser: 'vue-eslint-parser', + plugins: ['vue'], + parserOptions: { + parser: '@typescript-eslint/parser', + ecmaVersion: 2020, + sourceType: 'module', + jsxPragma: 'React', + ecmaFeatures: { + jsx: true + } + }, + extends: [ + 'plugin:vue/vue3-recommended', + 'plugin:@typescript-eslint/recommended', + 'prettier', + 'plugin:prettier/recommended', + '@unocss' + ], + rules: { + 'vue/script-setup-uses-vars': 'error', + 'vue/no-reserved-component-names': 'off', + 'vue/no-setup-props-destructure': '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': 'off', + 'no-unused-vars': 'off', + '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', + 'vue/no-v-html': 'off' + } +}) diff --git a/yunxi-ui-admin-vue3/.gitignore b/yunxi-ui-admin-vue3/.gitignore new file mode 100644 index 00000000..deb32045 --- /dev/null +++ b/yunxi-ui-admin-vue3/.gitignore @@ -0,0 +1,14 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +/dist* +*-lock.* +pnpm-debug +auto-*.d.ts +.idea +.history +.vscode +.vite + diff --git a/yunxi-ui-admin-vue3/.prettierignore b/yunxi-ui-admin-vue3/.prettierignore new file mode 100644 index 00000000..f68ea869 --- /dev/null +++ b/yunxi-ui-admin-vue3/.prettierignore @@ -0,0 +1,11 @@ +/node_modules/** +/dist/ +/dist* +/public/* +/docs/* +/vite.config.ts +/src/types/env.d.ts +/src/types/auto-components.d.ts +/src/types/auto-imports.d.ts +/docs/**/* +CHANGELOG diff --git a/yunxi-ui-admin-vue3/.stylelintignore b/yunxi-ui-admin-vue3/.stylelintignore new file mode 100644 index 00000000..aa605b49 --- /dev/null +++ b/yunxi-ui-admin-vue3/.stylelintignore @@ -0,0 +1,6 @@ +/dist/* +/public/* +public/* +/dist* +/src/types/env.d.ts +/docs/**/* diff --git a/yunxi-ui-admin-vue3/LICENSE b/yunxi-ui-admin-vue3/LICENSE new file mode 100644 index 00000000..9861118a --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/README.md b/yunxi-ui-admin-vue3/README.md new file mode 100644 index 00000000..8466bbc3 --- /dev/null +++ b/yunxi-ui-admin-vue3/README.md @@ -0,0 +1,235 @@ +**严肃声明:现在、未来都不会有商业版本,所有代码全部开源!!** + +**「我喜欢写代码,乐此不疲」** +**「我喜欢做开源,以此为乐」** + +我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。 + +如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。 + +## 🐶 新手必读 + +* nodejs > 16.18.0 && pnpm > 8.6.0 (强制使用pnpm) +* 演示地址【Vue3 + element-plus】: +* 演示地址【Vue3 + vben(ant-design-vue)】: +* 演示地址【Vue2 + element-ui】: +* 启动文档: +* 视频教程: + +## 🐯 平台简介 + +**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。 + +* 采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) 实现 +* 改换 saas,自动引入等功能 +* 使用 Element Plus 免费开源的中后台模版,具备如下特性: + +![首页](public/home.png) + +* **最新技术栈**:使用 Vue3、Vite4 等前端前沿技术开发 +* **TypeScript**: 应用程序级 JavaScript 的语言 +* **主题**: 可配置的主题 +* **国际化**:内置完善的国际化方案 +* **权限**:内置完善的动态路由权限生成方案 +* **组件**:二次封装了多个常用的组件 +* **示例**:内置丰富的示例 + +## 技术栈 + +| 框架 | 说明 | 版本 | +|----------------------------------------------------------------------|------------------|--------| +| [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.3.4 | +| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 4.4.9 | +| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.3.14 | +| [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 的超集 | 5.2.2 | +| [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.1.6 | +| [vueuse](https://vueuse.org/) | 常用工具集 | 10.4.1 | +| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.4.1 | +| [vue-router](https://router.vuejs.org/) | Vue 路由 | 4.2.5 | +| [unocss](https://uno.antfu.me/) | 原子 css | 0.56.1 | +| [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 3.1.1 | +| [wangeditor](https://www.wangeditor.com/) | 富文本编辑器 | 5.1.23 | + +## 开发工具 + +推荐 VS Code 开发,配合插件如下: + +| 插件名 | 功能 | +|-------------------------------|--------------------------| +| TypeScript Vue Plugin (Volar) | 用于 TypeScript 的 Vue 插件 | +| Vue Language Features (Volar) | Vue3.0 语法支持 | +| unocss | unocss for vscode | +| 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/yunxi-ui-admin-vue3/build/vite/index.ts b/yunxi-ui-admin-vue3/build/vite/index.ts new file mode 100644 index 00000000..02067107 --- /dev/null +++ b/yunxi-ui-admin-vue3/build/vite/index.ts @@ -0,0 +1,107 @@ +import { resolve } from 'path' +import Vue from '@vitejs/plugin-vue' +import VueJsx from '@vitejs/plugin-vue-jsx' +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' +// @ts-ignore +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 topLevelAwait from 'vite-plugin-top-level-await' +import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' +import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' +import UnoCSS from 'unocss/vite' + +export function createVitePlugins() { + const root = process.cwd() + + // 路径查找 + function pathResolve(dir: string) { + return resolve(root, '.', dir) + } + + return [ + Vue(), + VueJsx(), + UnoCSS(), + progress(), + PurgeIcons(), + ElementPlus({}), + AutoImport({ + include: [ + /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx + /\.vue$/, + /\.vue\?vue/, // .vue + /\.md$/ // .md + ], + imports: [ + 'vue', + 'vue-router', + // 可额外添加需要 autoImport 的组件 + { + '@/hooks/web/useI18n': ['useI18n'], + '@/hooks/web/useMessage': ['useMessage'], + '@/hooks/web/useTable': ['useTable'], + '@/hooks/web/useCrudSchemas': ['useCrudSchemas'], + '@/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(), + topLevelAwait({ + // https://juejin.cn/post/7152191742513512485 + // The export name of top-level await promise for each chunk module + promiseExportName: '__tla', + // The function to generate import names of top-level await promise in each chunk module + promiseImportName: (i) => `__tla_${i}` + }) + ] +} diff --git a/yunxi-ui-admin-vue3/build/vite/optimize.ts b/yunxi-ui-admin-vue3/build/vite/optimize.ts new file mode 100644 index 00000000..3dda50b0 --- /dev/null +++ b/yunxi-ui-admin-vue3/build/vite/optimize.ts @@ -0,0 +1,112 @@ +const include = [ + 'qs', + 'url', + 'vue', + 'sass', + 'mitt', + 'axios', + 'pinia', + 'dayjs', + 'qrcode', + 'unocss', + 'vue-router', + 'vue-types', + 'vue-i18n', + 'crypto-js', + 'cropperjs', + 'lodash-es', + 'nprogress', + '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/avatar/style/css', + 'element-plus/es/components/space/style/css', + 'element-plus/es/components/backtop/style/css', + 'element-plus/es/components/form/style/css', + 'element-plus/es/components/radio-group/style/css', + 'element-plus/es/components/radio/style/css', + 'element-plus/es/components/checkbox/style/css', + 'element-plus/es/components/checkbox-group/style/css', + 'element-plus/es/components/switch/style/css', + 'element-plus/es/components/time-picker/style/css', + 'element-plus/es/components/date-picker/style/css', + 'element-plus/es/components/descriptions/style/css', + 'element-plus/es/components/descriptions-item/style/css', + 'element-plus/es/components/link/style/css', + 'element-plus/es/components/tooltip/style/css', + 'element-plus/es/components/drawer/style/css', + 'element-plus/es/components/dialog/style/css', + 'element-plus/es/components/checkbox-button/style/css', + 'element-plus/es/components/option-group/style/css', + 'element-plus/es/components/radio-button/style/css', + 'element-plus/es/components/cascader/style/css', + 'element-plus/es/components/color-picker/style/css', + 'element-plus/es/components/input-number/style/css', + 'element-plus/es/components/rate/style/css', + 'element-plus/es/components/select-v2/style/css', + 'element-plus/es/components/tree-select/style/css', + 'element-plus/es/components/slider/style/css', + 'element-plus/es/components/time-select/style/css', + 'element-plus/es/components/autocomplete/style/css', + 'element-plus/es/components/image-viewer/style/css', + 'element-plus/es/components/upload/style/css', + 'element-plus/es/components/col/style/css', + 'element-plus/es/components/form-item/style/css', + 'element-plus/es/components/alert/style/css', + 'element-plus/es/components/breadcrumb/style/css', + 'element-plus/es/components/select/style/css', + 'element-plus/es/components/input/style/css', + 'element-plus/es/components/breadcrumb-item/style/css', + 'element-plus/es/components/tag/style/css', + 'element-plus/es/components/pagination/style/css', + 'element-plus/es/components/table/style/css', + 'element-plus/es/components/table-v2/style/css', + 'element-plus/es/components/table-column/style/css', + 'element-plus/es/components/card/style/css', + 'element-plus/es/components/row/style/css', + 'element-plus/es/components/button/style/css', + 'element-plus/es/components/menu/style/css', + 'element-plus/es/components/sub-menu/style/css', + 'element-plus/es/components/menu-item/style/css', + 'element-plus/es/components/option/style/css', + 'element-plus/es/components/dropdown/style/css', + 'element-plus/es/components/dropdown-menu/style/css', + 'element-plus/es/components/dropdown-item/style/css', + 'element-plus/es/components/skeleton/style/css', + 'element-plus/es/components/skeleton/style/css', + 'element-plus/es/components/backtop/style/css', + 'element-plus/es/components/menu/style/css', + 'element-plus/es/components/sub-menu/style/css', + 'element-plus/es/components/menu-item/style/css', + 'element-plus/es/components/dropdown/style/css', + 'element-plus/es/components/tree/style/css', + 'element-plus/es/components/dropdown-menu/style/css', + 'element-plus/es/components/dropdown-item/style/css', + 'element-plus/es/components/badge/style/css', + 'element-plus/es/components/breadcrumb/style/css', + 'element-plus/es/components/breadcrumb-item/style/css', + 'element-plus/es/components/image/style/css', + 'element-plus/es/components/collapse-transition/style/css', + 'element-plus/es/components/timeline/style/css', + 'element-plus/es/components/timeline-item/style/css', + 'element-plus/es/components/collapse/style/css', + 'element-plus/es/components/collapse-item/style/css', + 'element-plus/es/components/button-group/style/css', + 'element-plus/es/components/text/style/css' +] + +const exclude = ['@iconify/json'] + +export { include, exclude } diff --git a/yunxi-ui-admin-vue3/index.html b/yunxi-ui-admin-vue3/index.html new file mode 100644 index 00000000..8cfcbefa --- /dev/null +++ b/yunxi-ui-admin-vue3/index.html @@ -0,0 +1,151 @@ + + + + + + + + + + %VITE_APP_TITLE% + + +
+ +
+
+
+ +
%VITE_APP_TITLE%
+
+
+
+
+
+
+
+
+ + + diff --git a/yunxi-ui-admin-vue3/package.json b/yunxi-ui-admin-vue3/package.json new file mode 100644 index 00000000..22334907 --- /dev/null +++ b/yunxi-ui-admin-vue3/package.json @@ -0,0 +1,146 @@ +{ + "name": "yudao-ui-admin-vue3", + "version": "1.8.2-snapshot", + "description": "基于vue3、vite4、element-plus、typesScript", + "author": "xingyu", + "private": false, + "scripts": { + "i": "pnpm install", + "dev": "vite --mode base", + "front": "vite --mode front", + "ts:check": "vue-tsc --noEmit", + "build:pro": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode pro", + "build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode dev", + "build:base": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode base", + "build:stage": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode stage", + "build:static": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode static", + "build:front": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode front", + "serve:pro": "vite preview --mode pro", + "serve:dev": "vite preview --mode dev", + "preview": "pnpm build:base && vite preview", + "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 \"./src/**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", + "lint:lint-staged": "lint-staged -c " + }, + "dependencies": { + "@element-plus/icons-vue": "^2.1.0", + "@form-create/designer": "^3.1.3", + "@form-create/element-ui": "^3.1.24", + "@iconify/iconify": "^3.1.1", + "@videojs-player/vue": "^1.0.0", + "@vueuse/core": "^10.4.1", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.10", + "@zxcvbn-ts/core": "^3.0.4", + "animate.css": "^4.1.1", + "axios": "^1.5.0", + "benz-amr-recorder": "^1.1.5", + "bpmn-js-token-simulation": "^0.10.0", + "camunda-bpmn-moddle": "^7.0.1", + "cropperjs": "^1.6.1", + "crypto-js": "^4.1.1", + "dayjs": "^1.11.10", + "diagram-js": "^12.3.0", + "echarts": "^5.4.3", + "echarts-wordcloud": "^2.1.0", + "element-plus": "2.3.14", + "fast-xml-parser": "^4.3.0", + "highlight.js": "^11.8.0", + "intro.js": "^7.2.0", + "jsencrypt": "^3.3.2", + "lodash-es": "^4.17.21", + "min-dash": "^4.1.1", + "mitt": "^3.0.1", + "nprogress": "^0.2.0", + "pinia": "^2.1.6", + "qrcode": "^1.5.3", + "qs": "^6.11.2", + "steady-xml": "^0.1.0", + "url": "^0.11.3", + "video.js": "^7.21.5", + "vue": "^3.3.4", + "vue-dompurify-html": "^4.1.4", + "vue-i18n": "^9.4.1", + "vue-router": "^4.2.5", + "vue-types": "^5.1.1", + "vuedraggable": "^4.1.0", + "web-storage-cache": "^1.1.1", + "xml-js": "^1.6.11" + }, + "devDependencies": { + "@commitlint/cli": "^17.7.1", + "@commitlint/config-conventional": "^17.7.0", + "@iconify/json": "^2.2.119", + "@intlify/unplugin-vue-i18n": "^1.2.0", + "@purge-icons/generated": "^0.9.0", + "@types/intro.js": "^5.1.1", + "@types/lodash-es": "^4.17.9", + "@types/node": "^20.6.0", + "@types/nprogress": "^0.2.0", + "@types/qrcode": "^1.5.2", + "@types/qs": "^6.9.8", + "@typescript-eslint/eslint-plugin": "^6.7.2", + "@typescript-eslint/parser": "^6.7.2", + "@unocss/transformer-variant-group": "^0.56.1", + "@unocss/eslint-config": "^0.56.1", + "@vitejs/plugin-legacy": "^4.1.1", + "@vitejs/plugin-vue": "^4.3.4", + "@vitejs/plugin-vue-jsx": "^3.0.2", + "@vue-macros/volar": "^0.14.3", + "autoprefixer": "^10.4.16", + "bpmn-js": "8.9.0", + "bpmn-js-properties-panel": "0.46.0", + "consola": "^3.2.3", + "eslint": "^8.49.0", + "eslint-config-prettier": "^9.0.0", + "eslint-define-config": "^1.23.0", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-vue": "^9.17.0", + "lint-staged": "^14.0.1", + "postcss": "^8.4.30", + "postcss-html": "^1.5.0", + "postcss-scss": "^4.0.8", + "prettier": "^3.0.3", + "rimraf": "^5.0.1", + "rollup": "^3.29.2", + "sass": "^1.68.0", + "stylelint": "^15.10.3", + "stylelint-config-html": "^1.1.0", + "stylelint-config-recommended": "^13.0.0", + "stylelint-config-standard": "^34.0.0", + "stylelint-order": "^6.0.3", + "terser": "^5.20.0", + "typescript": "5.2.2", + "unocss": "^0.56.1", + "unplugin-auto-import": "^0.16.6", + "unplugin-element-plus": "^0.8.0", + "unplugin-vue-components": "^0.25.2", + "vite": "4.4.9", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-ejs": "^1.6.4", + "vite-plugin-eslint": "^1.8.1", + "vite-plugin-progress": "^0.0.7", + "vite-plugin-purge-icons": "^0.9.2", + "vite-plugin-svg-icons": "^2.0.1", + "vite-plugin-top-level-await": "^1.3.1", + "vue-eslint-parser": "^9.3.1", + "vue-tsc": "^1.8.13" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://gitee.com/yudaocode/yudao-ui-admin-vue3" + }, + "bugs": { + "url": "https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues" + }, + "homepage": "https://gitee.com/yudaocode/yudao-ui-admin-vue3", + "packageManager": "pnpm@8.6.0", + "engines": { + "node": ">= 16.0.0", + "pnpm": ">=8.6.0" + } +} diff --git a/yunxi-ui-admin-vue3/postcss.config.js b/yunxi-ui-admin-vue3/postcss.config.js new file mode 100644 index 00000000..961986e2 --- /dev/null +++ b/yunxi-ui-admin-vue3/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + autoprefixer: {} + } +} diff --git a/yunxi-ui-admin-vue3/prettier.config.js b/yunxi-ui-admin-vue3/prettier.config.js new file mode 100644 index 00000000..b014bbf1 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/public/favicon.ico b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/public/home.png b/yunxi-ui-admin-vue3/public/home.png new file mode 100644 index 0000000000000000000000000000000000000000..ccd41455a9ef128c5798691a1d74b472c6d6344a GIT binary patch literal 74352 zcmZ7cWk8%g*EI|e6n7gOiWD8ZP#lU&Db4_cYq8?)?(S0DWpI~b#f!VUTXBb%^SYn! z&-d%FBx|iCduL}K2~(7pLPsG+0RRB#GScG80006Q0QeA(^x^#tdj(kw008@_D5okx zM*a~G4-WujRI}^H!NDORA^n>D=Fqi9K}kjakwQR7n2?BwUqFzCmX3=0lkis&I(i0P zUOrwv0TEGgc6JUXW|p|W&Uh`UoLt=Uipmm_(vO2004$ofx3@3cJm;oA7#KmFe@01& z2v}IzdIu-b(b2`lC8T7&%gQN&9Y&PZwAwlcb@ffDYd8YgaII7FIUaPFrkiYWY>xVrU=87q%#=Zh_9OmXwghCZ?KS z)1_(Q$@g7PyuVzeHHcXw@Uu7!RR<5X5|6<#`;?#?c5iJt;-`w|=sOyp#=d;&3Z&n26)X5%g1v%P8~wO(Fc z&i%WuBO0{7vn$(%6DM3GG?NM!UlKElv+Z=^mPgF;XTKShI)VjFYF>$5x*VF{Y#U}0 zO_jbCylr=A7-Tht^}K3^=c<>zZJHZfl=L(Nd0|$M6~`s{)myJtD8%;K>56j#qY6>G zx8Axbdxrj*~?2l)lsU;Il$mwa2G>*Yv>0GN;LGU6htu1hDG z3RYBVgk5Pn)P5K~vZ$IAM52XNH2BCojUwtuxDjj;7+4sv<^rjyB?=x1PdRx_biS2an1Kc~cZ;*V)yhpv?0f2a z-kW@LNS;HHqZRxkWQRC)k+Ds@IU!_b)NNx+2$ss>dGZa_1^AFA8(XEM=wt(v%_#dLNlvH*#0XuZU`lHQ-&Na z%0WpBmwyV9D;%FrPk)2fzceFuh#LGK92KDaI2XM11JTtGH`1e{gnYgjx~ZrzF#d#M zv8&B>md%J*USyB;f4Ho$D=EUSw+~Vuy{<=W);nPSeX74qL>y?M?elJPvUhY$TocU= z4K5?plu*K%%!1d#82Yb=_{b}e?=O$r&R5+rT5i@n#=Uj}>DX~k9=J(6bh&m(V!Why zdYjjom0EQs6>%)j*5H}`N6rV>^`yQDlRK>La=zw%hTnj)+hMcjOP7C5o2>EU_5UL@uLR8*LCh`6vI0?TI9vedN;;09=ar) zxqK9PR`M8y*Ak$p{Y7OK^)tO#2cY|w5k?t&zY?xA1S55AHGF0po4eT;zW+g*Q#mtk z%g?2GRjJdaLyZCCm8B~pxqXq7@>goxYF zj%)nT!hsm(5fBVHg(+g|`BC5xW=jVKs6LNj_8>!(mU^2T`?^F0N}uxdK;nbiZPzzy zH9T=APDZ@y(1dV(xMyZHvk+AgO9{Wvffy)^%qTP!J71erjvSXob19JEMe6;G0t2kb zR&e-Mm9V&WKqcC;m1h2sIMB%2>d*2s-b!>l!5xc;{hy;c(WvB=wgUU5+_6jY0kwVq znIbv)zrfx@N4V-Z`HRQMk98#~g4c%+qe*Dq0}q_S zpQ+5LrxEEcBfK=D?w$x1rO{CoK(K%>gS5~)mK}oo#5#GD@uwz$OrC*S2%&}h|1P_j zeR+;Xk!qX++ERx|&y3R>qU30C5wiv`224ad5&!dfa# z;w?y&sm%lC42~BKe^P5MGo3BA$o}krpxLNpY@RxesjbBgZ=M#rWLJJknM8rWyK7Bf z&oQ(Xw7rcjKUjl3_cJR(<-1eZCCVem`>z7@1*AmtVM8&ufVE4IlY2^sWE2Xz?9S`r+78)_^s00X%uSv?y@hWA;`LWVA6kg$Bcohh6qdAU%D-;jM(A{Y6gEr#U;2wV-vxDrPGUUo*>IVY1Ts)SiSa~#8h}&@cN$S7U{|&SE6#T= z7{woYK!4~)?7x={+M@zG2YXd5T(}#SdH%O##1tht!tE_ZIDl@KUcYqDaBOYhZ|I`` z`CzRe*tWap*M^WXu!J7hIgirpvX+hRt7A7mGwf@N#K`t)EkW`ng|p zmM%fqvQXnIoL_Pwg&i8_cUlR`d|*UbER{Gg5<71y?^6ZVo9YBCFt?0gCX@*38;*@l zOnd4CkfMU8WTjuT{-GgBP&hcn;PGRl+Y2Yd6*vPjIgX%1{NBR2t-#T%0_CJ=^7=Kh z`hQ{(&g;jUsxB0M3X?A{hG)O2iGrLE z5vVo{5)qPC9gL_D)}RVh-!;!v38Yxs2rpLX*jnmG!ssO;Gw5&2jvKlCva4)P82U%5 zCw3x&HPOLuSQQgv8Vc8BY~6PzD|K=_Uwo3AOu#z$G$;q+mk68st8KO$MD?Gx`+-~t zvfc4T=F8DA*P(0tAOZLBSLZw29aD(g;>>VyzETtYcO5S!P9g6zWl&cYRkY!(7zo22 zI5X_$V)=52Mvh6kvWAK{bb9LQAV_C(=7-1Mf9;*GsclM2IHv!#svrsjC`0f)FS?q8 zJ$BO9!sNC4zJN)Y&wp_wG}lnPEXlAEUj2m_VzvNm0- zZ4q#GMz$hOZ1@t_V(WNGN}D+LlSg_>H!Q4>wF9WKLgJLL`|wzH&b zU9`GVFi1=dm$BVEP6Y|s5fc7!?4T5JPR4`9tRbC>4(j8A3`wJ#PtOc{)+_�BJed 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 z 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 literal 0 HcmV?d00001 diff --git a/yunxi-ui-admin-vue3/src/App.vue b/yunxi-ui-admin-vue3/src/App.vue new file mode 100644 index 00000000..7407d97a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/App.vue @@ -0,0 +1,57 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/api/bpm/activity/index.ts b/yunxi-ui-admin-vue3/src/api/bpm/activity/index.ts new file mode 100644 index 00000000..870d0d6c --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/api/bpm/definition/index.ts b/yunxi-ui-admin-vue3/src/api/bpm/definition/index.ts new file mode 100644 index 00000000..c0e51fab --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/bpm/definition/index.ts @@ -0,0 +1,21 @@ +import request from '@/config/axios' + +export const getProcessDefinitionBpmnXML = async (id: number) => { + return await request.get({ + url: '/bpm/process-definition/get-bpmn-xml?id=' + id + }) +} + +export const getProcessDefinitionPage = async (params) => { + return await request.get({ + url: '/bpm/process-definition/page', + params + }) +} + +export const getProcessDefinitionList = async (params) => { + return await request.get({ + url: '/bpm/process-definition/list', + params + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/bpm/form/index.ts b/yunxi-ui-admin-vue3/src/api/bpm/form/index.ts new file mode 100644 index 00000000..142ed24c --- /dev/null +++ b/yunxi-ui-admin-vue3/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 createForm = async (data: FormVO) => { + return await request.post({ + url: '/bpm/form/create', + data: data + }) +} + +// 更新工作流的表单定义 +export const updateForm = async (data: FormVO) => { + return await request.put({ + url: '/bpm/form/update', + data: data + }) +} + +// 删除工作流的表单定义 +export const deleteForm = async (id: number) => { + return await request.delete({ + url: '/bpm/form/delete?id=' + id + }) +} + +// 获得工作流的表单定义 +export const getForm = async (id: number) => { + return await request.get({ + url: '/bpm/form/get?id=' + id + }) +} + +// 获得工作流的表单定义分页 +export const getFormPage = async (params) => { + return await request.get({ + url: '/bpm/form/page', + params + }) +} + +// 获得动态表单的精简列表 +export const getSimpleFormList = async () => { + return await request.get({ + url: '/bpm/form/list-all-simple' + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/bpm/leave/index.ts b/yunxi-ui-admin-vue3/src/api/bpm/leave/index.ts new file mode 100644 index 00000000..d4fe8d58 --- /dev/null +++ b/yunxi-ui-admin-vue3/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 createLeave = async (data: LeaveVO) => { + return await request.post({ url: '/bpm/oa/leave/create', data: data }) +} + +// 获得请假申请 +export const getLeave = async (id: number) => { + return await request.get({ url: '/bpm/oa/leave/get?id=' + id }) +} + +// 获得请假申请分页 +export const getLeavePage = async (params: PageParam) => { + return await request.get({ url: '/bpm/oa/leave/page', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/bpm/model/index.ts b/yunxi-ui-admin-vue3/src/api/bpm/model/index.ts new file mode 100644 index 00000000..2e1d4e64 --- /dev/null +++ b/yunxi-ui-admin-vue3/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 getModelPage = async (params) => { + return await request.get({ url: '/bpm/model/page', params }) +} + +export const getModel = async (id: number) => { + return await request.get({ url: '/bpm/model/get?id=' + id }) +} + +export const updateModel = async (data: ModelVO) => { + return await request.put({ url: '/bpm/model/update', data: data }) +} + +// 任务状态修改 +export const updateModelState = async (id: number, state: number) => { + const data = { + id: id, + state: state + } + return await request.put({ url: '/bpm/model/update-state', data: data }) +} + +export const createModel = async (data: ModelVO) => { + return await request.post({ url: '/bpm/model/create', data: data }) +} + +export const deleteModel = async (id: number) => { + return await request.delete({ url: '/bpm/model/delete?id=' + id }) +} + +export const deployModel = async (id: number) => { + return await request.post({ url: '/bpm/model/deploy?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/bpm/processInstance/index.ts b/yunxi-ui-admin-vue3/src/api/bpm/processInstance/index.ts new file mode 100644 index 00000000..10cd3bc8 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/bpm/processInstance/index.ts @@ -0,0 +1,41 @@ +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 getMyProcessInstancePage = async (params) => { + return await request.get({ url: '/bpm/process-instance/my-page', params }) +} + +export const createProcessInstance = async (data) => { + return await request.post({ url: '/bpm/process-instance/create', data: data }) +} + +export const cancelProcessInstance = async (id: number, reason: string) => { + const data = { + id: id, + reason: reason + } + return await request.delete({ url: '/bpm/process-instance/cancel', data: data }) +} + +export const getProcessInstance = async (id: number) => { + return await request.get({ url: '/bpm/process-instance/get?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/bpm/task/index.ts b/yunxi-ui-admin-vue3/src/api/bpm/task/index.ts new file mode 100644 index 00000000..ccd5c4ee --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/bpm/task/index.ts @@ -0,0 +1,60 @@ +import request from '@/config/axios' + +export type TaskVO = { + id: number +} + +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 + }) +} + +// 导出任务 +export const exportTask = async (params) => { + return await request.download({ url: '/bpm/task/export', params }) +} + +// 获取所有可回退的节点 +export const getReturnList = async (params) => { + return await request.get({ url: '/bpm/task/get-return-list', params }) +} + +// 回退 +export const returnTask = async (data) => { + return await request.put({ url: '/bpm/task/return', data }) +} + +/** + * 委派 + */ +export const delegateTask = async (data) => { + return await request.put({ url: '/bpm/task/delegate', data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/bpm/taskAssignRule/index.ts b/yunxi-ui-admin-vue3/src/api/bpm/taskAssignRule/index.ts new file mode 100644 index 00000000..5fbe342d --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/api/bpm/userGroup/index.ts b/yunxi-ui-admin-vue3/src/api/bpm/userGroup/index.ts new file mode 100644 index 00000000..035762bf --- /dev/null +++ b/yunxi-ui-admin-vue3/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 createUserGroup = async (data: UserGroupVO) => { + return await request.post({ + url: '/bpm/user-group/create', + data: data + }) +} + +// 更新用户组 +export const updateUserGroup = async (data: UserGroupVO) => { + return await request.put({ + url: '/bpm/user-group/update', + data: data + }) +} + +// 删除用户组 +export const deleteUserGroup = async (id: number) => { + return await request.delete({ url: '/bpm/user-group/delete?id=' + id }) +} + +// 获得用户组 +export const getUserGroup = async (id: number) => { + return await request.get({ url: '/bpm/user-group/get?id=' + id }) +} + +// 获得用户组分页 +export const getUserGroupPage = async (params) => { + return await request.get({ url: '/bpm/user-group/page', params }) +} + +// 获取用户组精简信息列表 +export const getSimpleUserGroupList = async (): Promise => { + return await request.get({ url: '/bpm/user-group/list-all-simple' }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/apiAccessLog/index.ts b/yunxi-ui-admin-vue3/src/api/infra/apiAccessLog/index.ts new file mode 100644 index 00000000..c6b4b45f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/apiAccessLog/index.ts @@ -0,0 +1,30 @@ +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 +} + +// 查询列表API 访问日志 +export const getApiAccessLogPage = (params: PageParam) => { + return request.get({ url: '/infra/api-access-log/page', params }) +} + +// 导出API 访问日志 +export const exportApiAccessLog = (params) => { + return request.download({ url: '/infra/api-access-log/export-excel', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/apiErrorLog/index.ts b/yunxi-ui-admin-vue3/src/api/infra/apiErrorLog/index.ts new file mode 100644 index 00000000..59ee2143 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/apiErrorLog/index.ts @@ -0,0 +1,48 @@ +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 +} + +// 查询列表API 访问日志 +export const getApiErrorLogPage = (params: PageParam) => { + return request.get({ url: '/infra/api-error-log/page', params }) +} + +// 更新 API 错误日志的处理状态 +export const updateApiErrorLogPage = (id: number, processStatus: number) => { + return request.put({ + url: '/infra/api-error-log/update-status?id=' + id + '&processStatus=' + processStatus + }) +} + +// 导出API 访问日志 +export const exportApiErrorLog = (params) => { + return request.download({ + url: '/infra/api-error-log/export-excel', + params + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/codegen/index.ts b/yunxi-ui-admin-vue3/src/api/infra/codegen/index.ts new file mode 100644 index 00000000..64701efe --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/codegen/index.ts @@ -0,0 +1,123 @@ +import request from '@/config/axios' + +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 | any + columns: CodegenColumnVO[] +} + +export type CodegenCreateListReqVO = { + dataSourceConfigId: number + tableNames: string[] +} + +// 查询列表代码生成表定义 +export const getCodegenTablePage = (params: PageParam) => { + return request.get({ url: '/infra/codegen/table/page', params }) +} + +// 查询详情代码生成表定义 +export const getCodegenTable = (id: number) => { + return request.get({ url: '/infra/codegen/detail?tableId=' + id }) +} + +// 新增代码生成表定义 +export const createCodegenTable = (data: CodegenCreateListReqVO) => { + return request.post({ url: '/infra/codegen/create', data }) +} + +// 修改代码生成表定义 +export const updateCodegenTable = (data: CodegenUpdateReqVO) => { + return request.put({ url: '/infra/codegen/update', data }) +} + +// 基于数据库的表结构,同步数据库的表和字段定义 +export const syncCodegenFromDB = (id: number) => { + return request.put({ url: '/infra/codegen/sync-from-db?tableId=' + id }) +} + +// 基于 SQL 建表语句,同步数据库的表和字段定义 +export const syncCodegenFromSQL = (id: number, sql: string) => { + return request.put({ url: '/infra/codegen/sync-from-sql?tableId=' + id + '&sql=' + sql }) +} + +// 预览生成代码 +export const previewCodegen = (id: number) => { + return request.get({ url: '/infra/codegen/preview?tableId=' + id }) +} + +// 下载生成代码 +export const downloadCodegen = (id: number) => { + return request.download({ url: '/infra/codegen/download?tableId=' + id }) +} + +// 获得表定义 +export const getSchemaTableList = (params) => { + return request.get({ url: '/infra/codegen/db/table/list', params }) +} + +// 基于数据库的表结构,创建代码生成器的表定义 +export const createCodegenList = (data) => { + return request.post({ url: '/infra/codegen/create-list', data }) +} + +// 删除代码生成表定义 +export const deleteCodegenTable = (id: number) => { + return request.delete({ url: '/infra/codegen/delete?tableId=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/config/index.ts b/yunxi-ui-admin-vue3/src/api/infra/config/index.ts new file mode 100644 index 00000000..5ef59f33 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/config/index.ts @@ -0,0 +1,48 @@ +import request from '@/config/axios' + +export interface ConfigVO { + id: number | undefined + category: string + name: string + key: string + value: string + type: number + visible: boolean + remark: string + createTime: Date +} + +// 查询参数列表 +export const getConfigPage = (params: PageParam) => { + return request.get({ url: '/infra/config/page', params }) +} + +// 查询参数详情 +export const getConfig = (id: number) => { + return request.get({ url: '/infra/config/get?id=' + id }) +} + +// 根据参数键名查询参数值 +export const getConfigKey = (configKey: string) => { + return request.get({ url: '/infra/config/get-value-by-key?key=' + configKey }) +} + +// 新增参数 +export const createConfig = (data: ConfigVO) => { + return request.post({ url: '/infra/config/create', data }) +} + +// 修改参数 +export const updateConfig = (data: ConfigVO) => { + return request.put({ url: '/infra/config/update', data }) +} + +// 删除参数 +export const deleteConfig = (id: number) => { + return request.delete({ url: '/infra/config/delete?id=' + id }) +} + +// 导出参数 +export const exportConfig = (params) => { + return request.download({ url: '/infra/config/export', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/dataSourceConfig/index.ts b/yunxi-ui-admin-vue3/src/api/infra/dataSourceConfig/index.ts new file mode 100644 index 00000000..b413f345 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/dataSourceConfig/index.ts @@ -0,0 +1,35 @@ +import request from '@/config/axios' + +export interface DataSourceConfigVO { + id: number | undefined + name: string + url: string + username: string + password: string + createTime?: Date +} + +// 新增数据源配置 +export const createDataSourceConfig = (data: DataSourceConfigVO) => { + return request.post({ url: '/infra/data-source-config/create', data }) +} + +// 修改数据源配置 +export const updateDataSourceConfig = (data: DataSourceConfigVO) => { + return request.put({ url: '/infra/data-source-config/update', data }) +} + +// 删除数据源配置 +export const deleteDataSourceConfig = (id: number) => { + return request.delete({ url: '/infra/data-source-config/delete?id=' + id }) +} + +// 查询数据源配置详情 +export const getDataSourceConfig = (id: number) => { + return request.get({ url: '/infra/data-source-config/get?id=' + id }) +} + +// 查询数据源配置列表 +export const getDataSourceConfigList = () => { + return request.get({ url: '/infra/data-source-config/list' }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/dbDoc/index.ts b/yunxi-ui-admin-vue3/src/api/infra/dbDoc/index.ts new file mode 100644 index 00000000..1a1a36b4 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/dbDoc/index.ts @@ -0,0 +1,16 @@ +import request from '@/config/axios' + +// 导出Html +export const exportHtml = () => { + return request.download({ url: '/infra/db-doc/export-html' }) +} + +// 导出Word +export const exportWord = () => { + return request.download({ url: '/infra/db-doc/export-word' }) +} + +// 导出Markdown +export const exportMarkdown = () => { + return request.download({ url: '/infra/db-doc/export-markdown' }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/file/index.ts b/yunxi-ui-admin-vue3/src/api/infra/file/index.ts new file mode 100644 index 00000000..f64bc0d6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/file/index.ts @@ -0,0 +1,17 @@ +import request from '@/config/axios' + +export interface FilePageReqVO extends PageParam { + path?: string + type?: string + createTime?: Date[] +} + +// 查询文件列表 +export const getFilePage = (params: FilePageReqVO) => { + return request.get({ url: '/infra/file/page', params }) +} + +// 删除文件 +export const deleteFile = (id: number) => { + return request.delete({ url: '/infra/file/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/fileConfig/index.ts b/yunxi-ui-admin-vue3/src/api/infra/fileConfig/index.ts new file mode 100644 index 00000000..b72f18b7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/fileConfig/index.ts @@ -0,0 +1,61 @@ +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: any + master: boolean + visible: boolean + config: FileClientConfig + remark: string + createTime: Date +} + +// 查询文件配置列表 +export const getFileConfigPage = (params: PageParam) => { + return request.get({ url: '/infra/file-config/page', params }) +} + +// 查询文件配置详情 +export const getFileConfig = (id: number) => { + return request.get({ url: '/infra/file-config/get?id=' + id }) +} + +// 更新文件配置为主配置 +export const updateFileConfigMaster = (id: number) => { + return request.put({ url: '/infra/file-config/update-master?id=' + id }) +} + +// 新增文件配置 +export const createFileConfig = (data: FileConfigVO) => { + return request.post({ url: '/infra/file-config/create', data }) +} + +// 修改文件配置 +export const updateFileConfig = (data: FileConfigVO) => { + return request.put({ url: '/infra/file-config/update', data }) +} + +// 删除文件配置 +export const deleteFileConfig = (id: number) => { + return request.delete({ url: '/infra/file-config/delete?id=' + id }) +} + +// 测试文件配置 +export const testFileConfig = (id: number) => { + return request.get({ url: '/infra/file-config/test?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/job/index.ts b/yunxi-ui-admin-vue3/src/api/infra/job/index.ts new file mode 100644 index 00000000..033b2cbe --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/job/index.ts @@ -0,0 +1,63 @@ +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 const getJobPage = (params: PageParam) => { + return request.get({ url: '/infra/job/page', params }) +} + +// 任务详情 +export const getJob = (id: number) => { + return request.get({ url: '/infra/job/get?id=' + id }) +} + +// 新增任务 +export const createJob = (data: JobVO) => { + return request.post({ url: '/infra/job/create', data }) +} + +// 修改定时任务调度 +export const updateJob = (data: JobVO) => { + return request.put({ url: '/infra/job/update', data }) +} + +// 删除定时任务调度 +export const deleteJob = (id: number) => { + return request.delete({ url: '/infra/job/delete?id=' + id }) +} + +// 导出定时任务调度 +export const exportJob = (params) => { + return request.download({ url: '/infra/job/export-excel', params }) +} + +// 任务状态修改 +export const updateJobStatus = (id: number, status: number) => { + const params = { + id, + status + } + return request.put({ url: '/infra/job/update-status', params }) +} + +// 定时任务立即执行一次 +export const runJob = (id: number) => { + return request.put({ url: '/infra/job/trigger?id=' + id }) +} + +// 获得定时任务的下 n 次执行时间 +export const getJobNextTimes = (id: number) => { + return request.get({ url: '/infra/job/get_next_times?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/jobLog/index.ts b/yunxi-ui-admin-vue3/src/api/infra/jobLog/index.ts new file mode 100644 index 00000000..f429cd9e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/jobLog/index.ts @@ -0,0 +1,33 @@ +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 const getJobLogPage = (params: PageParam) => { + return request.get({ url: '/infra/job-log/page', params }) +} + +// 任务日志详情 +export const getJobLog = (id: number) => { + return request.get({ url: '/infra/job-log/get?id=' + id }) +} + +// 导出定时任务日志 +export const exportJobLog = (params) => { + return request.download({ + url: '/infra/job-log/export-excel', + params + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/redis/index.ts b/yunxi-ui-admin-vue3/src/api/infra/redis/index.ts new file mode 100644 index 00000000..f27be77f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/redis/index.ts @@ -0,0 +1,8 @@ +import request from '@/config/axios' + +/** + * 获取redis 监控信息 + */ +export const getCache = () => { + return request.get({ url: '/infra/redis/get-monitor-info' }) +} diff --git a/yunxi-ui-admin-vue3/src/api/infra/redis/types.ts b/yunxi-ui-admin-vue3/src/api/infra/redis/types.ts new file mode 100644 index 00000000..548bfe96 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/infra/redis/types.ts @@ -0,0 +1,176 @@ +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 +} diff --git a/yunxi-ui-admin-vue3/src/api/login/index.ts b/yunxi-ui-admin-vue3/src/api/login/index.ts new file mode 100644 index 00000000..b65a90cf --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/login/index.ts @@ -0,0 +1,64 @@ +import request from '@/config/axios' +import { getRefreshToken } from '@/utils/auth' +import type { UserLoginVO } from './types' + +export interface SmsCodeVO { + mobile: string + scene: number +} + +export interface SmsLoginVO { + mobile: string + code: string +} + +// 登录 +export const login = (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 getTenantIdByName = (name: string) => { + return request.get({ url: '/system/tenant/get-id-by-name?name=' + name }) +} + +// 登出 +export const loginOut = () => { + return request.post({ url: '/system/auth/logout' }) +} + +// 获取用户权限信息 +export const getInfo = () => { + return request.get({ url: '/system/auth/get-permission-info' }) +} + +//获取登录验证码 +export const sendSmsCode = (data: SmsCodeVO) => { + return request.post({ url: '/system/auth/send-sms-code', data }) +} + +// 短信验证码登录 +export const smsLogin = (data: SmsLoginVO) => { + return request.post({ url: '/system/auth/sms-login', data }) +} + +// 社交授权的跳转 +export const socialAuthRedirect = (type: number, redirectUri: string) => { + return request.get({ + url: '/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri + }) +} +// 获取验证图片以及 token +export const getCode = (data) => { + return request.postOriginal({ url: 'system/captcha/get', data }) +} + +// 滑动或者点选验证 +export const reqCheck = (data) => { + return request.postOriginal({ url: 'system/captcha/check', data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/login/oauth2/index.ts b/yunxi-ui-admin-vue3/src/api/login/oauth2/index.ts new file mode 100644 index 00000000..aef1820d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/login/oauth2/index.ts @@ -0,0 +1,41 @@ +import request from '@/config/axios' + +// 获得授权信息 +export const getAuthorize = (clientId: string) => { + return request.get({ url: '/system/oauth2/authorize?clientId=' + clientId }) +} + +// 发起授权 +export const authorize = ( + responseType: string, + clientId: string, + redirectUri: string, + state: string, + autoApprove: boolean, + checkedScopes: string[], + uncheckedScopes: string[] +) => { + // 构建 scopes + const scopes = {} + for (const scope of checkedScopes) { + scopes[scope] = true + } + for (const scope of uncheckedScopes) { + scopes[scope] = false + } + // 发起请求 + return request.post({ + url: '/system/oauth2/authorize', + headers: { + 'Content-type': 'application/x-www-form-urlencoded' + }, + params: { + response_type: responseType, + client_id: clientId, + redirect_uri: redirectUri, + state: state, + auto_approve: autoApprove, + scope: JSON.stringify(scopes) + } + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/login/types.ts b/yunxi-ui-admin-vue3/src/api/login/types.ts new file mode 100644 index 00000000..b2173f72 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/login/types.ts @@ -0,0 +1,28 @@ +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 +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/product/brand.ts b/yunxi-ui-admin-vue3/src/api/mall/product/brand.ts new file mode 100644 index 00000000..94d53704 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/product/brand.ts @@ -0,0 +1,61 @@ +import request from '@/config/axios' + +/** + * 商品品牌 + */ +export interface BrandVO { + /** + * 品牌编号 + */ + id?: number + /** + * 品牌名称 + */ + name: string + /** + * 品牌图片 + */ + picUrl: string + /** + * 品牌排序 + */ + sort?: number + /** + * 品牌描述 + */ + description?: string + /** + * 开启状态 + */ + status: number +} + +// 创建商品品牌 +export const createBrand = (data: BrandVO) => { + return request.post({ url: '/product/brand/create', data }) +} + +// 更新商品品牌 +export const updateBrand = (data: BrandVO) => { + return request.put({ url: '/product/brand/update', data }) +} + +// 删除商品品牌 +export const deleteBrand = (id: number) => { + return request.delete({ url: `/product/brand/delete?id=${id}` }) +} + +// 获得商品品牌 +export const getBrand = (id: number) => { + return request.get({ url: `/product/brand/get?id=${id}` }) +} + +// 获得商品品牌列表 +export const getBrandParam = (params: PageParam) => { + return request.get({ url: '/product/brand/page', params }) +} + +// 获得商品品牌精简信息列表 +export const getSimpleBrandList = () => { + return request.get({ url: '/product/brand/list-all-simple' }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/product/category.ts b/yunxi-ui-admin-vue3/src/api/mall/product/category.ts new file mode 100644 index 00000000..8158fc0f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/product/category.ts @@ -0,0 +1,60 @@ +import request from '@/config/axios' + +/** + * 产品分类 + */ +export interface CategoryVO { + /** + * 分类编号 + */ + id?: number + /** + * 父分类编号 + */ + parentId?: number + /** + * 分类名称 + */ + name: string + /** + * 移动端分类图 + */ + picUrl: string + /** + * PC 端分类图 + */ + bigPicUrl?: string + /** + * 分类排序 + */ + sort: number + /** + * 开启状态 + */ + status: number +} + +// 创建商品分类 +export const createCategory = (data: CategoryVO) => { + return request.post({ url: '/product/category/create', data }) +} + +// 更新商品分类 +export const updateCategory = (data: CategoryVO) => { + return request.put({ url: '/product/category/update', data }) +} + +// 删除商品分类 +export const deleteCategory = (id: number) => { + return request.delete({ url: `/product/category/delete?id=${id}` }) +} + +// 获得商品分类 +export const getCategory = (id: number) => { + return request.get({ url: `/product/category/get?id=${id}` }) +} + +// 获得商品分类列表 +export const getCategoryList = (params: any) => { + return request.get({ url: '/product/category/list', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/product/comment.ts b/yunxi-ui-admin-vue3/src/api/mall/product/comment.ts new file mode 100644 index 00000000..defdbb93 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/product/comment.ts @@ -0,0 +1,49 @@ +import request from '@/config/axios' + +export interface CommentVO { + id: number + userId: number + userNickname: string + userAvatar: string + anonymous: boolean + orderId: number + orderItemId: number + spuId: number + spuName: string + skuId: number + visible: boolean + scores: number + descriptionScores: number + benefitScores: number + content: string + picUrls: string + replyStatus: boolean + replyUserId: number + replyContent: string + replyTime: Date +} + +// 查询商品评论列表 +export const getCommentPage = async (params) => { + return await request.get({ url: `/product/comment/page`, params }) +} + +// 查询商品评论详情 +export const getComment = async (id: number) => { + return await request.get({ url: `/product/comment/get?id=` + id }) +} + +// 添加自评 +export const createComment = async (data: CommentVO) => { + return await request.post({ url: `/product/comment/create`, data }) +} + +// 显示 / 隐藏评论 +export const updateCommentVisible = async (data: any) => { + return await request.put({ url: `/product/comment/update-visible`, data }) +} + +// 商家回复 +export const replyComment = async (data: any) => { + return await request.put({ url: `/product/comment/reply`, data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/product/property.ts b/yunxi-ui-admin-vue3/src/api/mall/product/property.ts new file mode 100644 index 00000000..ac8bac59 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/product/property.ts @@ -0,0 +1,103 @@ +import request from '@/config/axios' + +/** + * 商品属性 + */ +export interface PropertyVO { + id?: number + /** 名称 */ + name: string + /** 备注 */ + remark?: string +} + +/** + * 属性值 + */ +export interface PropertyValueVO { + id?: number + /** 属性项的编号 */ + propertyId?: number + /** 名称 */ + name: string + /** 备注 */ + remark?: string +} + +/** + * 商品属性值的明细 + */ +export interface PropertyValueDetailVO { + /** 属性项的编号 */ + propertyId: number // 属性的编号 + /** 属性的名称 */ + propertyName: string + /** 属性值的编号 */ + valueId: number + /** 属性值的名称 */ + valueName: string +} + +// ------------------------ 属性项 ------------------- + +// 创建属性项 +export const createProperty = (data: PropertyVO) => { + return request.post({ url: '/product/property/create', data }) +} + +// 更新属性项 +export const updateProperty = (data: PropertyVO) => { + return request.put({ url: '/product/property/update', data }) +} + +// 删除属性项 +export const deleteProperty = (id: number) => { + return request.delete({ url: `/product/property/delete?id=${id}` }) +} + +// 获得属性项 +export const getProperty = (id: number): Promise => { + return request.get({ url: `/product/property/get?id=${id}` }) +} + +// 获得属性项分页 +export const getPropertyPage = (params: PageParam) => { + return request.get({ url: '/product/property/page', params }) +} + +// 获得属性项列表 +export const getPropertyList = (params: any) => { + return request.get({ url: '/product/property/list', params }) +} + +// 获得属性项列表 +export const getPropertyListAndValue = (data: any) => { + return request.post({ url: '/product/property/get-value-list', data }) +} + +// ------------------------ 属性值 ------------------- + +// 获得属性值分页 +export const getPropertyValuePage = (params: PageParam & any) => { + return request.get({ url: '/product/property/value/page', params }) +} + +// 获得属性值 +export const getPropertyValue = (id: number): Promise => { + return request.get({ url: `/product/property/value/get?id=${id}` }) +} + +// 创建属性值 +export const createPropertyValue = (data: PropertyValueVO) => { + return request.post({ url: '/product/property/value/create', data }) +} + +// 更新属性值 +export const updatePropertyValue = (data: PropertyValueVO) => { + return request.put({ url: '/product/property/value/update', data }) +} + +// 删除属性值 +export const deletePropertyValue = (id: number) => { + return request.delete({ url: `/product/property/value/delete?id=${id}` }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/product/spu.ts b/yunxi-ui-admin-vue3/src/api/mall/product/spu.ts new file mode 100644 index 00000000..c78bef47 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/product/spu.ts @@ -0,0 +1,108 @@ +import request from '@/config/axios' + +export interface Property { + propertyId?: number // 属性编号 + propertyName?: string // 属性名称 + valueId?: number // 属性值编号 + valueName?: string // 属性值名称 +} + +export interface Sku { + id?: number // 商品 SKU 编号 + name?: string // 商品 SKU 名称 + spuId?: number // SPU 编号 + properties?: Property[] // 属性数组 + price?: number | string // 商品价格 + marketPrice?: number | string // 市场价 + costPrice?: number | string // 成本价 + barCode?: string // 商品条码 + picUrl?: string // 图片地址 + stock?: number // 库存 + weight?: number // 商品重量,单位:kg 千克 + volume?: number // 商品体积,单位:m^3 平米 + firstBrokeragePrice?: number | string // 一级分销的佣金 + secondBrokeragePrice?: number | string // 二级分销的佣金 + salesCount?: number // 商品销量 +} + +export interface Spu { + id?: number + name?: string // 商品名称 + categoryId?: number | null // 商品分类 + keyword?: string // 关键字 + unit?: number | null // 单位 + picUrl?: string // 商品封面图 + sliderPicUrls?: string[] // 商品轮播图 + introduction?: string // 商品简介 + deliveryTemplateId?: number | null // 运费模版 + brandId?: number | null // 商品品牌编号 + specType?: boolean // 商品规格 + subCommissionType?: boolean // 分销类型 + skus?: Sku[] // sku数组 + description?: string // 商品详情 + sort?: number // 商品排序 + giveIntegral?: number // 赠送积分 + virtualSalesCount?: number // 虚拟销量 + recommendHot?: boolean // 是否热卖 + recommendBenefit?: boolean // 是否优惠 + recommendBest?: boolean // 是否精品 + recommendNew?: boolean // 是否新品 + recommendGood?: boolean // 是否优品 + price?: number // 商品价格 + salesCount?: number // 商品销量 + marketPrice?: number // 市场价 + costPrice?: number // 成本价 + stock?: number // 商品库存 + createTime?: Date // 商品创建时间 + status?: number // 商品状态 +} + +// 获得 Spu 列表 +export const getSpuPage = (params: PageParam) => { + return request.get({ url: '/product/spu/page', params }) +} + +// 获得 Spu 列表 tabsCount +export const getTabsCount = () => { + return request.get({ url: '/product/spu/get-count' }) +} + +// 创建商品 Spu +export const createSpu = (data: Spu) => { + return request.post({ url: '/product/spu/create', data }) +} + +// 更新商品 Spu +export const updateSpu = (data: Spu) => { + return request.put({ url: '/product/spu/update', data }) +} + +// 更新商品 Spu status +export const updateStatus = (data: { id: number; status: number }) => { + return request.put({ url: '/product/spu/update-status', data }) +} + +// 获得商品 Spu +export const getSpu = (id: number) => { + return request.get({ url: `/product/spu/get-detail?id=${id}` }) +} + +// 获得商品 Spu 详情列表 +export const getSpuDetailList = (ids: number[]) => { + return request.get({ url: `/product/spu/list?spuIds=${ids}` }) +} + +// 删除商品 Spu +export const deleteSpu = (id: number) => { + return request.delete({ url: `/product/spu/delete?id=${id}` }) +} + +// 导出商品 Spu Excel +export const exportSpu = async (params) => { + return await request.download({ url: '/product/spu/export', params }) +} + +// 获得商品 SPU 精简列表 +export const getSpuSimpleList = async () => { + return request.get({ url: '/product/spu/get-simple-list' }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainActivity.ts b/yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainActivity.ts new file mode 100644 index 00000000..9ad219ac --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainActivity.ts @@ -0,0 +1,68 @@ +import request from '@/config/axios' +import { Sku, Spu } from '@/api/mall/product/spu' + +export interface BargainActivityVO { + id?: number + name?: string + startTime?: Date + endTime?: Date + status?: number + helpMaxCount?: number // 达到该人数,才能砍到低价 + bargainCount?: number // 最大帮砍次数 + totalLimitCount?: number // 最大购买次数 + spuId: number + skuId: number + bargainFirstPrice: number // 砍价起始价格,单位分 + bargainMinPrice: number // 砍价底价 + stock: number // 活动库存 + randomMinPrice?: number // 用户每次砍价的最小金额,单位:分 + randomMaxPrice?: number // 用户每次砍价的最大金额,单位:分 +} + +// 砍价活动所需属性。选择的商品和属性的时候使用方便使用活动的通用封装 +export interface BargainProductVO { + spuId: number + skuId: number + bargainFirstPrice: number // 砍价起始价格,单位分 + bargainMinPrice: number // 砍价底价 + stock: number // 活动库存 +} + +// 扩展 Sku 配置 +export type SkuExtension = Sku & { + productConfig: BargainProductVO +} + +export interface SpuExtension extends Spu { + skus: SkuExtension[] // 重写类型 +} + +// 查询砍价活动列表 +export const getBargainActivityPage = async (params: any) => { + return await request.get({ url: '/promotion/bargain-activity/page', params }) +} + +// 查询砍价活动详情 +export const getBargainActivity = async (id: number) => { + return await request.get({ url: '/promotion/bargain-activity/get?id=' + id }) +} + +// 新增砍价活动 +export const createBargainActivity = async (data: BargainActivityVO) => { + return await request.post({ url: '/promotion/bargain-activity/create', data }) +} + +// 修改砍价活动 +export const updateBargainActivity = async (data: BargainActivityVO) => { + return await request.put({ url: '/promotion/bargain-activity/update', data }) +} + +// 关闭砍价活动 +export const closeBargainActivity = async (id: number) => { + return await request.put({ url: '/promotion/bargain-activity/close?id=' + id }) +} + +// 删除砍价活动 +export const deleteBargainActivity = async (id: number) => { + return await request.delete({ url: '/promotion/bargain-activity/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainHelp.ts b/yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainHelp.ts new file mode 100644 index 00000000..4308ae66 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainHelp.ts @@ -0,0 +1,14 @@ +import request from '@/config/axios' + +export interface BargainHelpVO { + id: number + record: number + userId: number + reducePrice: number + endTime: Date +} + +// 查询砍价记录列表 +export const getBargainHelpPage = async (params) => { + return await request.get({ url: `/promotion/bargain-help/page`, params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainRecord.ts b/yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainRecord.ts new file mode 100644 index 00000000..f90b7845 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/promotion/bargain/bargainRecord.ts @@ -0,0 +1,19 @@ +import request from '@/config/axios' + +export interface BargainRecordVO { + id: number + activityId: number + userId: number + spuId: number + skuId: number + bargainFirstPrice: number + bargainPrice: number + status: number + orderId: number + endTime: Date +} + +// 查询砍价记录列表 +export const getBargainRecordPage = async (params) => { + return await request.get({ url: `/promotion/bargain-record/page`, params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/promotion/combination/combinationActivity.ts b/yunxi-ui-admin-vue3/src/api/mall/promotion/combination/combinationActivity.ts new file mode 100644 index 00000000..15b7eef3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/promotion/combination/combinationActivity.ts @@ -0,0 +1,66 @@ +import request from '@/config/axios' +import { Sku, Spu } from '@/api/mall/product/spu' + +export interface CombinationActivityVO { + id?: number + name?: string + spuId?: number + totalLimitCount?: number + singleLimitCount?: number + startTime?: Date + endTime?: Date + userSize?: number + totalCount?: number + successCount?: number + orderUserCount?: number + virtualGroup?: number + status?: number + limitDuration?: number + products: CombinationProductVO[] +} + +// 拼团活动所需属性 +export interface CombinationProductVO { + spuId: number + skuId: number + combinationPrice: number // 拼团价格 +} + +// 扩展 Sku 配置 +export type SkuExtension = Sku & { + productConfig: CombinationProductVO +} + +export interface SpuExtension extends Spu { + skus: SkuExtension[] // 重写类型 +} + +// 查询拼团活动列表 +export const getCombinationActivityPage = async (params) => { + return await request.get({ url: '/promotion/combination-activity/page', params }) +} + +// 查询拼团活动详情 +export const getCombinationActivity = async (id: number) => { + return await request.get({ url: '/promotion/combination-activity/get?id=' + id }) +} + +// 新增拼团活动 +export const createCombinationActivity = async (data: CombinationActivityVO) => { + return await request.post({ url: '/promotion/combination-activity/create', data }) +} + +// 修改拼团活动 +export const updateCombinationActivity = async (data: CombinationActivityVO) => { + return await request.put({ url: '/promotion/combination-activity/update', data }) +} + +// 关闭拼团活动 +export const closeCombinationActivity = async (id: number) => { + return await request.put({ url: '/promotion/bargain-combination/close?id=' + id }) +} + +// 删除拼团活动 +export const deleteCombinationActivity = async (id: number) => { + return await request.delete({ url: '/promotion/combination-activity/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/promotion/combination/combinationRecord.ts b/yunxi-ui-admin-vue3/src/api/mall/promotion/combination/combinationRecord.ts new file mode 100644 index 00000000..90e8937e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/promotion/combination/combinationRecord.ts @@ -0,0 +1,33 @@ +import request from '@/config/axios' + +export interface CombinationRecordVO { + id: number // 拼团记录编号 + activityId: number // 拼团活动编号 + nickname: string // 用户昵称 + avatar: string // 用户头像 + headId: number // 团长编号 + expireTime: string // 过期时间 + userSize: number // 可参团人数 + userCount: number // 已参团人数 + status: number // 拼团状态 + spuName: string // 商品名字 + picUrl: string // 商品图片 + virtualGroup: boolean // 是否虚拟成团 + startTime: string // 开始时间 (订单付款后开始的时间) + endTime: string // 结束时间(成团时间/失败时间) +} + +// 查询拼团记录列表 +export const getCombinationRecordPage = async (params) => { + return await request.get({ url: '/promotion/combination-record/page', params }) +} + +// 查询一个拼团的完整拼团记录 +export const getCombinationRecordPageByHeadId = async (params) => { + return await request.get({ url: '/promotion/combination-record/page-by-headId', params }) +} + +// 获得拼团记录的概要信息 +export const getCombinationRecordSummary = async () => { + return await request.get({ url: '/promotion/combination-record/get-summary' }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/promotion/coupon/coupon.ts b/yunxi-ui-admin-vue3/src/api/mall/promotion/coupon/coupon.ts new file mode 100644 index 00000000..2ebff5da --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/promotion/coupon/coupon.ts @@ -0,0 +1,26 @@ +import request from '@/config/axios' + +// TODO @dhb52:vo 缺少 + +// 删除优惠劵 +export const deleteCoupon = async (id: number) => { + return request.delete({ + url: `/promotion/coupon/delete?id=${id}` + }) +} + +// 获得优惠劵分页 +export const getCouponPage = async (params: PageParam) => { + return request.get({ + url: '/promotion/coupon/page', + params: params + }) +} + +// 发送优惠券 +export const sendCoupon = async (data: any) => { + return request.post({ + url: '/promotion/coupon/send', + data: data + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/promotion/coupon/couponTemplate.ts b/yunxi-ui-admin-vue3/src/api/mall/promotion/coupon/couponTemplate.ts new file mode 100644 index 00000000..243e22ee --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/promotion/coupon/couponTemplate.ts @@ -0,0 +1,83 @@ +import request from '@/config/axios' + +export interface CouponTemplateVO { + id: number + name: string + status: number + totalCount: number + takeLimitCount: number + takeType: number + usePrice: number + productScope: number + productScopeValues: number[] + validityType: number + validStartTime: Date + validEndTime: Date + fixedStartTerm: number + fixedEndTerm: number + discountType: number + discountPercent: number + discountPrice: number + discountLimitPrice: number + takeCount: number + useCount: number +} + +// 创建优惠劵模板 +export function createCouponTemplate(data: CouponTemplateVO) { + return request.post({ + url: '/promotion/coupon-template/create', + data: data + }) +} + +// 更新优惠劵模板 +export function updateCouponTemplate(data: CouponTemplateVO) { + return request.put({ + url: '/promotion/coupon-template/update', + data: data + }) +} + +// 更新优惠劵模板的状态 +export function updateCouponTemplateStatus(id: number, status: [0, 1]) { + const data = { + id, + status + } + return request.put({ + url: '/promotion/coupon-template/update-status', + data: data + }) +} + +// 删除优惠劵模板 +export function deleteCouponTemplate(id: number) { + return request.delete({ + url: '/promotion/coupon-template/delete?id=' + id + }) +} + +// 获得优惠劵模板 +export function getCouponTemplate(id: number) { + return request.get({ + url: '/promotion/coupon-template/get?id=' + id + }) +} + +// 获得优惠劵模板分页 +export function getCouponTemplatePage(params: PageParam) { + return request.get({ + url: '/promotion/coupon-template/page', + params: params + }) +} + +// 导出优惠劵模板 Excel +export function exportCouponTemplateExcel(params: PageParam) { + return request.get({ + url: '/promotion/coupon-template/export-excel', + params: params, + responseType: 'blob' + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/promotion/seckill/seckillActivity.ts b/yunxi-ui-admin-vue3/src/api/mall/promotion/seckill/seckillActivity.ts new file mode 100644 index 00000000..e8346410 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/promotion/seckill/seckillActivity.ts @@ -0,0 +1,68 @@ +import request from '@/config/axios' +import { Sku, Spu } from '@/api/mall/product/spu' + +export interface SeckillActivityVO { + id?: number + spuId?: number + name?: string + status?: number + remark?: string + startTime?: Date + endTime?: Date + sort?: number + configIds?: string + orderCount?: number + userCount?: number + totalPrice?: number + totalLimitCount?: number + singleLimitCount?: number + stock?: number + totalStock?: number + products?: SeckillProductVO[] +} + +// 秒杀活动所需属性 +export interface SeckillProductVO { + skuId: number + seckillPrice: number + stock: number +} + +// 扩展 Sku 配置 +export type SkuExtension = Sku & { + productConfig: SeckillProductVO +} + +export interface SpuExtension extends Spu { + skus: SkuExtension[] // 重写类型 +} + +// 查询秒杀活动列表 +export const getSeckillActivityPage = async (params) => { + return await request.get({ url: '/promotion/seckill-activity/page', params }) +} + +// 查询秒杀活动详情 +export const getSeckillActivity = async (id: number) => { + return await request.get({ url: '/promotion/seckill-activity/get?id=' + id }) +} + +// 新增秒杀活动 +export const createSeckillActivity = async (data: SeckillActivityVO) => { + return await request.post({ url: '/promotion/seckill-activity/create', data }) +} + +// 修改秒杀活动 +export const updateSeckillActivity = async (data: SeckillActivityVO) => { + return await request.put({ url: '/promotion/seckill-activity/update', data }) +} + +// 关闭秒杀活动 +export const closeSeckillActivity = async (id: number) => { + return await request.put({ url: '/promotion/seckill-activity/close?id=' + id }) +} + +// 删除秒杀活动 +export const deleteSeckillActivity = async (id: number) => { + return await request.delete({ url: '/promotion/seckill-activity/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/promotion/seckill/seckillConfig.ts b/yunxi-ui-admin-vue3/src/api/mall/promotion/seckill/seckillConfig.ts new file mode 100644 index 00000000..23ad15ca --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/promotion/seckill/seckillConfig.ts @@ -0,0 +1,49 @@ +import request from '@/config/axios' + +export interface SeckillConfigVO { + id: number + name: string + startTime: string + endTime: string + sliderPicUrls: string[] + status: number +} + +// 查询秒杀时段配置列表 +export const getSeckillConfigPage = async (params) => { + return await request.get({ url: '/promotion/seckill-config/page', params }) +} + +// 查询秒杀时段配置详情 +export const getSeckillConfig = async (id: number) => { + return await request.get({ url: '/promotion/seckill-config/get?id=' + id }) +} + +// 获得所有开启状态的秒杀时段精简列表 +export const getSimpleSeckillConfigList = async () => { + return await request.get({ url: '/promotion/seckill-config/list-all-simple' }) +} + +// 新增秒杀时段配置 +export const createSeckillConfig = async (data: SeckillConfigVO) => { + return await request.post({ url: '/promotion/seckill-config/create', data }) +} + +// 修改秒杀时段配置 +export const updateSeckillConfig = async (data: SeckillConfigVO) => { + return await request.put({ url: '/promotion/seckill-config/update', data }) +} + +// 修改时段配置状态 +export const updateSeckillConfigStatus = (id: number, status: number) => { + const data = { + id, + status + } + return request.put({ url: '/promotion/seckill-config/update-status', data: data }) +} + +// 删除秒杀时段配置 +export const deleteSeckillConfig = async (id: number) => { + return await request.delete({ url: '/promotion/seckill-config/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/statistics/member.ts b/yunxi-ui-admin-vue3/src/api/mall/statistics/member.ts new file mode 100644 index 00000000..d4680d3d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/statistics/member.ts @@ -0,0 +1,91 @@ +import request from '@/config/axios' +import dayjs from 'dayjs' +import { TradeStatisticsComparisonRespVO } from '@/api/mall/statistics/trade' +import { formatDate } from '@/utils/formatTime' + +/** 会员分析 Request VO */ +export interface MemberAnalyseReqVO { + times: [dayjs.ConfigType, dayjs.ConfigType] +} + +/** 会员分析 Response VO */ +export interface MemberAnalyseRespVO { + visitorCount: number + orderUserCount: number + payUserCount: number + atv: number + comparison: TradeStatisticsComparisonRespVO +} + +/** 会员分析对照数据 Response VO */ +export interface MemberAnalyseComparisonRespVO { + userCount: number + activeUserCount: number + rechargeUserCount: number +} + +/** 会员地区统计 Response VO */ +export interface MemberAreaStatisticsRespVO { + areaId: number + areaName: string + userCount: number + orderCreateCount: number + orderPayCount: number + orderPayPrice: number +} + +/** 会员性别统计 Response VO */ +export interface MemberSexStatisticsRespVO { + sex: number + userCount: number +} + +/** 会员统计 Response VO */ +export interface MemberSummaryRespVO { + userCount: number + rechargeUserCount: number + rechargePrice: number + expensePrice: number +} + +/** 会员终端统计 Response VO */ +export interface MemberTerminalStatisticsRespVO { + terminal: number + userCount: number +} + +// 查询会员统计 +export const getMemberSummary = () => { + return request.get({ + url: '/statistics/member/summary' + }) +} + +// 查询会员分析数据 +export const getMemberAnalyse = (params: MemberAnalyseReqVO) => { + return request.get({ + url: '/statistics/member/analyse', + params: { times: [formatDate(params.times[0]), formatDate(params.times[1])] } + }) +} + +// 按照省份,查询会员统计列表 +export const getMemberAreaStatisticsList = () => { + return request.get({ + url: '/statistics/member/get-area-statistics-list' + }) +} + +// 按照性别,查询会员统计列表 +export const getMemberSexStatisticsList = () => { + return request.get({ + url: '/statistics/member/get-sex-statistics-list' + }) +} + +// 按照终端,查询会员统计列表 +export const getMemberTerminalStatisticsList = () => { + return request.get({ + url: '/statistics/member/get-terminal-statistics-list' + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/statistics/trade.ts b/yunxi-ui-admin-vue3/src/api/mall/statistics/trade.ts new file mode 100644 index 00000000..f7829ccb --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/statistics/trade.ts @@ -0,0 +1,70 @@ +import request from '@/config/axios' +import dayjs from 'dayjs' +import { formatDate } from '@/utils/formatTime' + +/** 交易统计对照 Response VO */ +export interface TradeStatisticsComparisonRespVO { + value: T + reference: T +} + +/** 交易统计 Response VO */ +export interface TradeSummaryRespVO { + yesterdayOrderCount: number + monthOrderCount: number + yesterdayPayPrice: number + monthPayPrice: number +} + +/** 交易状况 Request VO */ +export interface TradeTrendReqVO { + times: [dayjs.ConfigType, dayjs.ConfigType] +} + +/** 交易状况统计 Response VO */ +export interface TradeTrendSummaryRespVO { + time: string + turnover: number + orderPayPrice: number + rechargePrice: number + expensePrice: number + balancePrice: number + brokerageSettlementPrice: number + orderRefundPrice: number +} + +// 查询交易统计 +export const getTradeStatisticsSummary = () => { + return request.get>({ + url: '/statistics/trade/summary' + }) +} + +// 获得交易状况统计 +export const getTradeTrendSummary = (params: TradeTrendReqVO) => { + return request.get>({ + url: '/statistics/trade/trend/summary', + params: formatDateParam(params) + }) +} + +// 获得交易状况明细 +export const getTradeTrendList = (params: TradeTrendReqVO) => { + return request.get({ + url: '/statistics/trade/trend/list', + params: formatDateParam(params) + }) +} + +// 导出交易状况明细 +export const exportTradeTrend = (params: TradeTrendReqVO) => { + return request.download({ + url: '/statistics/trade/trend/export-excel', + params: formatDateParam(params) + }) +} + +/** 时间参数需要格式化, 确保接口能识别 */ +const formatDateParam = (params: TradeTrendReqVO) => { + return { times: [formatDate(params.times[0]), formatDate(params.times[1])] } as TradeTrendReqVO +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/trade/afterSale/index.ts b/yunxi-ui-admin-vue3/src/api/mall/trade/afterSale/index.ts new file mode 100644 index 00000000..a109ee6b --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/trade/afterSale/index.ts @@ -0,0 +1,75 @@ +import request from '@/config/axios' + +export interface TradeAfterSaleVO { + id?: number | null // 售后编号,主键自增 + no?: string // 售后单号 + status?: number | null // 退款状态 + way?: number | null // 售后方式 + type?: number | null // 售后类型 + userId?: number | null // 用户编号 + applyReason?: string // 申请原因 + applyDescription?: string // 补充描述 + applyPicUrls?: string[] // 补充凭证图片 + orderId?: number | null // 交易订单编号 + orderNo?: string // 订单流水号 + orderItemId?: number | null // 交易订单项编号 + spuId?: number | null // 商品 SPU 编号 + spuName?: string // 商品 SPU 名称 + skuId?: number | null // 商品 SKU 编号 + properties?: ProductPropertiesVO[] // 属性数组 + picUrl?: string // 商品图片 + count?: number | null // 退货商品数量 + auditTime?: Date // 审批时间 + auditUserId?: number | null // 审批人 + auditReason?: string // 审批备注 + refundPrice?: number | null // 退款金额,单位:分。 + payRefundId?: number | null // 支付退款编号 + refundTime?: Date // 退款时间 + logisticsId?: number | null // 退货物流公司编号 + logisticsNo?: string // 退货物流单号 + deliveryTime?: Date // 退货时间 + receiveTime?: Date // 收货时间 + receiveReason?: string // 收货备注 +} + +export interface ProductPropertiesVO { + propertyId?: number | null // 属性的编号 + propertyName?: string // 属性的名称 + valueId?: number | null //属性值的编号 + valueName?: string // 属性值的名称 +} + +// 获得交易售后分页 +export const getAfterSalePage = async (params) => { + return await request.get({ url: `/trade/after-sale/page`, params }) +} + +// 获得交易售后详情 +export const getAfterSale = async (id: any) => { + return await request.get({ url: `/trade/after-sale/get-detail?id=${id}` }) +} + +// 同意售后 +export const agree = async (id: any) => { + return await request.put({ url: `/trade/after-sale/agree?id=${id}` }) +} + +// 拒绝售后 +export const disagree = async (data: any) => { + return await request.put({ url: `/trade/after-sale/disagree`, data }) +} + +// 确认收货 +export const receive = async (id: any) => { + return await request.put({ url: `/trade/after-sale/receive?id=${id}` }) +} + +// 拒绝收货 +export const refuse = async (id: any) => { + return await request.put({ url: `/trade/after-sale/refuse?id=${id}` }) +} + +// 确认退款 +export const refund = async (id: any) => { + return await request.put({ url: `/trade/after-sale/refund?id=${id}` }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/record/index.ts b/yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/record/index.ts new file mode 100644 index 00000000..7df9a225 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/record/index.ts @@ -0,0 +1,11 @@ +import request from '@/config/axios' + +// 查询佣金记录列表 +export const getBrokerageRecordPage = async (params: any) => { + return await request.get({ url: `/trade/brokerage-record/page`, params }) +} + +// 查询佣金记录详情 +export const getBrokerageRecord = async (id: number) => { + return await request.get({ url: `/trade/brokerage-record/get?id=` + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/user/index.ts b/yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/user/index.ts new file mode 100644 index 00000000..1fed3bfa --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/user/index.ts @@ -0,0 +1,39 @@ +import request from '@/config/axios' + +export interface BrokerageUserVO { + id: number + bindUserId: number + bindUserTime: Date + brokerageEnabled: boolean + brokerageTime: Date + price: number + frozenPrice: number + + nickname: string + avatar: string +} + +// 查询分销用户列表 +export const getBrokerageUserPage = async (params: any) => { + return await request.get({ url: `/trade/brokerage-user/page`, params }) +} + +// 查询分销用户详情 +export const getBrokerageUser = async (id: number) => { + return await request.get({ url: `/trade/brokerage-user/get?id=` + id }) +} + +// 修改推广员 +export const updateBindUser = async (data: any) => { + return await request.put({ url: `/trade/brokerage-user/update-bind-user`, data }) +} + +// 清除推广员 +export const clearBindUser = async (data: any) => { + return await request.put({ url: `/trade/brokerage-user/clear-bind-user`, data }) +} + +// 修改推广资格 +export const updateBrokerageEnabled = async (data: any) => { + return await request.put({ url: `/trade/brokerage-user/update-brokerage-enable`, data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/withdraw/index.ts b/yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/withdraw/index.ts new file mode 100644 index 00000000..c93286a9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/trade/brokerage/withdraw/index.ts @@ -0,0 +1,39 @@ +import request from '@/config/axios' + +export interface BrokerageWithdrawVO { + id: number + userId: number + price: number + feePrice: number + totalPrice: number + type: number + name: string + accountNo: string + bankName: string + bankAddress: string + accountQrCodeUrl: string + status: number + auditReason: string + auditTime: Date + remark: string +} + +// 查询佣金提现列表 +export const getBrokerageWithdrawPage = async (params: any) => { + return await request.get({ url: `/trade/brokerage-withdraw/page`, params }) +} + +// 查询佣金提现详情 +export const getBrokerageWithdraw = async (id: number) => { + return await request.get({ url: `/trade/brokerage-withdraw/get?id=` + id }) +} + +// 佣金提现 - 通过申请 +export const approveBrokerageWithdraw = async (id: number) => { + return await request.put({ url: `/trade/brokerage-withdraw/approve?id=` + id }) +} + +// 审核佣金提现 - 驳回申请 +export const rejectBrokerageWithdraw = async (data: BrokerageWithdrawVO) => { + return await request.put({ url: `/trade/brokerage-withdraw/reject`, data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/trade/config/index.ts b/yunxi-ui-admin-vue3/src/api/mall/trade/config/index.ts new file mode 100644 index 00000000..66a81147 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/trade/config/index.ts @@ -0,0 +1,24 @@ +import request from '@/config/axios' + +export interface ConfigVO { + brokerageEnabled: boolean + brokerageEnabledCondition: number + brokerageBindMode: number + brokeragePosterUrls: string + brokerageFirstPercent: number + brokerageSecondPercent: number + brokerageWithdrawMinPrice: number + brokerageBankNames: string + brokerageFrozenDays: number + brokerageWithdrawTypes: string +} + +// 查询交易中心配置详情 +export const getTradeConfig = async () => { + return await request.get({ url: `/trade/config/get` }) +} + +// 保存交易中心配置 +export const saveTradeConfig = async (data: ConfigVO) => { + return await request.put({ url: `/trade/config/save`, data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/trade/delivery/express/index.ts b/yunxi-ui-admin-vue3/src/api/mall/trade/delivery/express/index.ts new file mode 100644 index 00000000..0070bcd6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/trade/delivery/express/index.ts @@ -0,0 +1,45 @@ +import request from '@/config/axios' + +export interface DeliveryExpressVO { + id: number + code: string + name: string + logo: string + sort: number + status: number +} + +// 查询快递公司列表 +export const getDeliveryExpressPage = async (params: PageParam) => { + return await request.get({ url: '/trade/delivery/express/page', params }) +} + +// 查询快递公司详情 +export const getDeliveryExpress = async (id: number) => { + return await request.get({ url: '/trade/delivery/express/get?id=' + id }) +} + +// 获得快递公司精简信息列表 +export const getSimpleDeliveryExpressList = () => { + return request.get({ url: '/trade/delivery/express/list-all-simple' }) +} + +// 新增快递公司 +export const createDeliveryExpress = async (data: DeliveryExpressVO) => { + return await request.post({ url: '/trade/delivery/express/create', data }) +} + +// 修改快递公司 +export const updateDeliveryExpress = async (data: DeliveryExpressVO) => { + return await request.put({ url: '/trade/delivery/express/update', data }) +} + +// 删除快递公司 +export const deleteDeliveryExpress = async (id: number) => { + return await request.delete({ url: '/trade/delivery/express/delete?id=' + id }) +} + +// 导出快递公司 Excel +export const exportDeliveryExpressApi = async (params) => { + return await request.download({ url: '/trade/delivery/express/export-excel', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/trade/delivery/expressTemplate/index.ts b/yunxi-ui-admin-vue3/src/api/mall/trade/delivery/expressTemplate/index.ts new file mode 100644 index 00000000..9ed23bc1 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/trade/delivery/expressTemplate/index.ts @@ -0,0 +1,54 @@ +import request from '@/config/axios' + +export interface DeliveryExpressTemplateVO { + id: number + name: string + chargeMode: number + sort: number + templateCharge: ExpressTemplateChargeVO[] + templateFree: ExpressTemplateFreeVO[] +} + +export declare type ExpressTemplateChargeVO = { + areaIds: number[] + startCount: number + startPrice: number + extraCount: number + extraPrice: number +} + +export declare type ExpressTemplateFreeVO = { + areaIds: number[] + freeCount: number + freePrice: number +} + +// 查询快递运费模板列表 +export const getDeliveryExpressTemplatePage = async (params: PageParam) => { + return await request.get({ url: '/trade/delivery/express-template/page', params }) +} + +// 查询快递运费模板详情 +export const getDeliveryExpressTemplate = async (id: number) => { + return await request.get({ url: '/trade/delivery/express-template/get?id=' + id }) +} + +// 查询快递运费模板详情 +export const getSimpleTemplateList = async () => { + return await request.get({ url: '/trade/delivery/express-template/list-all-simple' }) +} + +// 新增快递运费模板 +export const createDeliveryExpressTemplate = async (data: DeliveryExpressTemplateVO) => { + return await request.post({ url: '/trade/delivery/express-template/create', data }) +} + +// 修改快递运费模板 +export const updateDeliveryExpressTemplate = async (data: DeliveryExpressTemplateVO) => { + return await request.put({ url: '/trade/delivery/express-template/update', data }) +} + +// 删除快递运费模板 +export const deleteDeliveryExpressTemplate = async (id: number) => { + return await request.delete({ url: '/trade/delivery/express-template/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/trade/delivery/pickUpStore/index.ts b/yunxi-ui-admin-vue3/src/api/mall/trade/delivery/pickUpStore/index.ts new file mode 100644 index 00000000..82ba66c4 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/trade/delivery/pickUpStore/index.ts @@ -0,0 +1,46 @@ +import request from '@/config/axios' + +export interface DeliveryPickUpStoreVO { + id: number + name: string + introduction: string + phone: string + areaId: number + detailAddress: string + logo: string + openingTime: string + closingTime: string + latitude: number + longitude: number + status: number +} + +// 查询自提门店列表 +export const getDeliveryPickUpStorePage = async (params) => { + return await request.get({ url: '/trade/delivery/pick-up-store/page', params }) +} + +// 查询自提门店详情 +export const getDeliveryPickUpStore = async (id: number) => { + return await request.get({ url: '/trade/delivery/pick-up-store/get?id=' + id }) +} + +// 查询自提门店精简列表 +export const getListAllSimple = async () => { + return await request.get({ url: '/trade/delivery/pick-up-store/list-all-simple' }) +} + +// 新增自提门店 +export const createDeliveryPickUpStore = async (data: DeliveryPickUpStoreVO) => { + return await request.post({ url: '/trade/delivery/pick-up-store/create', data }) +} + +// 修改自提门店 +export const updateDeliveryPickUpStore = async (data: DeliveryPickUpStoreVO) => { + return await request.put({ url: '/trade/delivery/pick-up-store/update', data }) +} + +// 删除自提门店 +export const deleteDeliveryPickUpStore = async (id: number) => { + return await request.delete({ url: '/trade/delivery/pick-up-store/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mall/trade/order/index.ts b/yunxi-ui-admin-vue3/src/api/mall/trade/order/index.ts new file mode 100644 index 00000000..ea78275f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mall/trade/order/index.ts @@ -0,0 +1,146 @@ +import request from '@/config/axios' + +export interface OrderVO { + id?: number | null // 订单编号 + no?: string // 订单流水号 + createTime?: Date | null // 下单时间 + type?: number | null // 订单类型 + terminal?: number | null // 订单来源 + userId?: number | null // 用户编号 + userIp?: string // 用户 IP + userRemark?: string // 用户备注 + status?: number | null // 订单状态 + productCount?: number | null // 购买的商品数量 + finishTime?: Date | null // 订单完成时间 + cancelTime?: Date | null // 订单取消时间 + cancelType?: number | null // 取消类型 + remark?: string // 商家备注 + payOrderId?: number | null // 支付订单编号 + payed?: boolean // 是否已支付 + payTime?: Date | null // 付款时间 + payChannelCode?: string // 支付渠道 + totalPrice?: number | null // 商品原价(总) + orderPrice?: number | null // 订单原价(总) + discountPrice?: number | null // 订单优惠(总) + deliveryPrice?: number | null // 运费金额 + adjustPrice?: number | null // 订单调价(总) + payPrice?: number | null // 应付金额(总) + deliveryType?: number | null // 发货方式 + deliveryTemplateId?: number | null // 配送模板编号 + logisticsId?: number | null | null // 发货物流公司编号 + logisticsNo?: string // 发货物流单号 + deliveryStatus?: number | null // 发货状态 + deliveryTime?: Date | null // 发货时间 + receiveTime?: Date | null // 收货时间 + receiverName?: string // 收件人名称 + receiverMobile?: string // 收件人手机 + receiverAreaId?: number | null // 收件人地区编号 + receiverPostCode?: number | null // 收件人邮编 + receiverDetailAddress?: string // 收件人详细地址 + afterSaleStatus?: number | null // 售后状态 + refundPrice?: number | null // 退款金额 + couponId?: number | null // 优惠劵编号 + couponPrice?: number | null // 优惠劵减免金额 + vipPrice?: number | null // VIP 减免金额 + pointPrice?: number | null // 积分抵扣的金额 + receiverAreaName?: string //收件人地区名字 + items?: OrderItemRespVO[] // 订单项列表 + // 下单用户信息 + user?: { + id?: number | null + nickname?: string + avatar?: string + } + // 推广用户信息 + brokerageUser?: { + id?: number | null + nickname?: string + avatar?: string + } + // 订单操作日志 + logs?: OrderLogRespVO[] +} + +export interface OrderLogRespVO { + content?: string + createTime?: Date + userType?: number +} + +export interface OrderItemRespVO { + // ========== 订单项基本信息 ========== + id?: number | null // 编号 + userId?: number | null // 用户编号 + orderId?: number | null // 订单编号 + // ========== 商品基本信息 ========== + spuId?: number | null // 商品 SPU 编号 + spuName?: string //商品 SPU 名称 + skuId?: number | null // 商品 SKU 编号 + picUrl?: string //商品图片 + count?: number | null //购买数量 + // ========== 价格 + 支付基本信息 ========== + originalPrice?: number | null //商品原价(总) + originalUnitPrice?: number | null //商品原价(单) + discountPrice?: number | null //商品优惠(总) + payPrice?: number | null //商品实付金额(总) + orderPartPrice?: number | null //子订单分摊金额(总) + orderDividePrice?: number | null //分摊后子订单实付金额(总) + // ========== 营销基本信息 ========== + // TODO 芋艿:在捉摸一下 + // ========== 售后基本信息 ========== + afterSaleStatus?: number | null // 售后状态 + properties?: ProductPropertiesVO[] //属性数组 +} + +export interface ProductPropertiesVO { + propertyId?: number | null // 属性的编号 + propertyName?: string // 属性的名称 + valueId?: number | null //属性值的编号 + valueName?: string // 属性值的名称 +} + +// 查询交易订单列表 +export const getOrderPage = async (params) => { + return await request.get({ url: `/trade/order/page`, params }) +} + +// 查询交易订单详情 +export const getOrder = async (id: number | null) => { + return await request.get({ url: `/trade/order/get-detail?id=` + id }) +} + +// 查询交易订单物流详情 +export const getExpressTrackList = async (id: number | null) => { + return await request.get({ url: `/trade/order/get-express-track-list?id=` + id }) +} + +export interface DeliveryVO { + id: number // 订单编号 + logisticsId: number | null // 物流公司编号 + logisticsNo: string // 物流编号 +} + +// 订单发货 +export const deliveryOrder = async (data: DeliveryVO) => { + return await request.put({ url: `/trade/order/delivery`, data }) +} + +// 订单备注 +export const updateOrderRemark = async (data: any) => { + return await request.put({ url: `/trade/order/update-remark`, data }) +} + +// 订单调价 +export const updateOrderPrice = async (data: any) => { + return await request.put({ url: `/trade/order/update-price`, data }) +} + +// 修改订单地址 +export const updateOrderAddress = async (data: any) => { + return await request.put({ url: `/trade/order/update-address`, data }) +} + +// 订单核销 +export const pickUpOrder = async (id: number) => { + return await request.put({ url: `/trade/order/pick-up?id=${id}` }) +} diff --git a/yunxi-ui-admin-vue3/src/api/member/address/index.ts b/yunxi-ui-admin-vue3/src/api/member/address/index.ts new file mode 100644 index 00000000..a914f979 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/member/address/index.ts @@ -0,0 +1,15 @@ +import request from '@/config/axios' + +export interface AddressVO { + id: number + name: string + mobile: string + areaId: number + detailAddress: string + defaultStatus: boolean +} + +// 查询用户收件地址列表 +export const getAddressList = async (params) => { + return await request.get({ url: `/member/address/list`, params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/member/config/index.ts b/yunxi-ui-admin-vue3/src/api/member/config/index.ts new file mode 100644 index 00000000..7ddca16b --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/member/config/index.ts @@ -0,0 +1,19 @@ +import request from '@/config/axios' + +export interface ConfigVO { + id: number + pointTradeDeductEnable: number + pointTradeDeductUnitPrice: number + pointTradeDeductMaxPrice: number + pointTradeGivePoint: number +} + +// 查询积分设置详情 +export const getConfig = async () => { + return await request.get({ url: `/member/config/get` }) +} + +// 新增修改积分设置 +export const saveConfig = async (data: ConfigVO) => { + return await request.put({ url: `/member/config/save`, data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/member/experience-record/index.ts b/yunxi-ui-admin-vue3/src/api/member/experience-record/index.ts new file mode 100644 index 00000000..6d40a48d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/member/experience-record/index.ts @@ -0,0 +1,22 @@ +import request from '@/config/axios' + +export interface ExperienceRecordVO { + id: number + userId: number + bizId: string + bizType: number + title: string + description: string + experience: number + totalExperience: number +} + +// 查询会员经验记录列表 +export const getExperienceRecordPage = async (params) => { + return await request.get({ url: `/member/experience-record/page`, params }) +} + +// 查询会员经验记录详情 +export const getExperienceRecord = async (id: number) => { + return await request.get({ url: `/member/experience-record/get?id=` + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/member/group/index.ts b/yunxi-ui-admin-vue3/src/api/member/group/index.ts new file mode 100644 index 00000000..df3054e2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/member/group/index.ts @@ -0,0 +1,38 @@ +import request from '@/config/axios' + +export interface GroupVO { + id: number + name: string + remark: string + status: number +} + +// 查询用户分组列表 +export const getGroupPage = async (params: any) => { + return await request.get({ url: `/member/group/page`, params }) +} + +// 查询用户分组详情 +export const getGroup = async (id: number) => { + return await request.get({ url: `/member/group/get?id=` + id }) +} + +// 新增用户分组 +export const createGroup = async (data: GroupVO) => { + return await request.post({ url: `/member/group/create`, data }) +} + +// 查询用户分组 - 精简信息列表 +export const getSimpleGroupList = async () => { + return await request.get({ url: `/member/group/list-all-simple` }) +} + +// 修改用户分组 +export const updateGroup = async (data: GroupVO) => { + return await request.put({ url: `/member/group/update`, data }) +} + +// 删除用户分组 +export const deleteGroup = async (id: number) => { + return await request.delete({ url: `/member/group/delete?id=` + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/member/level/index.ts b/yunxi-ui-admin-vue3/src/api/member/level/index.ts new file mode 100644 index 00000000..0ded493a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/member/level/index.ts @@ -0,0 +1,42 @@ +import request from '@/config/axios' + +export interface LevelVO { + id: number + name: string + experience: number + value: number + discountPercent: number + icon: string + bgUrl: string + status: number +} + +// 查询会员等级列表 +export const getLevelList = async (params) => { + return await request.get({ url: `/member/level/list`, params }) +} + +// 查询会员等级详情 +export const getLevel = async (id: number) => { + return await request.get({ url: `/member/level/get?id=` + id }) +} + +// 查询会员等级 - 精简信息列表 +export const getSimpleLevelList = async () => { + return await request.get({ url: `/member/level/list-all-simple` }) +} + +// 新增会员等级 +export const createLevel = async (data: LevelVO) => { + return await request.post({ url: `/member/level/create`, data }) +} + +// 修改会员等级 +export const updateLevel = async (data: LevelVO) => { + return await request.put({ url: `/member/level/update`, data }) +} + +// 删除会员等级 +export const deleteLevel = async (id: number) => { + return await request.delete({ url: `/member/level/delete?id=` + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/member/point/record/index.ts b/yunxi-ui-admin-vue3/src/api/member/point/record/index.ts new file mode 100644 index 00000000..f47ae467 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/member/point/record/index.ts @@ -0,0 +1,18 @@ +import request from '@/config/axios' + +export interface RecordVO { + id: number + bizId: string + bizType: string + title: string + description: string + point: number + totalPoint: number + userId: number + createDate: Date +} + +// 查询用户积分记录列表 +export const getRecordPage = async (params) => { + return await request.get({ url: `/member/point/record/page`, params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/member/signin/config/index.ts b/yunxi-ui-admin-vue3/src/api/member/signin/config/index.ts new file mode 100644 index 00000000..50a7d63c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/member/signin/config/index.ts @@ -0,0 +1,34 @@ +import request from '@/config/axios' + +export interface SignInConfigVO { + id?: number + day?: number + point?: number + experience?: number + status?: number +} + +// 查询积分签到规则列表 +export const getSignInConfigList = async () => { + return await request.get({ url: `/member/sign-in/config/list` }) +} + +// 查询积分签到规则详情 +export const getSignInConfig = async (id: number) => { + return await request.get({ url: `/member/sign-in/config/get?id=` + id }) +} + +// 新增积分签到规则 +export const createSignInConfig = async (data: SignInConfigVO) => { + return await request.post({ url: `/member/sign-in/config/create`, data }) +} + +// 修改积分签到规则 +export const updateSignInConfig = async (data: SignInConfigVO) => { + return await request.put({ url: `/member/sign-in/config/update`, data }) +} + +// 删除积分签到规则 +export const deleteSignInConfig = async (id: number) => { + return await request.delete({ url: `/member/sign-in/config/delete?id=` + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/member/signin/record/index.ts b/yunxi-ui-admin-vue3/src/api/member/signin/record/index.ts new file mode 100644 index 00000000..7d137029 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/member/signin/record/index.ts @@ -0,0 +1,13 @@ +import request from '@/config/axios' + +export interface SignInRecordVO { + id: number + userId: number + day: number + point: number +} + +// 查询用户签到积分列表 +export const getSignInRecordPage = async (params) => { + return await request.get({ url: `/member/sign-in/record/page`, params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/member/tag/index.ts b/yunxi-ui-admin-vue3/src/api/member/tag/index.ts new file mode 100644 index 00000000..7ff6e9bf --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/member/tag/index.ts @@ -0,0 +1,36 @@ +import request from '@/config/axios' + +export interface TagVO { + id: number + name: string +} + +// 查询会员标签列表 +export const getMemberTagPage = async (params: any) => { + return await request.get({ url: `/member/tag/page`, params }) +} + +// 查询会员标签详情 +export const getMemberTag = async (id: number) => { + return await request.get({ url: `/member/tag/get?id=` + id }) +} + +// 查询会员标签 - 精简信息列表 +export const getSimpleTagList = async () => { + return await request.get({ url: `/member/tag/list-all-simple` }) +} + +// 新增会员标签 +export const createMemberTag = async (data: TagVO) => { + return await request.post({ url: `/member/tag/create`, data }) +} + +// 修改会员标签 +export const updateMemberTag = async (data: TagVO) => { + return await request.put({ url: `/member/tag/update`, data }) +} + +// 删除会员标签 +export const deleteMemberTag = async (id: number) => { + return await request.delete({ url: `/member/tag/delete?id=` + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/member/user/index.ts b/yunxi-ui-admin-vue3/src/api/member/user/index.ts new file mode 100644 index 00000000..e38206a8 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/member/user/index.ts @@ -0,0 +1,53 @@ +import request from '@/config/axios' + +export interface UserVO { + id: number + avatar: string | undefined + birthday: number | undefined + createTime: number | undefined + loginDate: number | undefined + loginIp: string + mark: string + mobile: string + name: string | undefined + nickname: string | undefined + registerIp: string + sex: number + status: number + areaId: number | undefined + areaName: string | undefined + levelName: string | null + point: number | undefined | null + totalPoint: number | undefined | null + experience: number | null | undefined +} + +// 查询会员用户列表 +export const getUserPage = async (params) => { + return await request.get({ url: `/member/user/page`, params }) +} + +// 查询会员用户详情 +export const getUser = async (id: number) => { + return await request.get({ url: `/member/user/get?id=` + id }) +} + +// 修改会员用户 +export const updateUser = async (data: UserVO) => { + return await request.put({ url: `/member/user/update`, data }) +} + +// 修改会员用户等级 +export const updateUserLevel = async (data: any) => { + return await request.put({ url: `/member/user/update-level`, data }) +} + +// 修改会员用户积分 +export const updateUserPoint = async (data: any) => { + return await request.put({ url: `/member/user/update-point`, data }) +} + +// 修改会员用户余额 +export const updateUserBalance = async (data: any) => { + return await request.put({ url: `/member/user/update-balance`, data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mp/account/index.ts b/yunxi-ui-admin-vue3/src/api/mp/account/index.ts new file mode 100644 index 00000000..e973cda3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mp/account/index.ts @@ -0,0 +1,46 @@ +import request from '@/config/axios' + +export interface AccountVO { + id: number + name: string +} + +// 创建公众号账号 +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 getSimpleAccountList = 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/yunxi-ui-admin-vue3/src/api/mp/autoReply/index.ts b/yunxi-ui-admin-vue3/src/api/mp/autoReply/index.ts new file mode 100644 index 00000000..5045e6d5 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/api/mp/draft/index.ts b/yunxi-ui-admin-vue3/src/api/mp/draft/index.ts new file mode 100644 index 00000000..ce6a4431 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/api/mp/freePublish/index.ts b/yunxi-ui-admin-vue3/src/api/mp/freePublish/index.ts new file mode 100644 index 00000000..beef0262 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/api/mp/material/index.ts b/yunxi-ui-admin-vue3/src/api/mp/material/index.ts new file mode 100644 index 00000000..fcc37abe --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/api/mp/menu/index.ts b/yunxi-ui-admin-vue3/src/api/mp/menu/index.ts new file mode 100644 index 00000000..cc78647c --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/api/mp/message/index.ts b/yunxi-ui-admin-vue3/src/api/mp/message/index.ts new file mode 100644 index 00000000..ad9b95dd --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mp/message/index.ts @@ -0,0 +1,17 @@ +import request from '@/config/axios' + +// 获得公众号消息分页 +export const getMessagePage = (query: PageParam) => { + 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/yunxi-ui-admin-vue3/src/api/mp/statistics/index.ts b/yunxi-ui-admin-vue3/src/api/mp/statistics/index.ts new file mode 100644 index 00000000..72cae601 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/api/mp/tag/index.ts b/yunxi-ui-admin-vue3/src/api/mp/tag/index.ts new file mode 100644 index 00000000..50183a51 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mp/tag/index.ts @@ -0,0 +1,60 @@ +import request from '@/config/axios' + +export interface TagVO { + id?: number + name: string + accountId: number + createTime: Date +} + +// 创建公众号标签 +export const createTag = (data: TagVO) => { + return request.post({ + url: '/mp/tag/create', + data: data + }) +} + +// 更新公众号标签 +export const updateTag = (data: TagVO) => { + return request.put({ + url: '/mp/tag/update', + data: data + }) +} + +// 删除公众号标签 +export const deleteTag = (id: number) => { + return request.delete({ + url: '/mp/tag/delete?id=' + id + }) +} + +// 获得公众号标签 +export const getTag = (id: number) => { + return request.get({ + url: '/mp/tag/get?id=' + id + }) +} + +// 获得公众号标签分页 +export const getTagPage = (query: PageParam) => { + return request.get({ + url: '/mp/tag/page', + params: query + }) +} + +// 获取公众号标签精简信息列表 +export const getSimpleTagList = () => { + return request.get({ + url: '/mp/tag/list-all-simple' + }) +} + +// 同步公众号标签 +export const syncTag = (accountId: number) => { + return request.post({ + url: '/mp/tag/sync?accountId=' + accountId + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/mp/user/index.ts b/yunxi-ui-admin-vue3/src/api/mp/user/index.ts new file mode 100644 index 00000000..d954e9eb --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/mp/user/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/yunxi-ui-admin-vue3/src/api/pay/app/index.ts b/yunxi-ui-admin-vue3/src/api/pay/app/index.ts new file mode 100644 index 00000000..4bb06b36 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/pay/app/index.ts @@ -0,0 +1,65 @@ +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 AppUpdateStatusReqVO { + id: number + status: number +} + +// 查询列表支付应用 +export const getAppPage = (params: AppPageReqVO) => { + return request.get({ url: '/pay/app/page', params }) +} + +// 查询详情支付应用 +export const getApp = (id: number) => { + return request.get({ url: '/pay/app/get?id=' + id }) +} + +// 新增支付应用 +export const createApp = (data: AppVO) => { + return request.post({ url: '/pay/app/create', data }) +} + +// 修改支付应用 +export const updateApp = (data: AppVO) => { + return request.put({ url: '/pay/app/update', data }) +} + +// 支付应用信息状态修改 +export const changeAppStatus = (data: AppUpdateStatusReqVO) => { + return request.put({ url: '/pay/app/update-status', data: data }) +} + +// 删除支付应用 +export const deleteApp = (id: number) => { + return request.delete({ url: '/pay/app/delete?id=' + id }) +} + +// 获得支付应用列表 +export const getAppList = () => { + return request.get({ + url: '/pay/app/list' + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/pay/channel/index.ts b/yunxi-ui-admin-vue3/src/api/pay/channel/index.ts new file mode 100644 index 00000000..0f4ff424 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/pay/channel/index.ts @@ -0,0 +1,46 @@ +import request from '@/config/axios' + +export interface ChannelVO { + id: number + code: string + config: string + status: number + remark: string + feeRate: number + appId: number + createTime: Date +} + +// 查询列表支付渠道 +export const getChannelPage = (params: PageParam) => { + return request.get({ url: '/pay/channel/page', params }) +} + +// 查询详情支付渠道 +export const getChannel = (appId: string, code: string) => { + const params = { + appId: appId, + code: code + } + return request.get({ url: '/pay/channel/get', params: params }) +} + +// 新增支付渠道 +export const createChannel = (data: ChannelVO) => { + return request.post({ url: '/pay/channel/create', data }) +} + +// 修改支付渠道 +export const updateChannel = (data: ChannelVO) => { + return request.put({ url: '/pay/channel/update', data }) +} + +// 删除支付渠道 +export const deleteChannel = (id: number) => { + return request.delete({ url: '/pay/channel/delete?id=' + id }) +} + +// 导出支付渠道 +export const exportChannel = (params) => { + return request.download({ url: '/pay/channel/export-excel', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/pay/demo/index.ts b/yunxi-ui-admin-vue3/src/api/pay/demo/index.ts new file mode 100644 index 00000000..3824a8b2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/pay/demo/index.ts @@ -0,0 +1,36 @@ +import request from '@/config/axios' + +export interface DemoOrderVO { + spuId: number + createTime: Date +} + +// 创建示例订单 +export function createDemoOrder(data: DemoOrderVO) { + return request.post({ + url: '/pay/demo-order/create', + data: data + }) +} + +// 获得示例订单 +export function getDemoOrder(id: number) { + return request.get({ + url: '/pay/demo-order/get?id=' + id + }) +} + +// 获得示例订单分页 +export function getDemoOrderPage(query: PageParam) { + return request.get({ + url: '/pay/demo-order/page', + params: query + }) +} + +// 退款示例订单 +export function refundDemoOrder(id) { + return request.put({ + url: '/pay/demo-order/refund?id=' + id + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/pay/notify/index.ts b/yunxi-ui-admin-vue3/src/api/pay/notify/index.ts new file mode 100644 index 00000000..dc8bd887 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/pay/notify/index.ts @@ -0,0 +1,16 @@ +import request from '@/config/axios' + +// 获得支付通知明细 +export const getNotifyTaskDetail = (id) => { + return request.get({ + url: '/pay/notify/get-detail?id=' + id + }) +} + +// 获得支付通知分页 +export const getNotifyTaskPage = (query) => { + return request.get({ + url: '/pay/notify/page', + params: query + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/pay/order/index.ts b/yunxi-ui-admin-vue3/src/api/pay/order/index.ts new file mode 100644 index 00000000..71960a8a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/pay/order/index.ts @@ -0,0 +1,104 @@ +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 getOrderPage = async (params: OrderPageReqVO) => { + return await request.get({ url: '/pay/order/page', params }) +} + +// 查询详情支付订单 +export const getOrder = async (id: number) => { + return await request.get({ url: '/pay/order/get?id=' + id }) +} + +// 获得支付订单的明细 +export const getOrderDetail = async (id: number) => { + return await request.get({ url: '/pay/order/get-detail?id=' + id }) +} + +// 提交支付订单 +export const submitOrder = async (data: any) => { + return await request.post({ url: '/pay/order/submit', data }) +} + +// 导出支付订单 +export const exportOrder = async (params: OrderExportReqVO) => { + return await request.download({ url: '/pay/order/export-excel', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/pay/refund/index.ts b/yunxi-ui-admin-vue3/src/api/pay/refund/index.ts new file mode 100644 index 00000000..4b587f22 --- /dev/null +++ b/yunxi-ui-admin-vue3/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 getRefundPage = (params: RefundPageReqVO) => { + return request.get({ url: '/pay/refund/page', params }) +} + +// 查询详情退款订单 +export const getRefund = (id: number) => { + return request.get({ url: '/pay/refund/get?id=' + id }) +} + +// 新增退款订单 +export const createRefund = (data: RefundVO) => { + return request.post({ url: '/pay/refund/create', data }) +} + +// 修改退款订单 +export const updateRefund = (data: RefundVO) => { + return request.put({ url: '/pay/refund/update', data }) +} + +// 删除退款订单 +export const deleteRefund = (id: number) => { + return request.delete({ url: '/pay/refund/delete?id=' + id }) +} + +// 导出退款订单 +export const exportRefund = (params: PayRefundExportReqVO) => { + return request.download({ url: '/pay/refund/export-excel', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/pay/wallet/index.ts b/yunxi-ui-admin-vue3/src/api/pay/wallet/index.ts new file mode 100644 index 00000000..b57deeb0 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/pay/wallet/index.ts @@ -0,0 +1,22 @@ +import request from '@/config/axios' + +/** 用户钱包查询参数 */ +export interface PayWalletUserReqVO { + userId: number + userType: number +} +/** 钱包 VO */ +export interface WalletVO { + id: number + userId: number + userType: number + balance: number + totalExpense: number + totalRecharge: number + freezePrice: number +} + +/** 查询用户钱包详情 */ +export const getWallet = async (params: PayWalletUserReqVO) => { + return await request.get({ url: `/pay/wallet/get`, params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/area/index.ts b/yunxi-ui-admin-vue3/src/api/system/area/index.ts new file mode 100644 index 00000000..e91a4997 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/area/index.ts @@ -0,0 +1,11 @@ +import request from '@/config/axios' + +// 获得地区树 +export const getAreaTree = async () => { + return await request.get({ url: '/system/area/tree' }) +} + +// 获得 IP 对应的地区名 +export const getAreaByIp = async (ip: string) => { + return await request.get({ url: '/system/area/get-by-ip?ip=' + ip }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/dept/index.ts b/yunxi-ui-admin-vue3/src/api/system/dept/index.ts new file mode 100644 index 00000000..d995f13d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/dept/index.ts @@ -0,0 +1,43 @@ +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 const getSimpleDeptList = async (): Promise => { + return await request.get({ url: '/system/dept/list-all-simple' }) +} + +// 查询部门列表 +export const getDeptPage = async (params: PageParam) => { + return await request.get({ url: '/system/dept/list', params }) +} + +// 查询部门详情 +export const getDept = async (id: number) => { + return await request.get({ url: '/system/dept/get?id=' + id }) +} + +// 新增部门 +export const createDept = async (data: DeptVO) => { + return await request.post({ url: '/system/dept/create', data: data }) +} + +// 修改部门 +export const updateDept = async (params: DeptVO) => { + return await request.put({ url: '/system/dept/update', data: params }) +} + +// 删除部门 +export const deleteDept = async (id: number) => { + return await request.delete({ url: '/system/dept/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/dict/dict.data.ts b/yunxi-ui-admin-vue3/src/api/system/dict/dict.data.ts new file mode 100644 index 00000000..87e7dce7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/dict/dict.data.ts @@ -0,0 +1,49 @@ +import request from '@/config/axios' + +export type DictDataVO = { + id: number | undefined + sort: number | undefined + label: string + value: string + dictType: string + status: number + colorType: string + cssClass: string + remark: string + createTime: Date +} + +// 查询字典数据(精简)列表 +export const listSimpleDictData = () => { + return request.get({ url: '/system/dict-data/list-all-simple' }) +} + +// 查询字典数据列表 +export const getDictDataPage = (params: PageParam) => { + return request.get({ url: '/system/dict-data/page', params }) +} + +// 查询字典数据详情 +export const getDictData = (id: number) => { + return request.get({ url: '/system/dict-data/get?id=' + id }) +} + +// 新增字典数据 +export const createDictData = (data: DictDataVO) => { + return request.post({ url: '/system/dict-data/create', data }) +} + +// 修改字典数据 +export const updateDictData = (data: DictDataVO) => { + return request.put({ url: '/system/dict-data/update', data }) +} + +// 删除字典数据 +export const deleteDictData = (id: number) => { + return request.delete({ url: '/system/dict-data/delete?id=' + id }) +} + +// 导出字典类型数据 +export const exportDictData = (params) => { + return request.get({ url: '/system/dict-data/export', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/dict/dict.type.ts b/yunxi-ui-admin-vue3/src/api/system/dict/dict.type.ts new file mode 100644 index 00000000..ed2969f1 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/dict/dict.type.ts @@ -0,0 +1,44 @@ +import request from '@/config/axios' + +export type DictTypeVO = { + id: number | undefined + name: string + type: string + status: number + remark: string + createTime: Date +} + +// 查询字典(精简)列表 +export const getSimpleDictTypeList = () => { + return request.get({ url: '/system/dict-type/list-all-simple' }) +} + +// 查询字典列表 +export const getDictTypePage = (params: PageParam) => { + return request.get({ url: '/system/dict-type/page', params }) +} + +// 查询字典详情 +export const getDictType = (id: number) => { + return request.get({ url: '/system/dict-type/get?id=' + id }) +} + +// 新增字典 +export const createDictType = (data: DictTypeVO) => { + return request.post({ url: '/system/dict-type/create', data }) +} + +// 修改字典 +export const updateDictType = (data: DictTypeVO) => { + return request.put({ url: '/system/dict-type/update', data }) +} + +// 删除字典 +export const deleteDictType = (id: number) => { + return request.delete({ url: '/system/dict-type/delete?id=' + id }) +} +// 导出字典类型 +export const exportDictType = (params) => { + return request.get({ url: '/system/dict-type/export', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/errorCode/index.ts b/yunxi-ui-admin-vue3/src/api/system/errorCode/index.ts new file mode 100644 index 00000000..8a86a639 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/errorCode/index.ts @@ -0,0 +1,40 @@ +import request from '@/config/axios' + +export interface ErrorCodeVO { + id: number | undefined + type: number + applicationName: string + code: number | undefined + message: string + memo: string + createTime: Date +} + +// 查询错误码列表 +export const getErrorCodePage = (params: PageParam) => { + return request.get({ url: '/system/error-code/page', params }) +} + +// 查询错误码详情 +export const getErrorCode = (id: number) => { + return request.get({ url: '/system/error-code/get?id=' + id }) +} + +// 新增错误码 +export const createErrorCode = (data: ErrorCodeVO) => { + return request.post({ url: '/system/error-code/create', data }) +} + +// 修改错误码 +export const updateErrorCode = (data: ErrorCodeVO) => { + return request.put({ url: '/system/error-code/update', data }) +} + +// 删除错误码 +export const deleteErrorCode = (id: number) => { + return request.delete({ url: '/system/error-code/delete?id=' + id }) +} +// 导出错误码 +export const excelErrorCode = (params) => { + return request.download({ url: '/system/error-code/export-excel', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/loginLog/index.ts b/yunxi-ui-admin-vue3/src/api/system/loginLog/index.ts new file mode 100644 index 00000000..f275c3e2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/loginLog/index.ts @@ -0,0 +1,24 @@ +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 const getLoginLogPage = (params: PageParam) => { + return request.get({ url: '/system/login-log/page', params }) +} + +// 导出登录日志 +export const exportLoginLog = (params) => { + return request.download({ url: '/system/login-log/export', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/mail/account/index.ts b/yunxi-ui-admin-vue3/src/api/system/mail/account/index.ts new file mode 100644 index 00000000..9e10c92a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/mail/account/index.ts @@ -0,0 +1,41 @@ +import request from '@/config/axios' + +export interface MailAccountVO { + id: number + mail: string + username: string + password: string + host: string + port: number + sslEnable: boolean +} + +// 查询邮箱账号列表 +export const getMailAccountPage = async (params: PageParam) => { + return await request.get({ url: '/system/mail-account/page', params }) +} + +// 查询邮箱账号详情 +export const getMailAccount = async (id: number) => { + return await request.get({ url: '/system/mail-account/get?id=' + id }) +} + +// 新增邮箱账号 +export const createMailAccount = async (data: MailAccountVO) => { + return await request.post({ url: '/system/mail-account/create', data }) +} + +// 修改邮箱账号 +export const updateMailAccount = async (data: MailAccountVO) => { + return await request.put({ url: '/system/mail-account/update', data }) +} + +// 删除邮箱账号 +export const deleteMailAccount = async (id: number) => { + return await request.delete({ url: '/system/mail-account/delete?id=' + id }) +} + +// 获得邮箱账号精简列表 +export const getSimpleMailAccountList = async () => { + return request.get({ url: '/system/mail-account/list-all-simple' }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/mail/log/index.ts b/yunxi-ui-admin-vue3/src/api/system/mail/log/index.ts new file mode 100644 index 00000000..13172a72 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/mail/log/index.ts @@ -0,0 +1,30 @@ +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 const getMailLogPage = async (params: PageParam) => { + return await request.get({ url: '/system/mail-log/page', params }) +} + +// 查询邮件日志详情 +export const getMailLog = async (id: number) => { + return await request.get({ url: '/system/mail-log/get?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/mail/template/index.ts b/yunxi-ui-admin-vue3/src/api/system/mail/template/index.ts new file mode 100644 index 00000000..fb7ce5ea --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/mail/template/index.ts @@ -0,0 +1,50 @@ +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 MailSendReqVO { + mail: string + templateCode: string + templateParams: Map +} + +// 查询邮件模版列表 +export const getMailTemplatePage = async (params: PageParam) => { + return await request.get({ url: '/system/mail-template/page', params }) +} + +// 查询邮件模版详情 +export const getMailTemplate = async (id: number) => { + return await request.get({ url: '/system/mail-template/get?id=' + id }) +} + +// 新增邮件模版 +export const createMailTemplate = async (data: MailTemplateVO) => { + return await request.post({ url: '/system/mail-template/create', data }) +} + +// 修改邮件模版 +export const updateMailTemplate = async (data: MailTemplateVO) => { + return await request.put({ url: '/system/mail-template/update', data }) +} + +// 删除邮件模版 +export const deleteMailTemplate = async (id: number) => { + return await request.delete({ url: '/system/mail-template/delete?id=' + id }) +} + +// 发送邮件 +export const sendMail = (data: MailSendReqVO) => { + return request.post({ url: '/system/mail-template/send-mail', data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/menu/index.ts b/yunxi-ui-admin-vue3/src/api/system/menu/index.ts new file mode 100644 index 00000000..4bb9a871 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/menu/index.ts @@ -0,0 +1,49 @@ +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 const getSimpleMenusList = () => { + return request.get({ url: '/system/menu/list-all-simple' }) +} + +// 查询菜单列表 +export const getMenuList = (params) => { + return request.get({ url: '/system/menu/list', params }) +} + +// 获取菜单详情 +export const getMenu = (id: number) => { + return request.get({ url: '/system/menu/get?id=' + id }) +} + +// 新增菜单 +export const createMenu = (data: MenuVO) => { + return request.post({ url: '/system/menu/create', data }) +} + +// 修改菜单 +export const updateMenu = (data: MenuVO) => { + return request.put({ url: '/system/menu/update', data }) +} + +// 删除菜单 +export const deleteMenu = (id: number) => { + return request.delete({ url: '/system/menu/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/notice/index.ts b/yunxi-ui-admin-vue3/src/api/system/notice/index.ts new file mode 100644 index 00000000..62bf5259 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/notice/index.ts @@ -0,0 +1,37 @@ +import request from '@/config/axios' + +export interface NoticeVO { + id: number | undefined + title: string + type: number + content: string + status: number + remark: string + creator: string + createTime: Date +} + +// 查询公告列表 +export const getNoticePage = (params: PageParam) => { + return request.get({ url: '/system/notice/page', params }) +} + +// 查询公告详情 +export const getNotice = (id: number) => { + return request.get({ url: '/system/notice/get?id=' + id }) +} + +// 新增公告 +export const createNotice = (data: NoticeVO) => { + return request.post({ url: '/system/notice/create', data }) +} + +// 修改公告 +export const updateNotice = (data: NoticeVO) => { + return request.put({ url: '/system/notice/update', data }) +} + +// 删除公告 +export const deleteNotice = (id: number) => { + return request.delete({ url: '/system/notice/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/notify/message/index.ts b/yunxi-ui-admin-vue3/src/api/system/notify/message/index.ts new file mode 100644 index 00000000..29036b95 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/notify/message/index.ts @@ -0,0 +1,48 @@ +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 const getNotifyMessagePage = async (params: PageParam) => { + return await request.get({ url: '/system/notify-message/page', params }) +} + +// 获得我的站内信分页 +export const getMyNotifyMessagePage = async (params: PageParam) => { + 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 getUnreadNotifyMessageList = async () => { + return await request.get({ url: '/system/notify-message/get-unread-list' }) +} + +// 获得当前用户的未读站内信数量 +export const getUnreadNotifyMessageCount = async () => { + return await request.get({ url: '/system/notify-message/get-unread-count' }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/notify/template/index.ts b/yunxi-ui-admin-vue3/src/api/system/notify/template/index.ts new file mode 100644 index 00000000..cd0e1223 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/notify/template/index.ts @@ -0,0 +1,49 @@ +import request from '@/config/axios' + +export interface NotifyTemplateVO { + id?: number + name: string + nickname: string + code: string + content: string + type: number + params: string + status: number + remark: string +} + +export interface NotifySendReqVO { + userId: number | null + templateCode: string + templateParams: Map +} + +// 查询站内信模板列表 +export const getNotifyTemplatePage = async (params: PageParam) => { + return await request.get({ url: '/system/notify-template/page', params }) +} + +// 查询站内信模板详情 +export const getNotifyTemplate = async (id: number) => { + return await request.get({ url: '/system/notify-template/get?id=' + id }) +} + +// 新增站内信模板 +export const createNotifyTemplate = async (data: NotifyTemplateVO) => { + return await request.post({ url: '/system/notify-template/create', data }) +} + +// 修改站内信模板 +export const updateNotifyTemplate = async (data: NotifyTemplateVO) => { + return await request.put({ url: '/system/notify-template/update', data }) +} + +// 删除站内信模板 +export const deleteNotifyTemplate = async (id: number) => { + return await request.delete({ url: '/system/notify-template/delete?id=' + id }) +} + +// 发送站内信 +export const sendNotify = (data: NotifySendReqVO) => { + return request.post({ url: '/system/notify-template/send-notify', data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/oauth2/client.ts b/yunxi-ui-admin-vue3/src/api/system/oauth2/client.ts new file mode 100644 index 00000000..6f71acad --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/oauth2/client.ts @@ -0,0 +1,47 @@ +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 +} + +// 查询 OAuth2 客户端的列表 +export const getOAuth2ClientPage = (params: PageParam) => { + return request.get({ url: '/system/oauth2-client/page', params }) +} + +// 查询 OAuth2 客户端的详情 +export const getOAuth2Client = (id: number) => { + return request.get({ url: '/system/oauth2-client/get?id=' + id }) +} + +// 新增 OAuth2 客户端 +export const createOAuth2Client = (data: OAuth2ClientVO) => { + return request.post({ url: '/system/oauth2-client/create', data }) +} + +// 修改 OAuth2 客户端 +export const updateOAuth2Client = (data: OAuth2ClientVO) => { + return request.put({ url: '/system/oauth2-client/update', data }) +} + +// 删除 OAuth2 +export const deleteOAuth2Client = (id: number) => { + return request.delete({ url: '/system/oauth2-client/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/oauth2/token.ts b/yunxi-ui-admin-vue3/src/api/system/oauth2/token.ts new file mode 100644 index 00000000..ac89ae89 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/oauth2/token.ts @@ -0,0 +1,22 @@ +import request from '@/config/axios' + +export interface OAuth2TokenVO { + id: number + accessToken: string + refreshToken: string + userId: number + userType: number + clientId: string + createTime: Date + expiresTime: Date +} + +// 查询 token列表 +export const getAccessTokenPage = (params: PageParam) => { + return request.get({ url: '/system/oauth2-token/page', params }) +} + +// 删除 token +export const deleteAccessToken = (accessToken: string) => { + return request.delete({ url: '/system/oauth2-token/delete?accessToken=' + accessToken }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/operatelog/index.ts b/yunxi-ui-admin-vue3/src/api/system/operatelog/index.ts new file mode 100644 index 00000000..848a5333 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/operatelog/index.ts @@ -0,0 +1,33 @@ +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 const getOperateLogPage = (params: PageParam) => { + return request.get({ url: '/system/operate-log/page', params }) +} +// 导出操作日志 +export const exportOperateLog = (params) => { + return request.download({ url: '/system/operate-log/export', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/permission/index.ts b/yunxi-ui-admin-vue3/src/api/system/permission/index.ts new file mode 100644 index 00000000..b3c7696b --- /dev/null +++ b/yunxi-ui-admin-vue3/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 getRoleMenuList = async (roleId: number) => { + return await request.get({ url: '/system/permission/list-role-menus?roleId=' + roleId }) +} + +// 赋予角色菜单权限 +export const assignRoleMenu = async (data: PermissionAssignRoleMenuReqVO) => { + return await request.post({ url: '/system/permission/assign-role-menu', data }) +} + +// 赋予角色数据权限 +export const assignRoleDataScope = async (data: PermissionAssignRoleDataScopeReqVO) => { + return await request.post({ url: '/system/permission/assign-role-data-scope', data }) +} + +// 查询用户拥有的角色数组 +export const getUserRoleList = async (userId: number) => { + return await request.get({ url: '/system/permission/list-user-roles?userId=' + userId }) +} + +// 赋予用户角色 +export const assignUserRole = async (data: PermissionAssignUserRoleReqVO) => { + return await request.post({ url: '/system/permission/assign-user-role', data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/post/index.ts b/yunxi-ui-admin-vue3/src/api/system/post/index.ts new file mode 100644 index 00000000..405db387 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/post/index.ts @@ -0,0 +1,46 @@ +import request from '@/config/axios' + +export interface PostVO { + id?: number + name: string + code: string + sort: number + status: number + remark: string + createTime?: Date +} + +// 查询岗位列表 +export const getPostPage = async (params: PageParam) => { + return await request.get({ url: '/system/post/page', params }) +} + +// 获取岗位精简信息列表 +export const getSimplePostList = async (): Promise => { + return await request.get({ url: '/system/post/list-all-simple' }) +} + +// 查询岗位详情 +export const getPost = async (id: number) => { + return await request.get({ url: '/system/post/get?id=' + id }) +} + +// 新增岗位 +export const createPost = async (data: PostVO) => { + return await request.post({ url: '/system/post/create', data }) +} + +// 修改岗位 +export const updatePost = async (data: PostVO) => { + return await request.put({ url: '/system/post/update', data }) +} + +// 删除岗位 +export const deletePost = async (id: number) => { + return await request.delete({ url: '/system/post/delete?id=' + id }) +} + +// 导出岗位 +export const exportPost = async (params) => { + return await request.download({ url: '/system/post/export', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/role/index.ts b/yunxi-ui-admin-vue3/src/api/system/role/index.ts new file mode 100644 index 00000000..93636ff0 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/role/index.ts @@ -0,0 +1,61 @@ +import request from '@/config/axios' + +export interface RoleVO { + id: number + name: string + code: string + sort: number + status: number + type: number + dataScope: number + dataScopeDeptIds: number[] + createTime: Date +} + +export interface UpdateStatusReqVO { + id: number + status: number +} + +// 查询角色列表 +export const getRolePage = async (params: PageParam) => { + return await request.get({ url: '/system/role/page', params }) +} + +// 查询角色(精简)列表 +export const getSimpleRoleList = async (): Promise => { + return await request.get({ url: '/system/role/list-all-simple' }) +} + +// 查询角色详情 +export const getRole = async (id: number) => { + return await request.get({ url: '/system/role/get?id=' + id }) +} + +// 新增角色 +export const createRole = async (data: RoleVO) => { + return await request.post({ url: '/system/role/create', data }) +} + +// 修改角色 +export const updateRole = async (data: RoleVO) => { + return await request.put({ url: '/system/role/update', data }) +} + +// 修改角色状态 +export const updateRoleStatus = async (data: UpdateStatusReqVO) => { + return await request.put({ url: '/system/role/update-status', data }) +} + +// 删除角色 +export const deleteRole = async (id: number) => { + return await request.delete({ url: '/system/role/delete?id=' + id }) +} + +// 导出角色 +export const exportRole = (params) => { + return request.download({ + url: '/system/role/export-excel', + params + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/sensitiveWord/index.ts b/yunxi-ui-admin-vue3/src/api/system/sensitiveWord/index.ts new file mode 100644 index 00000000..1116226f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/sensitiveWord/index.ts @@ -0,0 +1,58 @@ +import request from '@/config/axios' +import qs from 'qs' + +export interface SensitiveWordVO { + id: number + name: string + status: number + description: string + tags: string[] + createTime: Date +} + +export interface SensitiveWordTestReqVO { + text: string + tag: string[] +} + +// 查询敏感词列表 +export const getSensitiveWordPage = (params: PageParam) => { + return request.get({ url: '/system/sensitive-word/page', params }) +} + +// 查询敏感词详情 +export const getSensitiveWord = (id: number) => { + return request.get({ url: '/system/sensitive-word/get?id=' + id }) +} + +// 新增敏感词 +export const createSensitiveWord = (data: SensitiveWordVO) => { + return request.post({ url: '/system/sensitive-word/create', data }) +} + +// 修改敏感词 +export const updateSensitiveWord = (data: SensitiveWordVO) => { + return request.put({ url: '/system/sensitive-word/update', data }) +} + +// 删除敏感词 +export const deleteSensitiveWord = (id: number) => { + return request.delete({ url: '/system/sensitive-word/delete?id=' + id }) +} + +// 导出敏感词 +export const exportSensitiveWord = (params) => { + return request.download({ url: '/system/sensitive-word/export-excel', params }) +} + +// 获取所有敏感词的标签数组 +export const getSensitiveWordTagList = () => { + return request.get({ url: '/system/sensitive-word/get-tags' }) +} + +// 获得文本所包含的不合法的敏感词数组 +export const validateText = (query: SensitiveWordTestReqVO) => { + return request.get({ + url: '/system/sensitive-word/validate-text?' + qs.stringify(query, { arrayFormat: 'repeat' }) + }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/sms/smsChannel/index.ts b/yunxi-ui-admin-vue3/src/api/system/sms/smsChannel/index.ts new file mode 100644 index 00000000..f335628f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/sms/smsChannel/index.ts @@ -0,0 +1,43 @@ +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 const getSmsChannelPage = (params: PageParam) => { + return request.get({ url: '/system/sms-channel/page', params }) +} + +// 获得短信渠道精简列表 +export function getSimpleSmsChannelList() { + return request.get({ url: '/system/sms-channel/list-all-simple' }) +} + +// 查询短信渠道详情 +export const getSmsChannel = (id: number) => { + return request.get({ url: '/system/sms-channel/get?id=' + id }) +} + +// 新增短信渠道 +export const createSmsChannel = (data: SmsChannelVO) => { + return request.post({ url: '/system/sms-channel/create', data }) +} + +// 修改短信渠道 +export const updateSmsChannel = (data: SmsChannelVO) => { + return request.put({ url: '/system/sms-channel/update', data }) +} + +// 删除短信渠道 +export const deleteSmsChannel = (id: number) => { + return request.delete({ url: '/system/sms-channel/delete?id=' + id }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/sms/smsLog/index.ts b/yunxi-ui-admin-vue3/src/api/system/sms/smsLog/index.ts new file mode 100644 index 00000000..3d54fac1 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/sms/smsLog/index.ts @@ -0,0 +1,39 @@ +import request from '@/config/axios' + +export interface SmsLogVO { + id: number | null + channelId: number | null + channelCode: string + templateId: number | null + templateCode: string + templateType: number | null + templateContent: string + templateParams: Map | null + apiTemplateId: string + mobile: string + userId: number | null + userType: number | null + sendStatus: number | null + sendTime: Date | null + sendCode: number | null + sendMsg: string + apiSendCode: string + apiSendMsg: string + apiRequestId: string + apiSerialNo: string + receiveStatus: number | null + receiveTime: Date | null + apiReceiveCode: string + apiReceiveMsg: string + createTime: Date | null +} + +// 查询短信日志列表 +export const getSmsLogPage = (params: PageParam) => { + return request.get({ url: '/system/sms-log/page', params }) +} + +// 导出短信日志 +export const exportSmsLog = (params) => { + return request.download({ url: '/system/sms-log/export-excel', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/sms/smsTemplate/index.ts b/yunxi-ui-admin-vue3/src/api/system/sms/smsTemplate/index.ts new file mode 100644 index 00000000..35cb489d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/sms/smsTemplate/index.ts @@ -0,0 +1,60 @@ +import request from '@/config/axios' + +export interface SmsTemplateVO { + id: number | null + type: number | null + status: number + code: string + name: string + content: string + remark: string + apiTemplateId: string + channelId: number | null + channelCode?: string + params?: string[] + createTime?: Date +} + +export interface SendSmsReqVO { + mobile: string + templateCode: string + templateParams: Map +} + +// 查询短信模板列表 +export const getSmsTemplatePage = (params: PageParam) => { + return request.get({ url: '/system/sms-template/page', params }) +} + +// 查询短信模板详情 +export const getSmsTemplate = (id: number) => { + return request.get({ url: '/system/sms-template/get?id=' + id }) +} + +// 新增短信模板 +export const createSmsTemplate = (data: SmsTemplateVO) => { + return request.post({ url: '/system/sms-template/create', data }) +} + +// 修改短信模板 +export const updateSmsTemplate = (data: SmsTemplateVO) => { + return request.put({ url: '/system/sms-template/update', data }) +} + +// 删除短信模板 +export const deleteSmsTemplate = (id: number) => { + return request.delete({ url: '/system/sms-template/delete?id=' + id }) +} + +// 导出短信模板 +export const exportSmsTemplate = (params) => { + return request.download({ + url: '/system/sms-template/export-excel', + params + }) +} + +// 发送短信 +export const sendSms = (data: SendSmsReqVO) => { + return request.post({ url: '/system/sms-template/send-sms', data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/tenant/index.ts b/yunxi-ui-admin-vue3/src/api/system/tenant/index.ts new file mode 100644 index 00000000..176c3757 --- /dev/null +++ b/yunxi-ui-admin-vue3/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 getTenantPage = (params: TenantPageReqVO) => { + return request.get({ url: '/system/tenant/page', params }) +} + +// 查询租户详情 +export const getTenant = (id: number) => { + return request.get({ url: '/system/tenant/get?id=' + id }) +} + +// 新增租户 +export const createTenant = (data: TenantVO) => { + return request.post({ url: '/system/tenant/create', data }) +} + +// 修改租户 +export const updateTenant = (data: TenantVO) => { + return request.put({ url: '/system/tenant/update', data }) +} + +// 删除租户 +export const deleteTenant = (id: number) => { + return request.delete({ url: '/system/tenant/delete?id=' + id }) +} + +// 导出租户 +export const exportTenant = (params: TenantExportReqVO) => { + return request.download({ url: '/system/tenant/export-excel', params }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/tenantPackage/index.ts b/yunxi-ui-admin-vue3/src/api/system/tenantPackage/index.ts new file mode 100644 index 00000000..01d139e2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/tenantPackage/index.ts @@ -0,0 +1,42 @@ +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 const getTenantPackagePage = (params: PageParam) => { + return request.get({ url: '/system/tenant-package/page', params }) +} + +// 获得租户 +export const getTenantPackage = (id: number) => { + return request.get({ url: '/system/tenant-package/get?id=' + id }) +} + +// 新增租户套餐 +export const createTenantPackage = (data: TenantPackageVO) => { + return request.post({ url: '/system/tenant-package/create', data }) +} + +// 修改租户套餐 +export const updateTenantPackage = (data: TenantPackageVO) => { + return request.put({ url: '/system/tenant-package/update', data }) +} + +// 删除租户套餐 +export const deleteTenantPackage = (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/yunxi-ui-admin-vue3/src/api/system/user/index.ts b/yunxi-ui-admin-vue3/src/api/system/user/index.ts new file mode 100644 index 00000000..6224f0e8 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/api/system/user/index.ts @@ -0,0 +1,76 @@ +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 const getUserPage = (params: PageParam) => { + return request.get({ url: '/system/user/page', params }) +} + +// 查询用户详情 +export const getUser = (id: number) => { + return request.get({ url: '/system/user/get?id=' + id }) +} + +// 新增用户 +export const createUser = (data: UserVO) => { + return request.post({ url: '/system/user/create', data }) +} + +// 修改用户 +export const updateUser = (data: UserVO) => { + return request.put({ url: '/system/user/update', data }) +} + +// 删除用户 +export const deleteUser = (id: number) => { + return request.delete({ url: '/system/user/delete?id=' + id }) +} + +// 导出用户 +export const exportUser = (params) => { + return request.download({ url: '/system/user/export', params }) +} + +// 下载用户导入模板 +export const importUserTemplate = () => { + return request.download({ url: '/system/user/get-import-template' }) +} + +// 用户密码重置 +export const resetUserPwd = (id: number, password: string) => { + const data = { + id, + password + } + return request.put({ url: '/system/user/update-password', data: data }) +} + +// 用户状态修改 +export const updateUserStatus = (id: number, status: number) => { + const data = { + id, + status + } + return request.put({ url: '/system/user/update-status', data: data }) +} + +// 获取用户精简信息列表 +export const getSimpleUserList = (): Promise => { + return request.get({ url: '/system/user/list-all-simple' }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/user/profile.ts b/yunxi-ui-admin-vue3/src/api/system/user/profile.ts new file mode 100644 index 00000000..b2623c8b --- /dev/null +++ b/yunxi-ui-admin-vue3/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 getUserProfile = () => { + return request.get({ url: '/system/user/profile/get' }) +} + +// 修改用户个人信息 +export const updateUserProfile = (data: UserProfileUpdateReqVO) => { + return request.put({ url: '/system/user/profile/update', data }) +} + +// 用户密码重置 +export const updateUserPassword = (oldPassword: string, newPassword: string) => { + return request.put({ + url: '/system/user/profile/update-password', + data: { + oldPassword: oldPassword, + newPassword: newPassword + } + }) +} + +// 用户头像上传 +export const uploadAvatar = (data) => { + return request.upload({ url: '/system/user/profile/update-avatar', data: data }) +} diff --git a/yunxi-ui-admin-vue3/src/api/system/user/socialUser.ts b/yunxi-ui-admin-vue3/src/api/system/user/socialUser.ts new file mode 100644 index 00000000..79f4d402 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/assets/imgs/avatar.gif b/yunxi-ui-admin-vue3/src/assets/imgs/avatar.gif new file mode 100644 index 0000000000000000000000000000000000000000..fdbd32c675f85af4ed57021ac0638a21a3c6cad3 GIT binary patch literal 6334 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 literal 0 HcmV?d00001 diff --git a/yunxi-ui-admin-vue3/src/assets/imgs/avatar.jpg b/yunxi-ui-admin-vue3/src/assets/imgs/avatar.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d46a70a46430e31744420865138cc7eedb8b77e1 GIT binary patch literal 6264 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 BLq7lj literal 0 HcmV?d00001 diff --git a/yunxi-ui-admin-vue3/src/assets/imgs/logo.png b/yunxi-ui-admin-vue3/src/assets/imgs/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7e1043f21e57ead95f41354f0f1b7d1166c07ebd GIT binary patch literal 2801 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 NvOP|&09Jii_#cBOpN9Yd literal 0 HcmV?d00001 diff --git a/yunxi-ui-admin-vue3/src/assets/imgs/wechat.png b/yunxi-ui-admin-vue3/src/assets/imgs/wechat.png new file mode 100644 index 0000000000000000000000000000000000000000..6afc5e41cfc29d258e0c6135981a7a09b7bcf126 GIT binary patch literal 1881 zcmV-f2d4OmP)Px+6G=otRA@uxT5F6HRTlovt)4-Yt`>DxqM{h1piEaoG!it+;>Ku9`iG12%)6qSb>G)jC#QP~(>XJx8!Ncf>R3nRXU>6!tR>8fLH z&-BPlcUM(+R|8q^kNH#I`M&erd#dK%bDQux7!;JpamncoOxXzv({Uli+0)aoy&Fxi zgW|7-ry`T%jKJPYsh*xLg z=_mZbD@VDSjI=d2u_EpTV{+vZ?vV_<2cWG_H(#rjMoE)uN^1inQ7hpBUdB=@VJ2&# zSJb3xM4tobrdkMkaJE{yxiV`Dvjn&heT6&y}!$;8dSkH)Ae?JF=Qm5e2{TXFM(B5|Eyoa>Qb zi=~@uyi9mKA2ex1-va0|S1@Cjl`zjd6m$He(YfOA2lTk($P%F!8(B{iu%3c|8#NUDitV7DNwa~y0EA>Nbu?!+#%Zpl`| zS!mMGWT{>nB&i=RX1p&z>&T{cvi!#-r=KUtUPy$~m@yH+X#h_IhzaOoa2J@m2-Ja) zvp%yVz0Z%1#K>~p3*cnG0Ri9!K=e0Rf?*>%ML^7F@KpdVER*{sfL|vCo4pKE#L#v# zaB7(X6~X03!V%dU_cx$N-CK7Lh*wlZbZ8J^7c=hf)6J(!aZw}{eTA9iOxAj=v~;sI zoQhpZoHI8gV8L}x2>16dHn$hNlaZu$7ZV-`Sa1{_zisL1%hhCb1&HpenHHmm34a}Z zHl?tk%_U${&4huEE#16GO=<}c+G?mXNX_VT$eG)(Z{L#FlJDf13 zwT=^8cR1@^18ig2*fT57iXNu0r3`tR}is!F3D zS-LqZVrT~#7+WP*wc$I8wj?7)>{&FwOswejRRN_jn0?GM7iE-*~8LJ*>%_n!r5g92N}TF>amg< z7w8c^%fQRTd_F)^0ge^BuM9CJW(o4j;mRO$E|-g#7nL+QE)|_6m_8UH&4TE8F+eMO zmv7ruN+{Q&tVcr#*}XmAJKu&+2whHunqA3x8Xmpl866NYqE9nuF2FB{tg_0@oST?x zXs;is)g8GVq5Zubcf0@iKm=5XK2^EU%QC`yjQdGD#%*)#Sn>+H(se%MS78C?tDhM*>{ z2+z+#h?%`D+hm7-cpO%fIisoQ3NXoT-I##Qerk*Cy!|bmvX4As9Cnj~MztAWNTY%M z50#sbQj>N>Oy-kOliFX1_$mg?uXksla(-wPS#Ojve@iP`2#b^O0DIsA?r(JL%Hz4@s4x|Xv_L12Fc30Q=P TM8y=K00000NkvXXu0mjf4<3N5 literal 0 HcmV?d00001 diff --git a/yunxi-ui-admin-vue3/src/assets/map/json/china.json b/yunxi-ui-admin-vue3/src/assets/map/json/china.json new file mode 100644 index 00000000..0c92e71c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/map/json/china.json @@ -0,0 +1 @@ +{"type":"FeatureCollection","features":[{"type":"Feature","id":"710000","properties":{"id":"710000","cp":[121.509062,24.044332],"name":"台湾","childNum":6},"geometry":{"type":"MultiPolygon","coordinates":[["@@°Ü¯Û"],["@@ƛĴÕƊÉɼģºðʀ\\ƎsÆNŌÔĚäœnÜƤɊĂǀĆĴžĤNJŨxĚĮǂƺòƌ‚–âÔ®ĮXŦţƸZûЋƕƑGđ¨ĭMó·ęcëƝɉlÝƯֹÅŃ^Ó·śŃNjƏďíåɛGɉ™¿@ăƑŽ¥ĘWǬÏĶŁâ"],["@@\\p|WoYG¿¥I†j@¢"],["@@…¡‰@ˆV^RqˆBbAŒnTXeRz¤Lž«³I"],["@@ÆEE—„kWqë @œ"],["@@fced"],["@@„¯ɜÄèaì¯ØǓIġĽ"],["@@çûĖ롖hòř "]],"encodeOffsets":[[[122886,24033]],[[123335,22980]],[[122375,24193]],[[122518,24117]],[[124427,22618]],[[124862,26043]],[[126259,26318]],[[127671,26683]]]}},{"type":"Feature","id":"130000","properties":{"id":"130000","cp":[114.502461,38.045474],"name":"河北","childNum":3},"geometry":{"type":"MultiPolygon","coordinates":[["@@o~†Z]‚ªr‰ºc_ħ²G¼s`jΟnüsœłNX_“M`ǽÓnUK…Ĝēs¤­©yrý§uģŒc†JŠ›e"],["@@U`Ts¿m‚"],["@@oºƋÄd–eVŽDJj£€J|Ådz•Ft~žKŨ¸IÆv|”‡¢r}膎onb˜}`RÎÄn°ÒdÞ²„^®’lnÐèĄlðӜ×]ªÆ}LiĂ±Ö`^°Ç¶p®đDcœŋ`–ZÔ’¶êqvFƚ†N®ĆTH®¦O’¾ŠIbÐã´BĐɢŴÆíȦp–ĐÞXR€·nndOž¤’OÀĈƒ­Qg˜µFo|gȒęSWb©osx|hYh•gŃfmÖĩnº€T̒Sp›¢dYĤ¶UĈjl’ǐpäìë|³kÛfw²Xjz~ÂqbTŠÑ„ěŨ@|oM‡’zv¢ZrÃVw¬ŧĖ¸fŒ°ÐT€ªqŽs{Sž¯r æÝlNd®²Ğ džiGʂJ™¼lr}~K¨ŸƐÌWö€™ÆŠzRš¤lêmĞL΄’@¡|q]SvK€ÑcwpÏρ†ĿćènĪWlĄkT}ˆJ”¤~ƒÈT„d„™pddʾĬŠ”ŽBVt„EÀ¢ôPĎƗè@~‚k–ü\\rÊĔÖæW_§¼F˜†´©òDòj’ˆYÈrbĞāøŀG{ƀ|¦ðrb|ÀH`pʞkv‚GpuARhÞÆǶgʊTǼƹS£¨¡ù³ŘÍ]¿Ây™ôEP xX¶¹܇O¡“gÚ¡IwÃ鑦ÅB‡Ï|Ç°…N«úmH¯‹âŸDùŽyŜžŲIÄuШDž•¸dɂ‡‚FŸƒ•›Oh‡đ©OŸ›iÃ`ww^ƒÌkŸ‘ÑH«ƇǤŗĺtFu…{Z}Ö@U‡´…ʚLg®¯Oı°ÃwŸ ^˜—€VbÉs‡ˆmA…ê]]w„§›RRl£‡ȭµu¯b{ÍDěïÿȧŽuT£ġƒěŗƃĝ“Q¨fV†Ƌ•ƅn­a@‘³@šď„yýIĹÊKšŭfċŰóŒxV@tˆƯŒJ”]eƒR¾fe|rHA˜|h~Ėƍl§ÏŠlTíb ØoˆÅbbx³^zÃĶš¶Sj®A”yÂhðk`š«P€”ˈµEF†Û¬Y¨Ļrõqi¼‰Wi°§’б´°^[ˆÀ|ĠO@ÆxO\\tŽa\\tĕtû{ġŒȧXýĪÓjùÎRb›š^ΛfK[ݏděYfíÙTyŽuUSyŌŏů@Oi½’éŅ­aVcř§ax¹XŻác‡žWU£ôãºQ¨÷Ñws¥qEH‰Ù|‰›šYQoŕÇyáĂ£MðoťÊ‰P¡mšWO¡€v†{ôvîēÜISpÌhp¨ ‘j†deŔQÖj˜X³à™Ĉ[n`Yp@Už–cM`’RKhŒEbœ”pŞlNut®Etq‚nsÁŠgA‹iú‹oH‡qCX‡”hfgu“~ϋWP½¢G^}¯ÅīGCŸÑ^ãziMáļMTÃƘrMc|O_ž¯Ŏ´|‡morDkO\\mĆJfl@cĢ¬¢aĦtRıҙ¾ùƀ^juųœK­ƒUFy™—Ɲ…›īÛ÷ąV×qƥV¿aȉd³B›qPBm›aËđŻģm“Å®VŠ¹d^K‡KoŸnYg“¯Xhqa”Ldu¥•ÍpDž¡KąÅƒkĝęěhq‡}HyÓ]¹ǧ£…Í÷¿qᵧš™g‘¤o^á¾ZE‡¤i`ij{n•ƒOl»ŸWÝĔįhg›F[¿¡—ßkOüš_‰€ū‹i„DZàUtėGylƒ}ŒÓM}€jpEC~¡FtoQi‘šHkk{Ãmï‚"]],"encodeOffsets":[[[119712,40641]],[[121616,39981]],[[116462,37237]]]}},{"type":"Feature","id":"140000","properties":{"id":"140000","cp":[111.849248,36.857014],"name":"山西","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@Þĩ҃S‰ra}Á€yWix±Üe´lè“ßÓǏok‘ćiµVZģ¡coœ‘TS˹ĪmnÕńe–hZg{gtwªpXaĚThȑp{¶Eh—®RćƑP¿£‘Pmc¸mQÝW•ďȥoÅîɡųAďä³aωJ‘½¥PG­ąSM­™…EÅruµé€‘Yӎ•Ō_d›ĒCo­Èµ]¯_²ÕjāŽK~©ÅØ^ԛkïçămϑk]­±ƒcݯÑÃmQÍ~_a—pm…~ç¡q“ˆu{JÅŧ·Ls}–EyÁÆcI{¤IiCfUc•ƌÃp§]웫vD@¡SÀ‘µM‚ÅwuŽYY‡¡DbÑc¡hƒ×]nkoQdaMç~eD•ÛtT‰©±@¥ù@É¡‰ZcW|WqOJmĩl«ħşvOÓ«IqăV—¥ŸD[mI~Ó¢cehiÍ]Ɠ~ĥqXŠ·eƷœn±“}v•[ěďŽŕ]_‘œ•`‰¹ƒ§ÕōI™o©b­s^}Ét±ū«³p£ÿ·Wµ|¡¥ăFÏs׌¥ŅxŸÊdÒ{ºvĴÎêÌɊ²¶€ü¨|ÞƸµȲ‘LLúÉƎ¤ϊęĔV`„_bª‹S^|ŸdŠzY|dz¥p†ZbÆ£¶ÒK}tĦÔņƠ‚PYzn€ÍvX¶Ěn ĠÔ„zý¦ª˜÷žÑĸَUȌ¸‚dòÜJð´’ìúNM¬ŒXZ´‘¤ŊǸ_tldIš{¦ƀðĠȤ¥NehXnYG‚‡R° ƬDj¬¸|CĞ„Kq‚ºfƐiĺ©ª~ĆOQª ¤@ìǦɌ²æBŒÊ”TœŸ˜ʂōĖ’šĴŞ–ȀœÆÿȄlŤĒö„t”νî¼ĨXhŒ‘˜|ªM¤Ðz"],"encodeOffsets":[[116874,41716]]}},{"type":"Feature","id":"150000","properties":{"id":"150000","cp":[111.670801,41.818311],"name":"内蒙古","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@¯PqƒFB…‰|S•³C|kñ•H‹d‘iÄ¥sˆʼnő…PóÑÑE^‘ÅPpy_YtS™hQ·aHwsOnʼnÚs©iqj›‰€USiº]ïWš‰«gW¡A–Rë¥_ŽsgÁnUI«m‰…„‹]j‡vV¼euhwqA„aW˜ƒ_µj…»çjioQR¹ēÃßt@r³[ÛlćË^ÍÉáG“›OUۗOB±•XŸkŇ¹£k|e]ol™ŸkVͼÕqtaÏõjgÁ£§U^Œ”RLˆËnX°Ç’Bz†^~wfvˆypV ¯„ƫĉ˭ȫƗŷɿÿĿƑ˃ĝÿÃǃßËőó©ǐȍŒĖM×ÍEyx‹þp]Évïè‘vƀnÂĴÖ@‚‰†V~Ĉv¦wĖt—ējyÄDXÄxGQuv_›i¦aBçw‘˛wD™©{ŸtāmQ€{EJ§KPśƘƿ¥@‰sCT•É}ɃwˆƇy±ŸgÑ“}T[÷kÐ禫…SÒ¥¸ëBX½‰HáŵÀğtSÝÂa[ƣ°¯¦P]£ġ“–“Òk®G²„èQ°óMq}EŠóƐÇ\\ƒ‡@áügQ͋u¥Fƒ“T՛¿Jû‡]|mvāÎYua^WoÀa·­ząÒot׶CLƗi¯¤mƎHNJ¤îìɾŊìTdåwsRÖgĒųúÍġäÕ}Q¶—ˆ¿A•†‹[¡Œ{d×uQAƒ›M•xV‹vMOmăl«ct[wº_šÇʊŽŸjb£ĦS_é“QZ“_lwgOiýe`YYLq§IÁˆdz£ÙË[ÕªuƏ³ÍT—s·bÁĽäė[›b[ˆŗfãcn¥îC¿÷µ[ŏÀQ­ōšĉm¿Á^£mJVm‡—L[{Ï_£›F¥Ö{ŹA}…×Wu©ÅaųijƳhB{·TQqÙIķˑZđ©Yc|M¡…L•eVUóK_QWk’_ĥ‘¿ãZ•»X\\ĴuUƒè‡lG®ěłTĠğDєOrÍd‚ÆÍz]‹±…ŭ©ŸÅ’]ŒÅÐ}UË¥©Tċ™ïxgckfWgi\\ÏĒ¥HkµE˜ë{»ÏetcG±ahUiñiWsɁˆ·c–C‚Õk]wȑ|ća}w…VaĚ᠞ŒG°ùnM¬¯†{ȈÐÆA’¥ÄêJxÙ¢”hP¢Ûˆº€µwWOŸóFŽšÁz^ÀŗÎú´§¢T¤ǻƺSė‰ǵhÝÅQgvBHouʝl_o¿Ga{ïq{¥|ſĿHĂ÷aĝÇq‡Z‘ñiñC³ª—…»E`¨åXēÕqÉû[l•}ç@čƘóO¿¡ƒFUsA‰“ʽīccšocƒ‚ƒÇS}„“£‡IS~ălkĩXçmĈ…ŀЂoÐdxÒuL^T{r@¢‘žÍƒĝKén£kQ™‰yšÅõËXŷƏL§~}kqš»IHėDžjĝŸ»ÑÞoŸå°qTt|r©ÏS‹¯·eŨĕx«È[eMˆ¿yuˆ‘pN~¹ÏyN£{©’—g‹ħWí»Í¾s“əšDž_ÃĀɗ±ą™ijĉʍŌŷ—S›É“A‹±åǥɋ@럣R©ąP©}ĹªƏj¹erƒLDĝ·{i«ƫC£µsKCš…GS|úþX”gp›{ÁX¿Ÿć{ƱȏñZáĔyoÁhA™}ŅĆfdʼn„_¹„Y°ėǩÑ¡H¯¶oMQqð¡Ë™|‘Ñ`ƭŁX½·óۓxğįÅcQ‡ˆ“ƒs«tȋDžF“Ÿù^i‘t«Č¯[›hAi©á¥ÇĚ×l|¹y¯YȵƓ‹ñǙµï‚ċ™Ļ|Dœ™üȭ¶¡˜›oŽäÕG\\ďT¿Òõr¯œŸLguÏYęRƩšɷŌO\\İТæ^Ŋ IJȶȆbÜGŽĝ¬¿ĚVĎgª^íu½jÿĕęjık@Ľƒ]ėl¥Ë‡ĭûÁ„ƒėéV©±ćn©­ȇžÍq¯½•YÃÔʼn“ÉNѝÅÝy¹NqáʅDǡËñ­ƁYÅy̱os§ȋµʽǘǏƬɱà‘ưN¢ƔÊuľýľώȪƺɂļžxœZĈ}ÌʼnŪ˜ĺœŽĭFЛĽ̅ȣͽÒŵìƩÇϋÿȮǡŏçƑůĕ~Ǎ›¼ȳÐUf†dIxÿ\\G ˆzâɏÙOº·pqy£†@ŒŠqþ@Ǟ˽IBäƣzsÂZ†ÁàĻdñ°ŕzéØűzșCìDȐĴĺf®ŽÀľưø@ɜÖÞKĊŇƄ§‚͑těï͡VAġÑÑ»d³öǍÝXĉĕÖ{þĉu¸ËʅğU̎éhɹƆ̗̮ȘNJ֥ड़ࡰţાíϲäʮW¬®ҌeרūȠkɬɻ̼ãüfƠSצɩςåȈHϚÎKdzͲOðÏȆƘ¼CϚǚ࢚˼ФԂ¤ƌžĞ̪Qʤ´¼mȠJˀŸƲÀɠmǐnǔĎȆÞǠN~€ʢĜ‚¶ƌĆĘźʆȬ˪ĚĒ¸ĞGȖƴƀj`ĢçĶāàŃºēĢƒĖćšYŒÀŎüôQÐÂŎŞdžŞêƖš˜oˆDĤÕºÑǘÛˤ³̀gńƘĔÀ^žªƂ`ªt¾äƚêĦĀ¼Ð€Ĕǎ¨Ȕ»͠^ˮÊȦƤøxRrŜH¤¸ÂxDĝŒ|ø˂˜ƮÐ¬ɚwɲFjĔ²Äw°dždÀɞ_ĸdîàŎjʜêTЪŌ‡ŜWÈ|tqĢUB~´°ÎFC•ŽU¼pĀēƄN¦¾O¶ŠłKĊOj“Ě”j´ĜYp˜{¦„ˆSĚÍ\\Tš×ªV–÷Ší¨ÅDK°ßtŇĔKš¨ǵÂcḷ̌ĚǣȄĽF‡lġUĵœŇ‹ȣFʉɁƒMğįʏƶɷØŭOǽ«ƽū¹Ʊő̝Ȩ§ȞʘĖiɜɶʦ}¨֪ࠜ̀ƇǬ¹ǨE˦ĥªÔêFŽxúQ„Er´W„rh¤Ɛ \\talĈDJ˜Ü|[Pll̚¸ƎGú´Pž¬W¦†^¦–H]prR“n|or¾wLVnÇIujkmon£cX^Bh`¥V”„¦U¤¸}€xRj–[^xN[~ªŠxQ„‚[`ªHÆÂExx^wšN¶Ê˜|¨ì†˜€MrœdYp‚oRzNy˜ÀDs~€bcfÌ`L–¾n‹|¾T‚°c¨È¢a‚r¤–`[|òDŞĔöxElÖdH„ÀI`„Ď\\Àì~ƎR¼tf•¦^¢ķ¶e”ÐÚMŒptgj–„ɡČÅyġLû™ŇV®ŠÄÈƀ†Ď°P|ªVV†ªj–¬ĚÒêp¬–E|ŬÂc|ÀtƐK fˆ{ĘFĒœƌXƲąo½Ę‘\\¥–o}›Ûu£ç­kX‘{uĩ«āíÓUŅßŢq€Ť¥lyň[€oi{¦‹L‡ń‡ðFȪȖ”ĒL„¿Ì‹ˆfŒ£K£ʺ™oqNŸƒwğc`ue—tOj×°KJ±qƒÆġm‰Ěŗos¬…qehqsuœƒH{¸kH¡Š…ÊRǪÇƌbȆ¢´ä܍¢NìÉʖ¦â©Ġu¦öČ^â£Ăh–šĖMÈÄw‚\\fŦ°W ¢¾luŸD„wŠ\\̀ʉÌÛM…Ā[bӞEn}¶Vc…ê“sƒ"]],"encodeOffsets":[[[129102,52189]]]}},{"type":"Feature","id":"210000","properties":{"id":"210000","cp":[123.429096,41.796767],"name":"辽宁","childNum":16},"geometry":{"type":"MultiPolygon","coordinates":[["@@L–Ž@@s™a"],["@@MnNm"],["@@d‚c"],["@@eÀ‚C@b‚“‰"],["@@f‡…Xwkbr–Ä`qg"],["@@^jtW‘Q"],["@@~ Y]c"],["@@G`ĔN^_¿Z‚ÃM"],["@@iX¶B‹Y"],["@@„YƒZ"],["@@L_{Epf"],["@@^WqCT\\"],["@@\\[“‹§t|”¤_"],["@@m`n_"],["@@Ïxnj{q_×^Giip"],["@@@œé^B†‡ntˆaÊU—˜Ÿ]x ¯ÄPIJ­°h€ʙK³†VˆÕ@Y~†|EvĹsDŽ¦­L^p²ŸÒG ’Ël]„xxÄ_˜fT¤Ď¤cŽœP„–C¨¸TVjbgH²sdÎdHt`Bˆ—²¬GJję¶[ÐhjeXdlwhšðSȦªVÊπ‹Æ‘Z˜ÆŶ®²†^ŒÎyÅÎcPqń“ĚDMħĜŁH­ˆk„çvV[ij¼W–‚YÀäĦ’‘`XlžR`žôLUVžfK–¢†{NZdĒª’YĸÌÚJRr¸SA|ƴgŴĴÆbvªØX~†źBŽ|¦ÕœEž¤Ð`\\|Kˆ˜UnnI]¤ÀÂĊnŎ™R®Ő¿¶\\ÀøíDm¦ÎbŨab‰œaĘ\\ľã‚¸a˜tÎSƐ´©v\\ÖÚÌǴ¤Â‡¨JKr€Z_Z€fjþhPkx€`Y”’RIŒjJcVf~sCN¤ ˆE‚œhæm‰–sHy¨SðÑÌ\\\\ŸĐRZk°IS§fqŒßýáЍÙÉÖ[^¯ǤŲ„ê´\\¦¬ĆPM¯£Ÿˆ»uïpùzEx€žanµyoluqe¦W^£ÊL}ñrkqWňûP™‰UP¡ôJŠoo·ŒU}£Œ„[·¨@XŒĸŸ“‹‹DXm­Ûݏº‡›GU‹CÁª½{íĂ^cj‡k“¶Ã[q¤“LÉö³cux«zZfƒ²BWÇ®Yß½ve±ÃC•ý£W{Ú^’q^sÑ·¨‹ÍOt“¹·C¥‡GD›rí@wÕKţ݋˜Ÿ«V·i}xËÍ÷‘i©ĝ‡ɝǡ]ƒˆ{c™±OW‹³Ya±Ÿ‰_穂Hžĕoƫ€Ňqƒr³‰Lys[„ñ³¯OS–ďOMisZ†±ÅFC¥Pq{‚Ã[Pg}\\—¿ghćO…•k^ģÁFıĉĥM­oEqqZûěʼn³F‘¦oĵ—hŸÕP{¯~TÍlª‰N‰ßY“Ð{Ps{ÃVU™™eĎwk±ʼnVÓ½ŽJãÇÇ»Jm°dhcÀff‘dF~ˆ€ĀeĖ€d`sx² šƒ®EżĀdQ‹Âd^~ăÔHˆ¦\\›LKpĄVez¤NP ǹӗR™ÆąJSh­a[¦´Âghwm€BÐ¨źhI|žVVŽ—Ž|p] Â¼èNä¶ÜBÖ¼“L`‚¼bØæŒKV”ŸpoœúNZÞÒKxpw|ÊEMnzEQšŽIZ”ŽZ‡NBˆčÚFÜçmĩ‚WĪñt‘ÞĵÇñZ«uD‚±|Əlij¥ãn·±PmÍa‰–da‡ CL‡Ǒkùó¡³Ï«QaċϑOÃ¥ÕđQȥċƭy‹³ÃA"]],"encodeOffsets":[[[123686,41445]],[[126019,40435]],[[124393,40128]],[[126117,39963]],[[125322,40140]],[[126686,40700]],[[126041,40374]],[[125584,40168]],[[125453,40165]],[[125362,40214]],[[125280,40291]],[[125774,39997]],[[125976,40496]],[[125822,39993]],[[125509,40217]],[[122731,40949]]]}},{"type":"Feature","id":"220000","properties":{"id":"220000","cp":[125.3245,43.886841],"name":"吉林","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@‘p䔳PClƒFbbÍzš€wBG’ĭ€Z„Åi“»ƒlY­ċ²SgŽkÇ£—^S‰“qd¯•‹R…©éŽ£¯S†\\cZ¹iűƏCuƍÓX‡oR}“M^o•£…R}oªU­F…uuXHlEŕ‡€Ï©¤ÛmTŽþ¤D–²ÄufàÀ­XXȱAe„yYw¬dvõ´KÊ£”\\rµÄl”iˆdā]|DÂVŒœH¹ˆÞ®ÜWnŒC”Œķ W‹§@\\¸‹ƒ~¤‹Vp¸‰póIO¢ŠVOšŇürXql~òÉK]¤¥Xrfkvzpm¶bwyFoúvð‡¼¤ N°ąO¥«³[ƒéǡű_°Õ\\ÚÊĝŽþâőàerR¨­JYlďQ[ ÏYëЧTGz•tnŠß¡gFkMŸāGÁ¤ia É‰™È¹`\\xs€¬dĆkNnuNUŠ–užP@‚vRY¾•–\\¢…ŒGªóĄ~RãÖÎĢù‚đŴÕhQŽxtcæëSɽʼníëlj£ƍG£nj°KƘµDsØÑpyƸ®¿bXp‚]vbÍZuĂ{nˆ^IüœÀSք”¦EŒvRÎûh@℈[‚Əȉô~FNr¯ôçR±ƒ­HÑl•’Ģ–^¤¢‚OðŸŒævxsŒ]ÞÁTĠs¶¿âƊGW¾ìA¦·TѬ†è¥€ÏÐJ¨¼ÒÖ¼ƒƦɄxÊ~S–tD@ŠĂ¼Ŵ¡jlºWžvЉˆzƦZЎ²CH— „Axiukd‹ŒGgetqmcžÛ£Ozy¥cE}|…¾cZ…k‚‰¿uŐã[oxGikfeäT@…šSUwpiÚFM©’£è^ڟ‚`@v¶eň†f h˜eP¶žt“äOlÔUgƒÞzŸU`lœ}ÔÆUvØ_Ō¬Öi^ĉi§²ÃŠB~¡Ĉ™ÚEgc|DC_Ȧm²rBx¼MÔ¦ŮdĨÃâYx‘ƘDVÇĺĿg¿cwÅ\\¹˜¥Yĭlœ¤žOv†šLjM_a W`zļMž·\\swqÝSA‡š—q‰Śij¯Š‘°kŠRē°wx^Đkǂғ„œž“œŽ„‹\\]˜nrĂ}²ĊŲÒøãh·M{yMzysěnĒġV·°“G³¼XÀ““™¤¹i´o¤ŃšŸÈ`̃DzÄUĞd\\i֚ŒˆmÈBĤÜɲDEh LG¾ƀľ{WaŒYÍȏĢĘÔRîĐj‹}Ǟ“ccj‡oUb½š{“h§Ǿ{K‹ƖµÎ÷žGĀÖŠåưÎs­l›•yiē«‹`姝H¥Ae^§„GK}iã\\c]v©ģZ“mÃ|“[M}ģTɟĵ‘Â`À–çm‰‘FK¥ÚíÁbXš³ÌQґHof{‰]e€pt·GŋĜYünĎųVY^’˜ydõkÅZW„«WUa~U·Sb•wGçǑ‚“iW^q‹F‚“›uNĝ—·Ew„‹UtW·Ýďæ©PuqEzwAV•—XR‰ãQ`­©GŒM‡ehc›c”ďϝd‡©ÑW_ϗYƅŒ»…é\\ƒɹ~ǙG³mØ©BšuT§Ĥ½¢Ã_ý‘L¡‘ýŸqT^rme™\\Pp•ZZbƒyŸ’uybQ—efµ]UhĿDCmûvašÙNSkCwn‰cćfv~…Y‹„ÇG"],"encodeOffsets":[[130196,42528]]}},{"type":"Feature","id":"230000","properties":{"id":"230000","cp":[128.642464,46.756967],"name":"黑龙江","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@UƒµNÿ¥īè灋•HÍøƕ¶LŒǽ|g¨|”™Ža¾pViˆdd”~ÈiŒíďÓQġėǐZ΋ŽXb½|ſÃH½ŸKFgɱCģÛÇA‡n™‹jÕc[VĝDZÃ˄Ç_™ £ń³pŽj£º”š¿”»WH´¯”U¸đĢmžtĜyzzNN|g¸÷äűѱĉā~mq^—Œ[ƒ”››”ƒǁÑďlw]¯xQĔ‰¯l‰’€°řĴrŠ™˜BˆÞTxr[tŽ¸ĻN_yŸX`biN™Ku…P›£k‚ZĮ—¦[ºxÆÀdhŽĹŀUÈƗCw’áZħÄŭcÓ¥»NAw±qȥnD`{ChdÙFćš}¢‰A±Äj¨]ĊÕjŋ«×`VuÓś~_kŷVÝyh„“VkÄãPs”Oµ—fŸge‚Ň…µf@u_Ù ÙcŸªNªÙEojVx™T@†ãSefjlwH\\pŏäÀvŠŽlY†½d{†F~¦dyz¤PÜndsrhf‹HcŒvlwjFœ£G˜±DύƥY‡yϊu¹XikĿ¦ÏqƗǀOŜ¨LI|FRĂn sª|Cš˜zxAè¥bœfudTrFWÁ¹Am|˜ĔĕsķÆF‡´Nš‰}ć…UŠÕ@Áijſmužç’uð^ÊýowŒFzØÎĕNőžǏȎôªÌŒDŽàĀÄ˄ĞŀƒʀĀƘŸˮȬƬĊ°ƒUŸzou‡xe]}Ž…AyȑW¯ÌmK‡“Q]‹Īºif¸ÄX|sZt|½ÚUΠlkš^p{f¤lˆºlÆW –€A²˜PVܜPH”Êâ]ÎĈÌÜk´\\@qàsĔÄQºpRij¼èi†`¶—„bXƒrBgxfv»ŽuUiˆŒ^v~”J¬mVp´£Œ´VWrnP½ì¢BX‚¬h™ŠðX¹^TjVœŠriªj™tŊÄm€tPGx¸bgRšŽsT`ZozÆO]’ÒFô҆Oƒ‡ŊŒvŞ”p’cGŒêŠsx´DR–Œ{A†„EOr°Œ•žx|íœbˆ³Wm~DVjºéNN†Ëܲɶ­GƒxŷCStŸ}]ûō•SmtuÇÃĕN•™āg»šíT«u}ç½BĵÞʣ¥ëÊ¡Mێ³ãȅ¡ƋaǩÈÉQ‰†G¢·lG|›„tvgrrf«†ptęŘnŠÅĢr„I²¯LiØsPf˜_vĠd„xM prʹšL¤‹¤‡eˌƒÀđK“žïÙVY§]I‡óáĥ]ķ†Kˆ¥Œj|pŇ\\kzţ¦šnņäÔVĂîĪ¬|vW’®l¤èØr‚˜•xm¶ă~lÄƯĄ̈́öȄEÔ¤ØQĄ–Ą»ƢjȦOǺ¨ìSŖÆƬy”Qœv`–cwƒZSÌ®ü±DŽ]ŀç¬B¬©ńzƺŷɄeeOĨS’Œfm Ċ‚ƀP̎ēz©Ċ‚ÄÕÊmgŸÇsJ¥ƔˆŊśæ’΁Ñqv¿íUOµª‰ÂnĦÁ_½ä@ê텣P}Ġ[@gġ}g“ɊדûÏWXá¢užƻÌsNͽƎÁ§č՛AēeL³àydl›¦ĘVçŁpśdžĽĺſʃQíÜçÛġԏsĕ¬—Ǹ¯YßċġHµ ¡eå`ļƒrĉŘóƢFì“ĎWøxÊk†”ƈdƬv|–I|·©NqńRŀƒ¤é”eŊœŀ›ˆàŀU²ŕƀB‚Q£Ď}L¹Îk@©ĈuǰųǨ”Ú§ƈnTËÇéƟÊcfčŤ^Xm‡—HĊĕË«W·ċëx³ǔķÐċJā‚wİ_ĸ˜Ȁ^ôWr­°oú¬Ħ…ŨK~”ȰCĐ´Ƕ£’fNÎèâw¢XnŮeÂÆĶŽ¾¾xäLĴĘlļO¤ÒĨA¢Êɚ¨®‚ØCÔ ŬGƠ”ƦYĜ‡ĘÜƬDJ—g_ͥœ@čŅĻA“¶¯@wÎqC½Ĉ»NŸăëK™ďÍQ“Ùƫ[«Ãí•gßÔÇOÝáW‘ñuZ“¯ĥ€Ÿŕā¡ÑķJu¤E Ÿå¯°WKɱ_d_}}vyŸõu¬ï¹ÓU±½@gÏ¿rýD‰†g…Cd‰µ—°MFYxw¿CG£‹Rƛ½Õ{]L§{qqąš¿BÇƻğëšܭNJË|c²}Fµ}›ÙRsÓpg±ŠQNqǫŋRwŕnéÑÉKŸ†«SeYR…ŋ‹@{¤SJ}šD Ûǖ֍Ÿ]gr¡µŷjqWÛham³~S«“„›Þ]"]],"encodeOffsets":[[[134456,44547]]]}},{"type":"Feature","id":"320000","properties":{"id":"320000","cp":[119.767413,33.041544],"name":"江苏","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@cþÅPiŠ`ZŸRu¥É\\]~°ŽY`µ†Óƒ^phÁbnÀşúŽòa–ĬºTÖŒb‚˜e¦¦€{¸ZâćNpŒ©žHr|^ˆmjhŠSEb\\afv`sz^lkŽlj‹Ätg‹¤D˜­¾Xš¿À’|ДiZ„ȀåB·î}GL¢õcßjaŸyBFµÏC^ĭ•cÙt¿sğH]j{s©HM¢ƒQnDÀ©DaÜތ·jgàiDbPufjDk`dPOîƒhw¡ĥ‡¥šG˜ŸP²ĐobºrY†„î¶aHŢ´ ]´‚rılw³r_{£DB_Ûdåuk|ˆŨ¯F Cºyr{XFy™e³Þċ‡¿Â™kĭB¿„MvÛpm`rÚã”@Ę¹hågËÖƿxnlč¶Åì½Ot¾dJlŠVJʜǀœŞqvnOŠ^ŸJ”Z‘ż·Q}ê͎ÅmµÒ]Žƍ¦Dq}¬R^èĂ´ŀĻĊIԒtžIJyQŐĠMNtœR®òLh‰›Ěs©»œ}OӌGZz¶A\\jĨFˆäOĤ˜HYš†JvÞHNiÜaϚɖnFQlšNM¤ˆB´ĄNöɂtp–Ŭdf先‹qm¿QûŠùއÚb¤uŃJŴu»¹Ą•lȖħŴw̌ŵ²ǹǠ͛hĭłƕrçü±Y™xci‡tğ®jű¢KOķ•Coy`å®VTa­_Ā]ŐÝɞï²ʯÊ^]afYǸÃĆēĪȣJđ͍ôƋĝÄ͎ī‰çÛɈǥ£­ÛmY`ó£Z«§°Ó³QafusNıDž_k}¢m[ÝóDµ—¡RLčiXy‡ÅNïă¡¸iĔϑNÌŕoēdōîåŤûHcs}~Ûwbù¹£¦ÓCt‹OPrƒE^ÒoŠg™ĉIµžÛÅʹK…¤½phMŠü`o怆ŀ"],"encodeOffsets":[[121740,32276]]}},{"type":"Feature","id":"330000","properties":{"id":"330000","cp":[120.153576,29.287459],"name":"浙江","childNum":45},"geometry":{"type":"MultiPolygon","coordinates":[["@@E^dQ]K"],["@@jX^j‡"],["@@sfŠbU‡"],["@@qP\\xz[ck"],["@@‘Rƒ¢‚FX}°[s_"],["@@Cbœ\\—}"],["@@e|v\\la{u"],["@@v~u}"],["@@QxÂF¯}"],["@@¹nŒvÞs¯o"],["@@rSkUEj"],["@@bi­ZŒP"],["@@p[}INf"],["@@À¿€"],["@@¹dnbŒ…"],["@@rSŸBnR"],["@@g~h}"],["@@FlEk"],["@@OdPc"],["@@v[u\\"],["@@FjâL~wyoo~›sµL–\\"],["@@¬e¹aNˆ"],["@@\\nÔ¡q]L³ë\\ÿ®ŒQ֎"],["@@ÊA­©[¬"],["@@KxŒv­"],["@@@hlIk]"],["@@pW{o||j"],["@@Md|_mC"],["@@¢…X£ÏylD¼XˆtH"],["@@hlÜ[LykAvyfw^Ež›¤"],["@@fp¤Mus“R"],["@@®_ma~•LÁ¬šZ"],["@@iM„xZ"],["@@ZcYd"],["@@Z~dOSo|A¿qZv"],["@@@`”EN¡v"],["@@|–TY{"],["@@@n@m"],["@@XWkCT\\"],["@@ºwšZRkĕWO¢"],["@@™X®±Grƪ\\ÔáXq{‹"],["@@ůTG°ĄLHm°UC‹"],["@@¤Ž€aÜx~}dtüGæţŎíĔcŖpMËВjē¢·ðĄÆMzˆjWKĎ¢Q¶˜À_꒔_Bı€i«pZ€gf€¤Nrq]§ĂN®«H±‡yƳí¾×ŸīàLłčŴǝĂíÀBŖÕªˆŠÁŖHŗʼnåqûõi¨hÜ·ƒñt»¹ýv_[«¸m‰YL¯‰Qª…mĉÅdMˆ•gÇjcº«•ęœ¬­K­´ƒB«Âącoċ\\xKd¡gěŧ«®á’[~ıxu·Å”KsËɏc¢Ù\\ĭƛëbf¹­ģSƒĜkáƉÔ­ĈZB{ŠaM‘µ‰fzʼnfåÂŧįƋǝÊĕġć£g³ne­ą»@­¦S®‚\\ßðCšh™iqªĭiAu‡A­µ”_W¥ƣO\\lċĢttC¨£t`ˆ™PZäuXßBs‡Ļyek€OđġĵHuXBšµ]׌‡­­\\›°®¬F¢¾pµ¼kŘó¬Wät’¸|@ž•L¨¸µr“ºù³Ù~§WI‹ŸZWŽ®’±Ð¨ÒÉx€`‰²pĜ•rOògtÁZ}þÙ]„’¡ŒŸFK‚wsPlU[}¦Rvn`hq¬\\”nQ´ĘRWb”‚_ rtČFI֊kŠŠĦPJ¶ÖÀÖJĈĄTĚòžC ²@Pú…Øzœ©PœCÈڜĒ±„hŖ‡l¬â~nm¨f©–iļ«m‡nt–u†ÖZÜÄj“ŠLŽ®E̜Fª²iÊxبžIÈhhst"],["@@o\\V’zRZ}y"],["@@†@°¡mۛGĕ¨§Ianá[ýƤjfæ‡ØL–•äGr™"]],"encodeOffsets":[[[125592,31553]],[[125785,31436]],[[125729,31431]],[[125513,31380]],[[125223,30438]],[[125115,30114]],[[124815,29155]],[[124419,28746]],[[124095,28635]],[[124005,28609]],[[125000,30713]],[[125111,30698]],[[125078,30682]],[[125150,30684]],[[124014,28103]],[[125008,31331]],[[125411,31468]],[[125329,31479]],[[125626,30916]],[[125417,30956]],[[125254,30976]],[[125199,30997]],[[125095,31058]],[[125083,30915]],[[124885,31015]],[[125218,30798]],[[124867,30838]],[[124755,30788]],[[124802,30809]],[[125267,30657]],[[125218,30578]],[[125200,30562]],[[124968,30474]],[[125167,30396]],[[124955,29879]],[[124714,29781]],[[124762,29462]],[[124325,28754]],[[123990,28459]],[[125366,31477]],[[125115,30363]],[[125369,31139]],[[122495,31878]],[[125329,30690]],[[125192,30787]]]}},{"type":"Feature","id":"340000","properties":{"id":"340000","cp":[117.283042,31.26119],"name":"安徽","childNum":3},"geometry":{"type":"MultiPolygon","coordinates":[["@@^iuLX^"],["@@‚e©Ehl"],["@@°ZÆëϵmkǀwÌÕæhºgBĝâqÙĊz›ÖgņtÀÁÊÆá’hEz|WzqD¹€Ÿ°E‡ŧl{ævÜcA`¤C`|´qžxIJkq^³³ŸGšµbƒíZ…¹qpa±ď OH—¦™Ħˆx¢„gPícOl_iCveaOjCh߸i݋bÛªCC¿€m„RV§¢A|t^iĠGÀtÚs–d]ĮÐDE¶zAb àiödK¡~H¸íæAžǿYƒ“j{ď¿‘™À½W—®£ChŒÃsiŒkkly]_teu[bFa‰Tig‡n{]Gqªo‹ĈMYá|·¥f¥—őaSÕė™NµñĞ«ImŒ_m¿Âa]uĜp …Z_§{Cƒäg¤°r[_Yj‰ÆOdý“[ŽI[á·¥“Q_n‡ùgL¾mv™ˊBÜƶĊJhšp“c¹˜O]iŠ]œ¥ jtsggJǧw×jÉ©±›EFˍ­‰Ki”ÛÃÕYv…s•ˆm¬njĻª•§emná}k«ŕˆƒgđ²Ù›DǤ›í¡ªOy›†×Où±@DŸñSęćăÕIÕ¿IµĥO‰‰jNÕËT¡¿tNæŇàåyķrĕq§ÄĩsWÆߎF¶žX®¿‰mŒ™w…RIޓfßoG‘³¾©uyH‘į{Ɓħ¯AFnuP…ÍÔzšŒV—dàôº^Ðæd´€‡oG¤{S‰¬ćxã}›ŧ×Kǥĩ«žÕOEзÖdÖsƘѨ[’Û^Xr¢¼˜§xvěƵ`K”§ tÒ´Cvlo¸fzŨð¾NY´ı~ÉĔē…ßúLÃϖ_ÈÏ|]ÂÏFl”g`bšežž€n¾¢pU‚h~ƴĖ¶_‚r sĄ~cž”ƈ]|r c~`¼{À{ȒiJjz`îÀT¥Û³…]’u}›f…ïQl{skl“oNdŸjŸäËzDvčoQŠďHI¦rb“tHĔ~BmlRš—V_„ħTLnñH±’DžœL‘¼L˜ªl§Ťa¸ŒĚlK²€\\RòvDcÎJbt[¤€D@®hh~kt°ǾzÖ@¾ªdb„YhüóZ ň¶vHrľ\\ʗJuxAT|dmÀO„‹[ÃԋG·ĚąĐlŪÚpSJ¨ĸˆLvÞcPæķŨŽ®mАˆálŸwKhïgA¢ųƩޖ¤OȜm’°ŒK´"]],"encodeOffsets":[[[121722,32278]],[[119475,30423]],[[119168,35472]]]}},{"type":"Feature","id":"350000","properties":{"id":"350000","cp":[118.306239,26.075302],"name":"福建","childNum":18},"geometry":{"type":"MultiPolygon","coordinates":[["@@“zht´‡]"],["@@aj^~ĆG—©O"],["@@ed¨„C}}i"],["@@@vˆPGsQ"],["@@‰sBz‚ddW]Q"],["@@SŽ¨Q“{"],["@@NŽVucW"],["@@qptBAq"],["@@‰’¸[mu"],["@@Q\\pD]_"],["@@jSwUadpF"],["@@eXª~ƒ•"],["@@AjvFso"],["@@fT–›_Çí\\Ÿ™—v|ba¦jZÆy€°"],["@@IjJi"],["@@wJI€ˆxš«¼AoNe{M­"],["@@K‰±¡Óˆ”ČäeZ"],["@@k¡¹Eh~c®wBk‹UplÀ¡I•~Māe£bN¨gZý¡a±Öcp©PhžI”Ÿ¢Qq…ÇGj‹|¥U™ g[Ky¬ŏ–v@OpˆtÉEŸF„\\@ åA¬ˆV{Xģ‰ĐBy…cpě…¼³Ăp·¤ƒ¥o“hqqÚ¡ŅLsƒ^ᗞ§qlŸÀhH¨MCe»åÇGD¥zPO£čÙkJA¼ß–ėu›ĕeûҍiÁŧSW¥˜QŠûŗ½ùěcݧSùĩąSWó«íęACµ›eR—åǃRCÒÇZÍ¢‹ź±^dlsŒtjD¸•‚ZpužÔâÒH¾oLUêÃÔjjēò´ĄW‚ƛ…^Ñ¥‹ĦŸ@Çò–ŠmŒƒOw¡õyJ†yD}¢ďÑÈġfŠZd–a©º²z£šN–ƒjD°Ötj¶¬ZSÎ~¾c°¶Ðm˜x‚O¸¢Pl´žSL|¥žA†ȪĖM’ņIJg®áIJČĒü` ŽQF‡¬h|ÓJ@zµ |ê³È ¸UÖŬŬÀEttĸr‚]€˜ðŽM¤ĶIJHtÏ A’†žĬkvsq‡^aÎbvŒd–™fÊòSD€´Z^’xPsÞrv‹ƞŀ˜jJd×ŘÉ ®A–ΦĤd€xĆqAŒ†ZR”ÀMźŒnĊ»ŒİÐZ— YX–æJŠyĊ²ˆ·¶q§·–K@·{s‘Xãô«lŗ¶»o½E¡­«¢±¨Yˆ®Ø‹¶^A™vWĶGĒĢžPlzfˆļŽtàAvWYãšO_‡¤sD§ssČġ[kƤPX¦Ž`¶“ž®ˆBBvĪjv©šjx[L¥àï[F…¼ÍË»ğV`«•Ip™}ccÅĥZE‹ãoP…´B@ŠD—¸m±“z«Ƴ—¿å³BRضˆœWlâþäą`“]Z£Tc— ĹGµ¶H™m@_©—kŒ‰¾xĨ‡ôȉðX«½đCIbćqK³Á‹Äš¬OAwã»aLʼn‡ËĥW[“ÂGI—ÂNxij¤D¢ŽîĎÎB§°_JœGsƒ¥E@…¤uć…P‘å†cuMuw¢BI¿‡]zG¹guĮck\\_"]],"encodeOffsets":[[[123250,27563]],[[122541,27268]],[[123020,27189]],[[122916,27125]],[[122887,26845]],[[122808,26762]],[[122568,25912]],[[122778,26197]],[[122515,26757]],[[122816,26587]],[[123388,27005]],[[122450,26243]],[[122578,25962]],[[121255,25103]],[[120987,24903]],[[122339,25802]],[[121042,25093]],[[122439,26024]]]}},{"type":"Feature","id":"360000","properties":{"id":"360000","cp":[115.592151,27.676493],"name":"江西","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@ĢĨƐgļˆ¼ÂMD~ņªe^\\^§„ý©j׍cZ†Ø¨zdÒa¶ˆlҍJŒìõ`oz÷@¤u޸´†ôęöY¼‰HČƶajlÞƩ¥éZ[”|h}^U Œ ¥p„ĄžƦO lt¸Æ €Q\\€ŠaÆ|CnÂOjt­ĚĤd’ÈŒF`’¶„@Ð딠¦ōҞ¨Sêv†HĢûXD®…QgėWiØPÞìºr¤dž€NĠ¢l–•ĄtZoœCƞÔºCxrpĠV®Ê{f_Y`_ƒeq’’®Aot`@o‚DXfkp¨|Šs¬\\D‘ÄSfè©Hn¬…^DhÆyøJh“ØxĢĀLʈ„ƠPżċĄwȠ̦G®ǒĤäTŠÆ~ĦwŠ«|TF¡Šn€c³Ïå¹]ĉđxe{ÎӐ†vOEm°BƂĨİ|G’vz½ª´€H’àp”eJ݆Qšxn‹ÀŠW­žEµàXÅĪt¨ÃĖrÄwÀFÎ|ňÓMå¼ibµ¯»åDT±m[“r«_gŽmQu~¥V\\OkxtL E¢‹ƒ‘Ú^~ýê‹Pó–qo슱_Êw§ÑªåƗā¼‹mĉŹ‹¿NQ“…YB‹ąrwģcÍ¥B•Ÿ­ŗÊcØiI—žƝĿuŒqtāwO]‘³YCñTeɕš‹caub͈]trlu€ī…B‘ПGsĵıN£ï—^ķqss¿FūūV՟·´Ç{éĈý‰ÿ›OEˆR_ŸđûIċâJh­ŅıN‘ȩĕB…¦K{Tk³¡OP·wn—µÏd¯}½TÍ«YiµÕsC¯„iM•¤™­•¦¯P|ÿUHv“he¥oFTu‰õ\\ŽOSs‹MòđƇiaºćXŸĊĵà·çhƃ÷ǜ{‘ígu^›đg’m[×zkKN‘¶Õ»lčÓ{XSƉv©_ÈëJbVk„ĔVÀ¤P¾ºÈMÖxlò~ªÚàGĂ¢B„±’ÌŒK˜y’áV‡¼Ã~­…`g›ŸsÙfI›Ƌlę¹e|–~udjˆuTlXµf`¿JdŠ[\\˜„L‚‘²"],"encodeOffsets":[[116689,26234]]}},{"type":"Feature","id":"370000","properties":{"id":"370000","cp":[118.000923,36.275807],"name":"山东","childNum":13},"geometry":{"type":"MultiPolygon","coordinates":[["@@Xjd]{K"],["@@itbFHy"],["@@HlGk"],["@@T‚ŒGŸy"],["@@K¬˜•‹U"],["@@WdXc"],["@@PtOs"],["@@•LnXhc"],["@@ppVƒu]Or"],["@@cdzAUa"],["@@udRhnCI‡"],["@@ˆoIƒpR„"],["@@Ľč{fzƤî’Kš–ÎMĮ]†—ZFˆ½Y]â£ph’™š¶¨râøÀ†ÎǨ¤^ºÄ”Gzˆ~grĚĜlĞƄLĆdž¢Îo¦–cv“Kb€gr°Wh”mZp ˆL]LºcU‰Æ­n”żĤÌĒœbAnrOAœ´žȊcÀbƦUØrĆUÜøœĬƞ†š˜Ez„VL®öØBkŖÝĐĖ¹ŧ̄±ÀbÎɜnb²ĦhņBĖ›žįĦåXćì@L¯´ywƕCéõė ƿ¸‘lµ¾Z|†ZWyFYŸ¨Mf~C¿`€à_RÇzwƌfQnny´INoƬˆèôº|sT„JUš›‚L„îVj„ǎ¾Ē؍‚Dz²XPn±ŴPè¸ŔLƔÜƺ_T‘üÃĤBBċȉöA´fa„˜M¨{«M`‡¶d¡ô‰Ö°šmȰBÔjjŒ´PM|”c^d¤u•ƒ¤Û´Œä«ƢfPk¶Môlˆ]Lb„}su^ke{lC‘…M•rDŠÇ­]NÑFsmoõľH‰yGă{{çrnÓE‰‹ƕZGª¹Fj¢ïW…uøCǷ돡ąuhÛ¡^Kx•C`C\\bÅxì²ĝÝ¿_N‰īCȽĿåB¥¢·IŖÕy\\‡¹kx‡Ã£Č×GDyÕ¤ÁçFQ¡„KtŵƋ]CgÏAùSed‡cÚź—ŠuYfƒyMmhUWpSyGwMPqŀ—›Á¼zK›¶†G•­Y§Ëƒ@–´śÇµƕBmœ@Io‚g——Z¯u‹TMx}C‘‰VK‚ï{éƵP—™_K«™pÛÙqċtkkù]gŽ‹Tğwo•ɁsMõ³ă‡AN£™MRkmEʕč™ÛbMjÝGu…IZ™—GPģ‡ãħE[iµBEuŸDPԛ~ª¼ętŠœ]ŒûG§€¡QMsğNPŏįzs£Ug{đJĿļā³]ç«Qr~¥CƎÑ^n¶ÆéÎR~Ż¸Y’I“] P‰umŝrƿ›‰›Iā‹[x‰edz‹L‘¯v¯s¬ÁY…~}…ťuŁŒg›ƋpÝĄ_ņī¶ÏSR´ÁP~ž¿Cyžċßdwk´Ss•X|t‰`Ä Èð€AªìÎT°¦Dd–€a^lĎDĶÚY°Ž`ĪŴǒˆ”àŠv\\ebŒZH„ŖR¬ŢƱùęO•ÑM­³FۃWp[ƒ"]],"encodeOffsets":[[[123806,39303]],[[123821,39266]],[[123742,39256]],[[123702,39203]],[[123649,39066]],[[123847,38933]],[[123580,38839]],[[123894,37288]],[[123043,36624]],[[123344,38676]],[[123522,38857]],[[123628,38858]],[[118260,36742]]]}},{"type":"Feature","id":"410000","properties":{"id":"410000","cp":[113.665412,33.757975],"name":"河南","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@•ýL™ùµP³swIÓxcŢĞð†´E®žÚPt†ĴXØx¶˜@«ŕŕQGƒ‹Yfa[şu“ßǩ™đš_X³ijÕčC]kbc•¥CS¯ëÍB©÷‹–³­Siˆ_}m˜YTtž³xlàcȂzÀD}ÂOQ³ÐTĨ¯†ƗòËŖ[hœł‹Ŧv~††}ÂZž«¤lPǕ£ªÝŴÅR§ØnhcŒtâk‡nύ­ľŹUÓÝdKuķ‡I§oTũÙďkęĆH¸ÓŒ\\ăŒ¿PcnS{wBIvɘĽ[GqµuŸŇôYgûƒZcaŽ©@½Õǽys¯}lgg@­C\\£as€IdÍuCQñ[L±ęk·‹ţb¨©kK—’»›KC²‘òGKmĨS`ƒ˜UQ™nk}AGē”sqaJ¥ĐGR‰ĎpCuÌy ã iMc”plk|tRk†ðœev~^‘´†¦ÜŽSí¿_iyjI|ȑ|¿_»d}qŸ^{“Ƈdă}Ÿtqµ`Ƴĕg}V¡om½fa™Ço³TTj¥„tĠ—Ry”K{ùÓjuµ{t}uËR‘iŸvGŠçJFjµŠÍyqΘàQÂFewixGw½Yŷpµú³XU›½ġy™łå‰kÚwZXˆ·l„¢Á¢K”zO„Λ΀jc¼htoDHr…|­J“½}JZ_¯iPq{tę½ĕ¦Zpĵø«kQ…Ťƒ]MÛfaQpě±ǽ¾]u­Fu‹÷nƒ™čįADp}AjmcEǒaª³o³ÆÍSƇĈÙDIzˑ赟^ˆKLœ—i—Þñ€[œƒaA²zz‰Ì÷Dœ|[šíijgf‚ÕÞd®|`ƒĆ~„oĠƑô³Ŋ‘D×°¯CsŠøÀ«ì‰UMhTº¨¸ǡîS–Ô„DruÂÇZ•ÖEŽ’vPZ„žW”~؋ÐtĄE¢¦Ðy¸bŠô´oŬ¬Ž²Ês~€€]®tªašpŎJ¨Öº„_ŠŔ–`’Ŗ^Ѝ\\Ĝu–”~m²Ƹ›¸fW‰ĦrƔ}Î^gjdfÔ¡J}\\n C˜¦þWxªJRÔŠu¬ĨĨmF†dM{\\d\\ŠYÊ¢ú@@¦ª²SŠÜsC–}fNècbpRmlØ^g„d¢aÒ¢CZˆZxvÆ¶N¿’¢T@€uCœ¬^ĊðÄn|žlGl’™Rjsp¢ED}€Fio~ÔNŽ‹„~zkĘHVsDzßjƒŬŒŠŢ`Pûàl¢˜\\ÀœEhŽİgÞē X¼Pk–„|m"],"encodeOffsets":[[118256,37017]]}},{"type":"Feature","id":"420000","properties":{"id":"420000","cp":[113.298572,30.684355],"name":"湖北","childNum":3},"geometry":{"type":"MultiPolygon","coordinates":[["@@AB‚"],["@@lskt"],["@@¾«}{ra®pîÃ\\™›{øCŠËyyB±„b\\›ò˜Ý˜jK›‡L ]ĎĽÌ’JyÚCƈćÎT´Å´pb©È‘dFin~BCo°BĎĚømvŒ®E^vǾ½Ĝ²Ro‚bÜeNŽ„^ĺ£R†¬lĶ÷YoĖ¥Ě¾|sOr°jY`~I”¾®I†{GqpCgyl{‡£œÍƒÍyPL“¡ƒ¡¸kW‡xYlÙ抚ŁĢzœ¾žV´W¶ùŸo¾ZHxjwfx„GNÁ•³Xéæl¶‰EièIH‰ u’jÌQ~v|sv¶Ôi|ú¢Fh˜Qsğ¦ƒSiŠBg™ÐE^ÁÐ{–čnOÂȞUÎóĔ†ÊēIJ}Z³½Mŧïeyp·uk³DsѨŸL“¶_œÅuèw»—€¡WqÜ]\\‘Ò§tƗcÕ¸ÕFÏǝĉăxŻČƟO‡ƒKÉġÿ×wg”÷IÅzCg†]m«ªGeçÃTC’«[‰t§{loWeC@ps_Bp‘­r‘„f_``Z|ei¡—oċMqow€¹DƝӛDYpûs•–‹Ykıǃ}s¥ç³[§ŸcYŠ§HK„«Qy‰]¢“wwö€¸ïx¼ņ¾Xv®ÇÀµRĠЋžHMž±cÏd„ƒǍũȅȷ±DSyúĝ£ŤĀàtÖÿï[îb\\}pĭÉI±Ñy…¿³x¯N‰o‰|¹H™ÏÛm‹júË~Tš•u˜ęjCöAwě¬R’đl¯ Ñb­‰ŇT†Ŀ_[Œ‘IčĄʿnM¦ğ\\É[T·™k¹œ©oĕ@A¾w•ya¥Y\\¥Âaz¯ãÁ¡k¥ne£Ûw†E©Êō¶˓uoj_Uƒ¡cF¹­[Wv“P©w—huÕyBF“ƒ`R‹qJUw\\i¡{jŸŸEPïÿ½fć…QÑÀQ{ž‚°‡fLԁ~wXg—ītêݾ–ĺ‘Hdˆ³fJd]‹HJ²…E€ƒoU¥†HhwQsƐ»Xmg±çve›]Dm͂PˆoCc¾‹_h”–høYrŊU¶eD°Č_N~øĹĚ·`z’]Äþp¼…äÌQŒv\\rCŒé¾TnkžŐڀÜa‡“¼ÝƆĢ¶Ûo…d…ĔňТJq’Pb ¾|JŒ¾fXŠƐîĨ_Z¯À}úƲ‹N_ĒĊ^„‘ĈaŐyp»CÇĕKŠšñL³ŠġMŒ²wrIÒŭxjb[œžn«øœ˜—æˆàƒ ^²­h¯Ú€ŐªÞ¸€Y²ĒVø}Ā^İ™´‚LŠÚm„¥ÀJÞ{JVŒųÞŃx×sxxƈē ģMř–ÚðòIf–Ċ“Œ\\Ʈ±ŒdʧĘD†vČ_Àæ~DŒċ´A®µ†¨ØLV¦êHÒ¤"]],"encodeOffsets":[[[113712,34000]],[[115612,30507]],[[113649,34054]]]}},{"type":"Feature","id":"430000","properties":{"id":"430000","cp":[111.782279,28.09409],"name":"湖南","childNum":3},"geometry":{"type":"MultiPolygon","coordinates":[["@@—n„FTs"],["@@ßÅÆችÔXr—†CO™“…ËR‘ïÿĩ­TooQyšÓ[‹ŅBE¬–ÎÓXa„į§Ã¸G °ITxp‰úxÚij¥Ïš–Ģ¾ŠedžÄ©ĸG…œàGh‚€M¤–Â_U}Ċ}¢pczfŠþg¤€”ÇòAV‘‹M"],["@@©K—ƒA·³CQ±Á«³BUŠƑ¹AŠtćOw™D]ŒJiØSm¯b£‘ylƒ›X…HËѱH•«–‘C^õľA–Å§¤É¥„ïyuǙuA¢^{ÌC´­¦ŷJ£^[†“ª¿‡ĕ~•Ƈ…•N… skóā‡¹¿€ï]ă~÷O§­@—Vm¡‹Qđ¦¢Ĥ{ºjԏŽŒª¥nf´•~ÕoŸž×Ûą‹MąıuZœmZcÒ IJĪ²SÊDŽŶ¨ƚƒ’CÖŎªQؼrŭŽ­«}NÏürʬŒmjr€@ĘrTW ­SsdHzƓ^ÇÂyUi¯DÅYlŹu{hTœ}mĉ–¹¥ě‰Dÿë©ıÓ[Oº£ž“¥ót€ł¹MՄžƪƒ`Pš…Di–ÛUŠ¾Å‌ìˆU’ñB“È£ýhe‰dy¡oċ€`pfmjP~‚kZa…ZsÐd°wj§ƒ@€Ĵ®w~^‚kÀÅKvNmX\\¨a“”сqvíó¿F„¤¡@ũÑVw}S@j}¾«pĂr–ªg àÀ²NJ¶¶Dô…K‚|^ª†Ž°LX¾ŴäPĪ±œ£EXd›”^¶›IJÞܓ~‘u¸ǔ˜Ž›MRhsR…e†`ÄofIÔ\\Ø  i”ćymnú¨cj ¢»–GČìƊÿШXeĈĀ¾Oð Fi ¢|[jVxrIQŒ„_E”zAN¦zLU`œcªx”OTu RLÄ¢dV„i`p˔vŎµªÉžF~ƒØ€d¢ºgİàw¸Áb[¦Zb¦–z½xBĖ@ªpº›šlS¸Ö\\Ĕ[N¥ˀmĎă’J\\‹ŀ`€…ňSڊĖÁĐiO“Ĝ«BxDõĚiv—ž–S™Ì}iùŒžÜnšÐºGŠ{Šp°M´w†ÀÒzJ²ò¨ oTçüöoÛÿñŽőФ‚ùTz²CȆȸǎŪƒƑÐc°dPÎŸğ˶[Ƚu¯½WM¡­Éž“’B·rížnZŸÒ `‡¨GA¾\\pē˜XhÆRC­üWGġu…T靧Ŏѝ©ò³I±³}_‘‹EÃħg®ęisÁPDmÅ{‰b[Rşs·€kPŸŽƥƒóRo”O‹ŸVŸ~]{g\\“êYƪ¦kÝbiċƵŠGZ»Ěõ…ó·³vŝž£ø@pyö_‹ëŽIkѵ‡bcѧy…×dY؎ªiþž¨ƒ[]f]Ņ©C}ÁN‡»hĻħƏ’ĩ"]],"encodeOffsets":[[[115640,30489]],[[112543,27312]],[[116690,26230]]]}},{"type":"Feature","id":"440000","properties":{"id":"440000","cp":[113.280637,23.125178],"name":"广东","childNum":24},"geometry":{"type":"MultiPolygon","coordinates":[["@@QdˆAua"],["@@ƒlxDLo"],["@@sbhNLo"],["@@Ă āŸ"],["@@WltO[["],["@@Krœ]S"],["@@e„„I]y"],["@@I|„Mym"],["@@ƒÛ³LSŒž¼Y"],["@@nvºB–ëui©`¾"],["@@zdšÛ›Jw®"],["@@†°…¯"],["@@a yAª¸ËJIx،@€ĀHAmßV¡o•fu•o"],["@@šs‰ŗÃÔėAƁ›ZšÄ ~°ČP‚‹äh"],["@@‹¶Ý’Ì‚vmĞh­ı‡Q"],["@@HœŠdSjĒ¢D}war…“u«ZqadYM"],["@@elŒ\\LqqU"],["@@~rMo\\"],["@@f„^ƒC"],["@@øPªoj÷ÍÝħXČx”°Q¨ıXNv"],["@@gÇƳˆŽˆ”oˆŠˆ[~tly"],["@@E–ÆC¿‘"],["@@OŽP"],["@@w‹†đóg‰™ĝ—[³‹¡VÙæÅöM̳¹pÁaËýý©D©Ü“JŹƕģGą¤{Ùū…ǘO²«BƱéA—Ò‰ĥ‡¡«BhlmtÃPµyU¯uc“d·w_bŝcīímGOŽ|KP’ȏ‡ŹãŝIŕŭŕ@Óoo¿ē‹±ß}Ž…ŭ‚ŸIJWÈCőâUâǙI›ğʼn©I›ijEׅÁ”³Aó›wXJþ±ÌŒÜӔĨ£L]ĈÙƺZǾĆĖMĸĤfŒÎĵl•ŨnȈ‘ĐtF”Š–FĤ–‚êk¶œ^k°f¶gŠŽœ}®Fa˜f`vXŲxl˜„¦–ÔÁ²¬ÐŸ¦pqÊ̲ˆi€XŸØRDÎ}†Ä@ZĠ’s„x®AR~®ETtĄZ†–ƈfŠŠHâÒÐA†µ\\S¸„^wĖkRzŠalŽŜ|E¨ÈNĀňZTŒ’pBh£\\ŒĎƀuXĖtKL–¶G|Ž»ĺEļĞ~ÜĢÛĊrˆO˜Ùîvd]nˆ¬VœÊĜ°R֟pM††–‚ƂªFbwžEÀˆ˜©Œž\\…¤]ŸI®¥D³|ˎ]CöAŤ¦…æ’´¥¸Lv¼€•¢ĽBaô–F~—š®²GÌҐEY„„œzk¤’°ahlV՞I^‹šCxĈPŽsB‰ƒºV‰¸@¾ªR²ĨN]´_eavSi‡vc•}p}Đ¼ƌkJœÚe thœ†_¸ ºx±ò_xN›Ë‹²‘@ƒă¡ßH©Ùñ}wkNÕ¹ÇO½¿£ĕ]ly_WìIžÇª`ŠuTÅxYĒÖ¼k֞’µ‚MžjJÚwn\\h‘œĒv]îh|’È›Ƅøègž¸Ķß ĉĈWb¹ƀdéʌNTtP[ŠöSvrCZžžaGuœbo´ŖÒÇА~¡zCI…özx¢„Pn‹•‰Èñ @ŒĥÒ¦†]ƞŠV}³ăĔñiiÄÓVépKG½Ä‘ÓávYo–C·sit‹iaÀy„ŧΡÈYDÑům}‰ý|m[węõĉZÅxUO}÷N¹³ĉo_qtă“qwµŁYلǝŕ¹tïÛUïmRCº…ˆĭ|µ›ÕÊK™½R‘ē ó]‘–GªęAx–»HO£|ām‡¡diď×YïYWªʼnOeÚtĐ«zđ¹T…ā‡úE™á²\\‹ķÍ}jYàÙÆſ¿Çdğ·ùTßÇţʄ¡XgWÀLJğ·¿ÃˆOj YÇ÷Qě‹i"]],"encodeOffsets":[[[117381,22988]],[[116552,22934]],[[116790,22617]],[[116973,22545]],[[116444,22536]],[[116931,22515]],[[116496,22490]],[[116453,22449]],[[113301,21439]],[[118726,21604]],[[118709,21486]],[[113210,20816]],[[115482,22082]],[[113171,21585]],[[113199,21590]],[[115232,22102]],[[115739,22373]],[[115134,22184]],[[113056,21175]],[[119573,21271]],[[119957,24020]],[[115859,22356]],[[116561,22649]],[[116285,22746]]]}},{"type":"Feature","id":"450000","properties":{"id":"450000","cp":[108.320004,22.82402],"name":"广西","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@H– TQ§•A"],["@@ĨʪƒLƒƊDÎĹĐCǦė¸zÚGn£¾›rªŀÜt¬@֛ڈSx~øOŒ˜ŶÐÂæȠ\\„ÈÜObĖw^oބLf¬°bI lTØB̈F£Ć¹gñĤaY“t¿¤VSñœK¸¤nM†¼‚JE±„½¸šŠño‹ÜCƆæĪ^ŠĚQÖ¦^‡ˆˆf´Q†üÜʝz¯šlzUĺš@쇀p¶n]sxtx¶@„~ÒĂJb©gk‚{°‚~c°`ԙ¬rV\\“la¼¤ôá`¯¹LC†ÆbŒxEræO‚v[H­˜„[~|aB£ÖsºdAĐzNÂðsŽÞƔ…Ĥªbƒ–ab`ho¡³F«èVloŽ¤™ÔRzpp®SŽĪº¨ÖƒºN…ij„d`’a”¦¤F³ºDÎńĀìŠCžĜº¦Ċ•~nS›|gźvZkCÆj°zVÈÁƔ]LÊFZg…čP­kini«‹qǀcz͔Y®¬Ů»qR×ō©DՄ‘§ƙǃŵTÉĩ±ŸıdÑnYY›IJvNĆƌØÜ Öp–}e³¦m‹©iÓ|¹Ÿħņ›|ª¦QF¢Â¬ʖovg¿em‡^ucà÷gՎuŒíÙćĝ}FĻ¼Ĺ{µHK•sLSđƃr‹č¤[Ag‘oS‹ŇYMÿ§Ç{Fśbky‰lQxĕƒ]T·¶[B…ÑÏGáşşƇe€…•ăYSs­FQ}­Bƒw‘tYğÃ@~…C̀Q ×W‡j˱rÉ¥oÏ ±«ÓÂ¥•ƒ€k—ŽwWűŒmcih³K›~‰µh¯e]lµ›él•Eģ‰•E“ďs‡’mǖŧē`ãògK_ÛsUʝ“ćğ¶hŒöŒO¤Ǜn³Žc‘`¡y‹¦C‘ez€YŠwa™–‘[ďĵűMę§]X˜Î_‚훘Û]é’ÛUćİÕBƣ±…dƒy¹T^džûÅÑŦ·‡PĻþÙ`K€¦˜…¢ÍeœĥR¿Œ³£[~Œäu¼dl‰t‚†W¸oRM¢ď\\zœ}Æzdvň–{ÎXF¶°Â_„ÒÂÏL©Ö•TmuŸ¼ãl‰›īkiqéfA„·Êµ\\őDc¥ÝF“y›Ôć˜c€űH_hL܋êĺШc}rn`½„Ì@¸¶ªVLŒŠhŒ‹\\•Ţĺk~ŽĠið°|gŒtTĭĸ^x‘vK˜VGréAé‘bUu›MJ‰VÃO¡…qĂXËS‰ģãlýàŸ_ju‡YÛÒB†œG^˜é֊¶§ŽƒEG”ÅzěƒƯ¤Ek‡N[kdåucé¬dnYpAyČ{`]þ¯T’bÜÈk‚¡Ġ•vŒàh„ÂƄ¢J"]],"encodeOffsets":[[[111707,21520]],[[107619,25527]]]}},{"type":"Feature","id":"460000","properties":{"id":"460000","cp":[109.83119,19.031971],"name":"海南","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@š¦Ŝil¢”XƦ‘ƞò–ïè§ŞCêɕrŧůÇąĻõ™·ĉ³œ̅kÇm@ċȧƒŧĥ‰Ľʉ­ƅſ“ȓÒ˦ŝE}ºƑ[ÍĜȋ gÎfǐÏĤ¨êƺ\\Ɔ¸ĠĎvʄȀœÐ¾jNðĀÒRŒšZdž™zÐŘΰH¨Ƣb²_Ġ "],"encodeOffsets":[[112750,20508]]}},{"type":"Feature","id":"510000","properties":{"id":"510000","cp":[104.065735,30.659462],"name":"四川","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@LqKr"],["@@Š[ĻéV£ž_ţġñpG •réÏ·~ąSfy×͂·ºſƽiÍıƣıĻmHH}siaX@iÇ°ÁÃ×t«ƒ­Tƒ¤J–JJŒyJ•ÈŠ`Ohߦ¡uËhIyCjmÿw…ZG……Ti‹SˆsO‰žB²ŸfNmsPaˆ{M{ŠõE‘^Hj}gYpaeuž¯‘oáwHjÁ½M¡pM“–uå‡mni{fk”\\oƒÎqCw†EZ¼K›ĝŠƒAy{m÷L‡wO×SimRI¯rK™õBS«sFe‡]fµ¢óY_ÆPRcue°Cbo׌bd£ŌIHgtrnyPt¦foaXďx›lBowz‹_{ÊéWiêE„GhܸºuFĈIxf®Ž•Y½ĀǙ]¤EyŸF²ċ’w¸¿@g¢§RGv»–áŸW`ÃĵJwi]t¥wO­½a[׈]`Ãi­üL€¦LabbTÀå’c}Íh™Æhˆ‹®BH€î|Ék­¤S†y£„ia©taį·Ɖ`ō¥Uh“O…ƒĝLk}©Fos‰´›Jm„µlŁu—…ø–nÑJWΪ–YÀïAetTžŅ‚ӍG™Ë«bo‰{ıwodƟ½ƒžOġܑµxàNÖ¾P²§HKv¾–]|•B‡ÆåoZ`¡Ø`ÀmºĠ~ÌЧnDž¿¤]wğ@sƒ‰rğu‰~‘Io”[é±¹ ¿žſđӉ@q‹gˆ¹zƱřaí°KtǤV»Ã[ĩǭƑ^ÇÓ@ỗs›Zϕ‹œÅĭ€Ƌ•ěpwDóÖሯneQˌq·•GCœýS]xŸ·ý‹q³•O՜Œ¶Qzßti{ř‰áÍÇWŝŭñzÇW‹pç¿JŒ™‚Xœĩè½cŒF–ÂLiVjx}\\N†ŇĖ¥Ge–“JA¼ÄHfÈu~¸Æ«dE³ÉMA|b˜Ò…˜ćhG¬CM‚õŠ„ƤąAvƒüV€éŀ‰_V̳ĐwQj´·ZeÈÁ¨X´Æ¡Qu·»Ÿ“˜ÕZ³ġqDo‰y`L¬gdp°şŠp¦ėìÅĮZŽ°Iä”h‚‘ˆzŠĵœf²å ›ĚрKp‹IN|‹„Ñz]ń……·FU×é»R³™MƒÉ»GM«€ki€™ér™}Ã`¹ăÞmȝnÁîRǀ³ĜoİzŔwǶVÚ£À]ɜ»ĆlƂ²Ġ…þTº·àUȞÏʦ¶†I’«dĽĢdĬ¿–»Ĕ׊h\\c¬†ä²GêëĤł¥ÀǿżÃÆMº}BÕĢyFVvw–ˆxBèĻĒ©Ĉ“tCĢɽŠȣ¦āæ·HĽî“ôNԓ~^¤Ɗœu„œ^s¼{TA¼ø°¢İªDè¾Ň¶ÝJ‘®Z´ğ~Sn|ªWÚ©òzPOȸ‚bð¢|‹øĞŠŒœŒQìÛÐ@Ğ™ǎRS¤Á§d…i“´ezÝúØã]Hq„kIŸþËQǦÃsǤ[E¬ÉŪÍxXƒ·ÖƁİlƞ¹ª¹|XÊwn‘ÆƄmÀêErĒtD®ċæcQƒ”E®³^ĭ¥©l}äQto˜ŖÜqƎkµ–„ªÔĻĴ¡@Ċ°B²Èw^^RsºTĀ£ŚæœQP‘JvÄz„^Đ¹Æ¯fLà´GC²‘dt˜­ĀRt¼¤ĦOðğfÔðDŨŁĞƘïžPȆ®âbMüÀXZ ¸£@Ś›»»QÉ­™]d“sÖ×_͖_ÌêŮPrĔĐÕGĂeZÜîĘqBhtO ¤tE[h|Y‹Ô‚ZśÎs´xº±UŒ’ñˆt|O’ĩĠºNbgþŠJy^dÂY Į„]Řz¦gC‚³€R`ĀŠz’¢AjŒ¸CL„¤RÆ»@­Ŏk\\Ç´£YW}z@Z}‰Ã¶“oû¶]´^N‡Ò}èN‚ª–P˜Íy¹`S°´†ATe€VamdUĐwʄvĮÕ\\ƒu‹Æŗ¨Yp¹àZÂm™Wh{á„}WØǍ•Éüw™ga§áCNęÎ[ĀÕĪgÖɪX˜øx¬½Ů¦¦[€—„NΆL€ÜUÖ´òrÙŠxR^–†J˜k„ijnDX{Uƒ~ET{ļº¦PZc”jF²Ė@Žp˜g€ˆ¨“B{ƒu¨ŦyhoÚD®¯¢˜ WòàFΤ¨GDäz¦kŮPœġq˚¥À]€Ÿ˜eŽâÚ´ªKxī„Pˆ—Ö|æ[xäJÞĥ‚s’NÖ½ž€I†¬nĨY´®Ð—ƐŠ€mD™ŝuäđđEb…e’e_™v¡}ìęNJē}q”É埁T¯µRs¡M@}ůa†a­¯wvƉåZwž\\Z{åû^›"]],"encodeOffsets":[[[108815,30935]],[[110617,31811]]]}},{"type":"Feature","id":"520000","properties":{"id":"520000","cp":[106.713478,26.578343],"name":"贵州","childNum":3},"geometry":{"type":"MultiPolygon","coordinates":[["@@†G\\†lY£‘in"],["@@q‚|ˆ‚mc¯tχVSÎ"],["@@hÑ£Is‡NgßH†›HªķÃh_¹ƒ¡ĝħń¦uيùŽgS¯JHŸ|sÝÅtÁïyMDč»eÕtA¤{b\\}—ƒG®u\\åPFq‹wÅaD…žK°ºâ_£ùbµ”mÁ‹ÛœĹM[q|hlaªāI}тƒµ@swtwm^oµˆD鼊yV™ky°ÉžûÛR…³‚‡eˆ‡¥]RՋěħ[ƅåÛDpŒ”J„iV™™‰ÂF²I…»mN·£›LbÒYb—WsÀbŽ™pki™TZĄă¶HŒq`……ĥ_JŸ¯ae«ƒKpÝx]aĕÛPƒÇȟ[ÁåŵÏő—÷Pw}‡TœÙ@Õs«ĿÛq©½œm¤ÙH·yǥĘĉBµĨÕnđ]K„©„œá‹ŸG纍§Õßg‡ǗĦTèƤƺ{¶ÉHÎd¾ŚÊ·OÐjXWrãLyzÉAL¾ę¢bĶėy_qMĔąro¼hĊžw¶øV¤w”²Ĉ]ʚKx|`ź¦ÂÈdr„cȁbe¸›`I¼čTF´¼Óýȃr¹ÍJ©k_șl³´_pН`oÒhŽ¶pa‚^ÓĔ}D»^Xyœ`d˜[Kv…JPhèhCrĂĚÂ^Êƌ wˆZL­Ġ£šÁbrzOIl’MM”ĪŐžËr×ÎeŦŽtw|Œ¢mKjSǘňĂStÎŦEtqFT†¾†E쬬ôxÌO¢Ÿ KŠ³ŀºäY†„”PVgŎ¦Ŋm޼VZwVlŒ„z¤…ž£Tl®ctĽÚó{G­A‡ŒÇgeš~Αd¿æaSba¥KKûj®_ć^\\ؾbP®¦x^sxjĶI_Ä X‚⼕Hu¨Qh¡À@Ëô}Ž±žGNìĎlT¸ˆ…`V~R°tbÕĊ`¸úÛtπFDu€[ƒMfqGH·¥yA‰ztMFe|R‚_Gk†ChZeÚ°to˜v`x‹b„ŒDnÐ{E}šZ˜è€x—†NEފREn˜[Pv@{~rĆAB§‚EO¿|UZ~ì„Uf¨J²ĂÝƀ‚sª–B`„s¶œfvö¦ŠÕ~dÔq¨¸º»uù[[§´sb¤¢zþFœ¢Æ…Àhˆ™ÂˆW\\ıŽËI݊o±ĭŠ£þˆÊs}¡R]ŒěƒD‚g´VG¢‚j±®è†ºÃmpU[Á›‘Œëº°r›ÜbNu¸}Žº¼‡`ni”ºÔXĄ¤¼Ôdaµ€Á_À…†ftQQgœR—‘·Ǔ’v”}Ýלĵ]µœ“Wc¤F²›OĩųãW½¯K‚©…]€{†LóµCIµ±Mß¿hŸ•©āq¬o‚½ž~@i~TUxŪÒ¢@ƒ£ÀEîôruń‚”“‚b[§nWuMÆLl¿]x}ij­€½"]],"encodeOffsets":[[[112158,27383]],[[112105,27474]],[[112095,27476]]]}},{"type":"Feature","id":"530000","properties":{"id":"530000","cp":[101.512251,24.740609],"name":"云南","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@[„ùx½}ÑRH‘YīĺûsÍn‘iEoã½Ya²ė{c¬ĝg•ĂsA•ØÅwď‚õzFjw}—«Dx¿}UũlŸê™@•HÅ­F‰¨ÇoJ´Ónũuą¡Ã¢pÒŌ“Ø TF²‚xa²ËX€‚cʋlHîAßËŁkŻƑŷÉ©h™W­æßU‡“Ës¡¦}•teèƶStǀÇ}Fd£j‹ĈZĆÆ‹¤T‚č\\Dƒ}O÷š£Uˆ§~ŃG™‚åŃDĝ¸œTsd¶¶Bªš¤u¢ŌĎo~t¾ÍŶÒtD¦Ú„iôö‰€z›ØX²ghįh½Û±¯€ÿm·zR¦Ɵ`ªŊÃh¢rOԍ´£Ym¼èêf¯ŪĽn„†cÚbŒw\\zlvWžªâˆ ¦g–mĿBş£¢ƹřbĥkǫßeeZkÙIKueT»sVesb‘aĕ  ¶®dNœĄÄpªyŽ¼—„³BE˜®l‡ŽGœŭCœǶwêżĔÂe„pÍÀQƞpC„–¼ŲÈ­AÎô¶R„ä’Q^Øu¬°š_Èôc´¹ò¨P΢hlϦ´Ħ“Æ´sâDŽŲPnÊD^¯°’Upv†}®BP̪–jǬx–Söwlfòªv€qĸ|`H€­viļ€ndĜ­Ćhň•‚em·FyށqóžSį¯‘³X_ĞçêtryvL¤§z„¦c¦¥jnŞk˜ˆlD¤øz½ĜàžĂŧMÅ|áƆàÊcðÂF܎‚áŢ¥\\\\º™İøÒÐJĴ‡„îD¦zK²ǏÎEh~’CD­hMn^ÌöÄ©ČZÀžaü„fɭyœpį´ěFűk]Ôě¢qlÅĆÙa¶~Äqššê€ljN¬¼H„ÊšNQ´ê¼VظE††^ŃÒyŒƒM{ŒJLoÒœęæŸe±Ķ›y‰’‡gã“¯JYÆĭĘëo¥Š‰o¯hcK«z_pŠrC´ĢÖY”—¼ v¸¢RŽÅW³Â§fǸYi³xR´ďUˊ`êĿU„û€uĆBƒƣö‰N€DH«Ĉg†——Ñ‚aB{ÊNF´¬c·Åv}eÇÃGB»”If•¦HňĕM…~[iwjUÁKE•Ž‹¾dĪçW›šI‹èÀŒoÈXòyŞŮÈXâÎŚŠj|àsRy‹µÖ›–Pr´þŒ ¸^wþTDŔ–Hr¸‹žRÌmf‡żÕâCôox–ĜƌÆĮŒ›Ð–œY˜tâŦÔ@]ÈǮƒ\\Ī¼Ä£UsȯLbîƲŚºyh‡rŒŠ@ĒԝƀŸÀ²º\\êp“’JŠ}ĠvŠqt„Ġ@^xÀ£È†¨mËÏğ}n¹_¿¢×Y_æpˆÅ–A^{½•Lu¨GO±Õ½ßM¶w’ÁĢۂP‚›Ƣ¼pcIJxŠ|ap̬HšÐŒŊSfsðBZ¿©“XÏÒK•k†÷Eû¿‰S…rEFsÕūk”óVǥʼniTL‚¡n{‹uxţÏh™ôŝ¬ğōN“‘NJkyPaq™Âğ¤K®‡YŸxÉƋÁ]āęDqçgOg†ILu—\\_gz—]W¼ž~CÔē]bµogpў_oď`´³Țkl`IªºÎȄqÔþž»E³ĎSJ»œ_f·‚adÇqƒÇc¥Á_Źw{™L^ɱćx“U£µ÷xgĉp»ĆqNē`rĘzaĵĚ¡K½ÊBzyäKXqiWPÏɸ½řÍcÊG|µƕƣG˛÷Ÿk°_^ý|_zċBZocmø¯hhcæ\\lˆMFlư£Ĝ„ÆyH“„F¨‰µêÕ]—›HA…àӄ^it `þßäkŠĤÎT~Wlÿ¨„ÔPzUC–NVv [jâôDôď[}ž‰z¿–msSh‹¯{jïğl}šĹ[–őŒ‰gK‹©U·µË@¾ƒm_~q¡f¹…ÅË^»‘f³ø}Q•„¡Ö˳gͱ^ǁ…\\ëÃA_—¿bW›Ï[¶ƛ鏝£F{īZgm@|kHǭƁć¦UĔťƒ×ë}ǝƒeďºȡȘÏíBə£āĘPªij¶“ʼnÿ‡y©n‰ď£G¹¡I›Š±LÉĺÑdĉ܇W¥˜‰}g˜Á†{aqÃ¥aŠıęÏZ—ï`"],"encodeOffsets":[[104636,22969]]}},{"type":"Feature","id":"540000","properties":{"id":"540000","cp":[89.132212,30.860361],"name":"西藏","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@hžľxŽŖ‰xƒÒVŽ†ºÅâAĪÝȆµę¯Ňa±r_w~uSÕň‘qOj]ɄQ…£Z……UDûoY’»©M[‹L¼qãË{V͕çWViŽ]ë©Ä÷àyƛh›ÚU°ŒŒa”d„cQƒ~Mx¥™cc¡ÙaSyF—ցk­ŒuRýq¿Ôµ•QĽ³aG{¿FµëªéĜÿª@¬·–K‰·àariĕĀ«V»Ŷ™Ĵū˜gèLǴŇƶaf‹tŒèBŚ£^Šâ†ǐÝ®–šM¦ÁǞÿ¬LhŸŽJ¾óƾƺcxw‹f]Y…´ƒ¦|œQLn°aœdĊ…œ\\¨o’œǀÍŎœ´ĩĀd`tÊQŞŕ|‚¨C^©œĈ¦„¦ÎJĊ{ŽëĎjª²rЉšl`¼Ą[t|¦St辉PŒÜK¸€d˜Ƅı]s¤—î_v¹ÎVòŦj˜£Əsc—¬_Ğ´|Ł˜¦AvŽ¦w`ăaÝaa­¢e¤ı²©ªSªšÈMĄwžÉØŔì@T‘¤—Ę™\\õª@”þo´­xA s”ÂtŎKzó´ÇĊµ¢rž^nĊ­Æ¬×üGž¢‚³ {âĊ]š™G‚~bÀgVjzlhǶf€žOšfdŠ‰ªB]pj„•TO–tĊ‚n¤}®¦ƒČ¥d¢¼»ddš”Y¼Žt—¢eȤJ¤}Ǿ¡°§¤AГlc@ĝ”sªćļđAç‡wx•UuzEÖġ~AN¹ÄÅȀŻ¦¿ģŁéì±H…ãd«g[؉¼ēÀ•cīľġ¬cJ‘µ…ÐʥVȝ¸ßS¹†ý±ğkƁ¼ą^ɛ¤Ûÿ‰b[}¬ōõÃ]ËNm®g@•Bg}ÍF±ǐyL¥íCˆƒIij€Ï÷њį[¹¦[⚍EÛïÁÉdƅß{âNÆāŨߝ¾ě÷yC£‡k­´ÓH@¹†TZ¥¢įƒ·ÌAЧ®—Zc…v½ŸZ­¹|ŕWZqgW“|ieZÅYVӁqdq•bc²R@†c‡¥Rã»Ge†ŸeƃīQ•}J[ғK…¬Ə|o’ėjġĠÑN¡ð¯EBčnwôɍėªƒ²•CλŹġǝʅįĭạ̃ūȹ]ΓͧgšsgȽóϧµǛ†ęgſ¶ҍć`ĘąŌJޚä¤rÅň¥ÖÁUětęuůÞiĊÄÀ\\Æs¦ÓRb|Â^řÌkÄŷ¶½÷‡f±iMݑ›‰@ĥ°G¬ÃM¥n£Øą‚ğ¯ß”§aëbéüÑOčœk£{\\‘eµª×M‘šÉfm«Ƒ{Å׃Gŏǩãy³©WÑăû‚··‘Q—òı}¯ã‰I•éÕÂZ¨īès¶ZÈsŽæĔTŘvŽgÌsN@îá¾ó@‰˜ÙwU±ÉT廣TđŸWxq¹Zo‘b‹s[׌¯cĩv‡Œėŧ³BM|¹k‰ªħ—¥TzNYnݍßpęrñĠĉRS~½ŠěVVŠµ‚õ‡«ŒM££µB•ĉ¥áºae~³AuĐh`Ü³ç@BۘïĿa©|z²Ý¼D”£àč²‹ŸƒIƒû›I ā€óK¥}rÝ_Á´éMaň¨€~ªSĈ½Ž½KÙóĿeƃÆBŽ·¬ën×W|Uº}LJrƳ˜lŒµ`bÔ`QˆˆÐÓ@s¬ñIŒÍ@ûws¡åQÑßÁ`ŋĴ{Ī“T•ÚÅTSij‚‹Yo|Ç[ǾµMW¢ĭiÕØ¿@˜šMh…pÕ]j†éò¿OƇĆƇp€êĉâlØw–ěsˆǩ‚ĵ¸c…bU¹ř¨WavquSMzeo_^gsÏ·¥Ó@~¯¿RiīB™Š\\”qTGªÇĜçPoŠÿfñòą¦óQīÈáP•œābß{ƒZŗĸIæńhnszÁCËìñšÏ·ąĚÝUm®ó­L·ăU›Èíoù´Êj°ŁŤ_uµ^‘°Œìǖ@tĶĒ¡Æ‡M³Ģ«˜İĨÅ®ğ†RŽāð“ggheÆ¢z‚Ê©Ô\\°ÝĎz~ź¤Pn–MĪÖB£Ÿk™n鄧żćŠ˜ĆK„Ē°¼L¶è‰âz¨u¦¥LDĘz¬ýÎmĘd¾ß”Fz“hg²™Fy¦ĝ¤ċņbΛ@y‚Ąæm°NĮZRÖíŽJ²öLĸÒ¨Y®ƌÐV‰à˜tt_ڀÂyĠzž]Ţh€zĎ{†ĢX”ˆc|šÐqŽšfO¢¤ög‚ÌHNŽ„PKŖœŽ˜Uú´xx[xˆvĐCûĀŠìÖT¬¸^}Ìsòd´_Ž‡KgžLĴ…ÀBon|H@–Êx˜—¦BpŰˆŌ¿fµƌA¾zLjRxŠ¶F”œkĄźRzŀˆ~¶[”´Hnª–VƞuĒ­È¨ƎcƽÌm¸ÁÈM¦x͊ëÀxdžB’šú^´W†£–d„kɾĬpœw‚˂ØɦļĬIŚœÊ•n›Ŕa¸™~J°î”lɌxĤÊÈðhÌ®‚g˜T´øŽàCˆŽÀ^ªerrƘdž¢İP|Ė ŸWœªĦ^¶´ÂL„aT±üWƜ˜ǀRšŶUńšĖ[QhlLüA†‹Ü\\†qR›Ą©"],"encodeOffsets":[[90849,37210]]}},{"type":"Feature","id":"610000","properties":{"id":"610000","cp":[108.948024,34.263161],"name":"陕西","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@˜p¢—ȮµšûG™Ħ}Ħšðǚ¶òƄ€jɂz°{ºØkÈęâ¦jª‚Bg‚\\œċ°s¬Ž’]jžú ‚E”Ȍdž¬s„t‡”RˆÆdĠݎwܔ¸ôW¾ƮłÒ_{’Ìšû¼„jº¹¢GǪÒ¯ĘƒZ`ºŊƒecņąš~BÂgzpâēòYǠȰÌTΨÂWœ|fcŸă§uF—Œ@NŸ¢XLƒŠRMº[ğȣſï|¥J™kc`sʼnǷ’Y¹‹W@µ÷K…ãï³ÛIcñ·VȋڍÒķø©—þ¥ƒy‚ÓŸğęmWµÎumZyOŅƟĥÓ~sÑL¤µaŅY¦ocyZ{‰y c]{ŒTa©ƒ`U_Ěē£ωÊƍKù’K¶ȱÝƷ§{û»ÅÁȹÍéuij|¹cÑd‘ŠìUYƒŽO‘uF–ÕÈYvÁCqӃT•Ǣí§·S¹NgŠV¬ë÷Át‡°Dد’C´ʼnƒópģ}„ċcE˅FŸŸéGU¥×K…§­¶³B‹Č}C¿åċ`wġB·¤őcƭ²ő[Å^axwQO…ÿEËߌ•ĤNĔŸwƇˆÄŠńwĪ­Šo[„_KÓª³“ÙnK‰Çƒěœÿ]ď€ă_d©·©Ýŏ°Ù®g]±„Ÿ‡ß˜å›—¬÷m\\›iaǑkěX{¢|ZKlçhLt€Ňîŵ€œè[€É@ƉĄEœ‡tƇÏ˜³­ħZ«mJ…›×¾‘MtÝĦ£IwÄå\\Õ{‡˜ƒOwĬ©LÙ³ÙgBƕŀr̛ĢŭO¥lãyC§HÍ£ßEñŸX¡—­°ÙCgpťz‘ˆb`wI„vA|§”‡—hoĕ@E±“iYd¥OĻ¹S|}F@¾oAO²{tfžÜ—¢Fǂ҈W²°BĤh^Wx{@„¬‚­F¸¡„ķn£P|ŸªĴ@^ĠĈæb–Ôc¶l˜Yi…–^Mi˜cĎ°Â[ä€vï¶gv@À“Ĭ·lJ¸sn|¼u~a]’ÆÈtŌºJp’ƒþ£KKf~Š¦UbyäIšĺãn‡Ô¿^­žŵMT–hĠܤko¼Ŏìąǜh`[tŒRd²IJ_œXPrɲ‰l‘‚XžiL§àƒ–¹ŽH˜°Ȧqº®QC—bA†„ŌJ¸ĕÚ³ĺ§ `d¨YjžiZvRĺ±öVKkjGȊĐePОZmļKÀ€‚[ŠŽ`ösìh†ïÎoĬdtKÞ{¬èÒÒBŒÔpIJÇĬJŊ¦±J«ˆY§‹@·pH€µàåVKe›pW†ftsAÅqC·¬ko«pHÆuK@oŸHĆۄķhx“e‘n›S³àǍrqƶRbzy€¸ËАl›¼EºpĤ¼Œx¼½~Ğ’”à@†ÚüdK^ˆmÌSj"],"encodeOffsets":[[110234,38774]]}},{"type":"Feature","id":"620000","properties":{"id":"620000","cp":[103.823557,36.058039],"name":"甘肃","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@VuUv"],["@@ũ‹EĠtt~nkh`Q‰¦ÅÄÜdw˜Ab×ĠąJˆ¤DüègĺqBqœj°lI¡ĨÒ¤úSHbš‡ŠjΑBŠ°aZˆ¢KJŽ’O[|A£žDx}Nì•HUnrk„ kp€¼Y kMJn[aG‚áÚÏ[½rc†}aQxOgsPMnUs‡nc‹Z…ž–sKúvA›t„Þġ’£®ĀYKdnFwš¢JE°”Latf`¼h¬we|€Æ‡šbj}GA€·~WŽ”—`†¢MC¤tL©IJ°qdf”O‚“bÞĬ¹ttu`^ZúE`Œ[@„Æsîz®¡’C„ƳƜG²“R‘¢R’m”fŽwĸg܃‚ą G@pzJM½mŠhVy¸uÈÔO±¨{LfæU¶ßGĂq\\ª¬‡²I‚¥IʼnÈīoı‹ÓÑAçÑ|«LÝcspīðÍg…të_õ‰\\ĉñLYnĝg’ŸRǡÁiHLlõUĹ²uQjYi§Z_c¨Ÿ´ĹĖÙ·ŋI…ƒaBD˜­R¹ȥr—¯G•ºß„K¨jWk’ɱŠOq›Wij\\a­‹Q\\sg_ĆǛōëp»£lğۀgS•ŶN®À]ˆÓäm™ĹãJaz¥V}‰Le¤L„ýo‘¹IsŋÅÇ^‘Žbz…³tmEÁ´aŠ¹cčecÇN•ĊãÁ\\č¯—dNj•]j†—ZµkÓda•ćå]ğij@ ©O{¤ĸm¢ƒE·®ƒ«|@Xwg]Aģ±¯‡XǁÑdzªc›wQÚŝñsÕ³ÛV_ýƒ˜¥\\ů¥©¾÷w—Ž©WÕÊĩhÿÖÁRo¸V¬âDb¨šhûx–Ê×nj~Zâƒg|šXÁnßYoº§ZÅŘvŒ[„ĭÖʃuďxcVbnUSf…B¯³_Tzº—ΕO©çMÑ~Mˆ³]µ^püµ”ŠÄY~y@X~¤Z³€[Èōl@®Å¼£QKƒ·Di‹¡By‘ÿ‰Q_´D¥hŗyƒ^ŸĭÁZ]cIzý‰ah¹MĪğP‘s{ò‡‹‘²Vw¹t³Ŝˁ[ŽÑ}X\\gsFŸ£sPAgěp×ëfYHāďÖqēŭOÏë“dLü•\\iŒ”t^c®šRʺ¶—¢H°mˆ‘rYŸ£BŸ¹čIoľu¶uI]vģSQ{ƒUŻ”Å}QÂ|̋°ƅ¤ĩŪU ęĄžÌZҞ\\v˜²PĔ»ƢNHƒĂyAmƂwVmž`”]ȏb•”H`‰Ì¢²ILvĜ—H®¤Dlt_„¢JJÄämèÔDëþgºƫ™”aʎÌrêYi~ ÎݤNpÀA¾Ĕ¼b…ð÷’Žˆ‡®‚”üs”zMzÖĖQdȨý†v§Tè|ªH’þa¸|šÐ ƒwKĢx¦ivr^ÿ ¸l öæfƟĴ·PJv}n\\h¹¶v†·À|\\ƁĚN´Ĝ€çèÁz]ġ¤²¨QÒŨTIl‡ªťØ}¼˗ƦvÄùØE‹’«Fï˛Iq”ōŒTvāÜŏ‚íÛߜÛV—j³âwGăÂíNOŠˆŠPìyV³ʼnĖýZso§HіiYw[߆\\X¦¥c]ÔƩÜ·«j‡ÐqvÁ¦m^ċ±R™¦΋ƈťĚgÀ»IïĨʗƮŽ°Ɲ˜ĻþÍAƉſ±tÍEÕÞāNU͗¡\\ſčåÒʻĘm ƭÌŹöʥ’ëQ¤µ­ÇcƕªoIýˆ‰Iɐ_mkl³ă‰Ɠ¦j—¡Yz•Ňi–}Msßõ–īʋ —}ƒÁVmŸ_[n}eı­Uĥ¼‘ª•I{ΧDӜƻėoj‘qYhĹT©oūĶ£]ďxĩ‹ǑMĝ‰q`B´ƃ˺Ч—ç~™²ņj@”¥@đ´ί}ĥtPńǾV¬ufӃÉC‹tÓ̻‰…¹£G³€]ƖƾŎĪŪĘ̖¨ʈĢƂlɘ۪üºňUðǜȢƢż̌ȦǼ‚ĤŊɲĖ­Kq´ï¦—ºĒDzņɾªǀÞĈĂD†½ĄĎÌŗĞrôñnŽœN¼â¾ʄľԆ|DŽŽ֦ज़ȗlj̘̭ɺƅêgV̍ʆĠ·ÌĊv|ýĖÕWĊǎÞ´õ¼cÒÒBĢ͢UĜð͒s¨ňƃLĉÕÝ@ɛƯ÷¿Ľ­ĹeȏijëCȚDŲyê×Ŗyò¯ļcÂßY…tÁƤyAã˾J@ǝrý‹‰@¤…rz¸oP¹ɐÚyᐇHŸĀ[Jw…cVeȴϜ»ÈŽĖ}ƒŰŐèȭǢόĀƪÈŶë;Ñ̆ȤМľĮEŔ—ĹŊũ~ËUă{ŸĻƹɁύȩþĽvĽƓÉ@ē„ĽɲßǐƫʾǗĒpäWÐxnsÀ^ƆwW©¦cÅ¡Ji§vúF¶Ž¨c~c¼īŒeXǚ‹\\đ¾JŽwÀďksãA‹fÕ¦L}wa‚o”Z’‹D½†Ml«]eÒÅaɲáo½FõÛ]ĻÒ¡wYR£¢rvÓ®y®LF‹LzĈ„ôe]gx}•|KK}xklL]c¦£fRtív¦†PĤoH{tK"]],"encodeOffsets":[[[108619,36299]],[[108589,36341]]]}},{"type":"Feature","id":"630000","properties":{"id":"630000","cp":[96.778916,35.623178],"name":"青海","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@InJm"],["@@CƒÆ½OŃĦsΰ~Ē³¦@@“Ņiš±è}ؘƄ˹A³r_ĞŠǒNĪŒĐw¤^ŬĵªpĺSZg’rpiƼĘԛ¨C|͖J’©Ħ»®VIJ~f\\m `Un„˜~ʌŸ•ĬàöNt•~ňjy–¢Zi˜Ɣ¥ĄŠk´nl`JʇŠJþ©pdƖ®È£¶ìRʦ‘źõƮËnŸʼėæÑƀĎ[‚˜¢VÎĂMÖÝÎF²sƊƀÎBļýƞ—¯ʘƭðħ¼Jh¿ŦęΌƇš¥²Q]Č¥nuÂÏriˆ¸¬ƪÛ^Ó¦d€¥[Wà…x\\ZŽjҕ¨GtpþYŊĕ´€zUO뇉P‰îMĄÁxH´á˜iÜUà›îÜՁĂÛSuŎ‹r“œJð̬EŒ‘FÁú×uÃÎkr“Ē{V}İ«O_ÌËĬ©ŽÓŧSRѱ§Ģ£^ÂyèçěM³Ƃę{[¸¿u…ºµ[gt£¸OƤĿéYŸõ·kĀŸq]juw¥Dĩƍ€õÇPéĽG‘ž©ã‡¤G…uȧþRcÕĕNy“yût“ˆ­‡ø‘†ï»a½ē¿BMoį£ŸÍj}éZËqbʍš“Ƭh¹ìÿÓAçãnIáI`ƒks£CG­ě˜Uy×Cy•…’Ÿ@¶ʡÊBnāzG„ơMē¼±O÷õJËĚăVŸĪũƆ£Œ¯{ËL½Ìzż“„VR|ĠTbuvJvµhĻĖH”Aëáa…­OÇðñęNw‡…œľ·L›mI±íĠĩPÉ×®ÿs—’cB³±JKßĊ«`…ađ»·QAmO’‘Vţéÿ¤¹SQt]]Çx€±¯A@ĉij¢Óļ©•ƒl¶ÅÛr—ŕspãRk~¦ª]Į­´“FR„åd­ČsCqđéFn¿Åƃm’Éx{W©ºƝºįkÕƂƑ¸wWūЩÈFž£\\tÈ¥ÄRÈýÌJ ƒlGr^×äùyÞ³fj”c†€¨£ÂZ|ǓMĝšÏ@ëÜőR‹›ĝ‰Œ÷¡{aïȷPu°ËXÙ{©TmĠ}Y³’­ÞIňµç½©C¡į÷¯B»|St»›]vƒųƒs»”}MÓ ÿʪƟǭA¡fs˜»PY¼c¡»¦c„ċ­¥£~msĉP•–Siƒ^o©A‰Šec‚™PeǵŽkg‚yUi¿h}aH™šĉ^|ᴟ¡HØûÅ«ĉ®]m€¡qĉ¶³ÈyôōLÁst“BŸ®wn±ă¥HSòėš£˜S’ë@לÊăxÇN©™©T±ª£IJ¡fb®ÞbŽb_Ą¥xu¥B—ž{łĝ³«`d˜Ɛt—¤ťiñžÍUuºí`£˜^tƃIJc—·ÛLO‹½Šsç¥Ts{ă\\_»™kϊ±q©čiìĉ|ÍIƒ¥ć¥›€]ª§D{ŝŖÉR_sÿc³Īō›ƿΑ›§p›[ĉ†›c¯bKm›R¥{³„Z†e^ŽŒwx¹dƽŽôIg §Mĕ ƹĴ¿—ǣÜ̓]‹Ý–]snåA{‹eŒƭ`ǻŊĿ\\ijŬű”YÂÿ¬jĖqŽßbŠ¸•L«¸©@ěĀ©ê¶ìÀEH|´bRľž–Ó¶rÀQþ‹vl®Õ‚E˜TzÜdb ˜hw¤{LR„ƒd“c‹b¯‹ÙVgœ‚ƜßzÃô쮍^jUèXΖ|UäÌ»rKŽ\\ŒªN‘¼pZCü†VY††¤ɃRi^rPҒTÖ}|br°qňbĚ°ªiƶGQ¾²„x¦PœmlŜ‘[Ĥ¡ΞsĦŸÔÏâ\\ªÚŒU\\f…¢N²§x|¤§„xĔsZPòʛ²SÐqF`ª„VƒÞŜĶƨVZŒÌL`ˆ¢dŐIqr\\oäõ–F礻Ŷ×h¹]Clـ\\¦ďÌį¬řtTӺƙgQÇÓHţĒ”´ÃbEÄlbʔC”|CˆŮˆk„Ʈ[ʼ¬ňœ´KŮÈΰÌĪ¶ƶlð”ļA†TUvdTŠG†º̼ŠÔ€ŒsÊDԄveOg"]],"encodeOffsets":[[[105308,37219]],[[95370,40081]]]}},{"type":"Feature","id":"640000","properties":{"id":"640000","cp":[106.278179,37.26637],"name":"宁夏","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@KëÀęĞ«OęȿȕŸı]ʼn¡åįÕÔ«Ǵõƪ™ĚQÐZhv K°›öqÀѐS[ÃÖHƖčË‡nL]ûc…Ùß@‚“ĝ‘¾}w»»‹oģF¹œ»kÌÏ·{zPƒ§B­¢íyÅt@ƒ@áš]Yv_ssģ¼i߁”ĻL¾ġsKD£¡N_…“˜X¸}B~Haiˆ™Åf{«x»ge_bs“KF¯¡Ix™mELcÿZ¤­Ģ‘ƒÝœsuBLù•t†ŒYdˆmVtNmtOPhRw~bd…¾qÐ\\âÙH\\bImlNZŸ»loƒŸqlVm–Gā§~QCw¤™{A\\‘PKŸNY‡¯bF‡kC¥’sk‹Šs_Ã\\ă«¢ħkJi¯r›rAhĹûç£CU‡ĕĊ_ԗBixÅُĄnªÑaM~ħpOu¥sîeQ¥¤^dkKwlL~{L~–hw^‚ófćƒKyEŒ­K­zuÔ¡qQ¤xZÑ¢^ļöܾEpž±âbÊÑÆ^fk¬…NC¾‘Œ“YpxbK~¥Že֎ŒäBlt¿Đx½I[ĒǙŒWž‹f»Ĭ}d§dµùEuj¨‚IÆ¢¥dXªƅx¿]mtÏwßRĶŒX¢͎vÆzƂZò®ǢÌʆCrâºMÞzžÆMҔÊÓŊZľ–r°Î®Ȉmª²ĈUªĚøºˆĮ¦ÌĘk„^FłĬhĚiĀĖ¾iİbjÕ"],["@@mfwěwMrŢªv@G‰"]],"encodeOffsets":[[[109366,40242]],[[108600,36303]]]}},{"type":"Feature","id":"650000","properties":{"id":"650000","cp":[85.617733,40.792818],"name":"新疆","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@QØĔ²X¨”~ǘBºjʐßØvK”ƔX¨vĊOžÃƒ·¢i@~c—‡ĝe_«”Eš“}QxgɪëÏÃ@sÅyXoŖ{ô«ŸuX…ê•Îf`œC‚¹ÂÿÐGĮÕĞXŪōŸMźÈƺQèĽôe|¿ƸJR¤ĘEjcUóº¯Ĩ_ŘÁMª÷Ð¥Oéȇ¿ÖğǤǷÂF҇zÉx[]­Ĥĝ‰œ¦EP}ûƥé¿İƷTėƫœŕƅ™ƱB»Đ±’ēO…¦E–•}‘`cȺrĦáŖuҞª«IJ‡πdƺÏØZƴwʄ¤ĖGЙǂZĶƒèH¶}ÚZצʥĪï|ÇĦMŔ»İĝLj‹ì¥Βœba­¯¥ǕǚkĆŵĦɑĺƯxūД̵nơʃĽá½M»›òmqóŘĝč˾ăC…ćāƿÝɽ©DZŅ¹đ¥˜³ðLrÁ®ɱĕģʼnǻ̋ȥơŻǛȡVï¹Ň۩ûkɗġƁ§ʇė̕ĩũƽō^ƕŠUv£ƁQï“Ƶkŏ½ΉÃŭdzLқʻ«ƭ\\lƒ‡ŭD‡“{ʓDkaFÃÄa“³ŤđÔGRÈƚhSӹŚsİ«ĐË[¥ÚDkº^Øg¼ŵ¸£EÍö•€ůʼnT¡c_‡ËKY‹ƧUśĵ„݃U_©rETÏʜ±OñtYwē¨ƒ{£¨uM³x½şL©Ùá[ÓÐĥ Νtģ¢\\‚ś’nkO›w¥±ƒT»ƷFɯàĩÞáB¹Æ…ÑUw„੍žĽw[“mG½Èå~‡Æ÷QyŠěCFmĭZī—ŵVÁ™ƿQƛ—ûXS²‰b½KϽĉS›©ŷXĕŸ{ŽĕK·¥Ɨcqq©f¿]‡ßDõU³h—­gËÇïģÉɋw“k¯í}I·šœbmœÉ–ř›īJɥĻˁ×xo›ɹī‡l•c…¤³Xù]‘™DžA¿w͉ì¥wÇN·ÂËnƾƍdǧđ®Ɲv•Um©³G\\“}µĿ‡QyŹl㓛µEw‰LJQ½yƋBe¶ŋÀů‡ož¥A—˜Éw@•{Gpm¿Aij†ŽKLhˆ³`ñcËtW‚±»ÕS‰ëüÿďD‡u\\wwwù³—V›LŕƒOMËGh£õP¡™er™Ïd{“‡ġWÁ…č|yšg^ğyÁzÙs`—s|ÉåªÇ}m¢Ń¨`x¥’ù^•}ƒÌ¥H«‰Yªƅ”Aйn~ź¯šf¤áÀz„gŠÇDIԝ´AňĀ҄¶ûEYospõD[{ù°]u›Jq•U•|Soċxţ[õÔĥkŋÞŭZ˺óYËüċrw €ÞkrťË¿XGÉbřaDü·Ē÷Aê[Ää€I®BÕИÞ_¢āĠpŠÛÄȉĖġDKwbm‡ÄNô‡ŠfœƫVÉvi†dz—H‘‹QµâFšù­Âœ³¦{YGžƒd¢ĚÜO „€{Ö¦ÞÍÀPŒ^b–ƾŠlŽ[„vt×ĈÍE˨¡Đ~´î¸ùÎh€uè`¸ŸHÕŔVºwĠââWò‡@{œÙNÝ´ə²ȕn{¿¥{l—÷eé^e’ďˆXj©î\\ªÑò˜Üìc\\üqˆÕ[Č¡xoÂċªbØ­Œø|€¶ȴZdÆšońéŒGš\\”¼C°ÌƁn´nxšÊOĨ’Ūƴĸ¢¸òTxÊǪMīИÖŲÃɎOvˆʦƢ~FŽ‡Rěò—¿ġ~åŊœú‰Nšžš¸qŽ’Ę[Ĕ¶ÂćnÒPĒÜvúĀÊbÖ{Äî¸~Ŕünp¤ÂH¾œĄYÒ©ÊfºmԈĘcDoĬMŬ’˜S¤„s²‚”ʘچžȂVŦ –ŽèW°ªB|IJXŔþÈJĦÆæFĚêŠYĂªĂ]øªŖNÞüA€’fɨJ€˜¯ÎrDDšĤ€`€mz\\„§~D¬{vJÂ˜«lµĂb–¤p€ŌŰNĄ¨ĊXW|ų ¿¾ɄĦƐMT”‡òP˜÷fØĶK¢ȝ˔Sô¹òEð­”`Ɩ½ǒÂň×äı–§ĤƝ§C~¡‚hlå‚ǺŦŞkâ’~}ŽFøàIJaĞ‚fƠ¥Ž„Ŕdž˜®U¸ˆźXœv¢aƆúŪtŠųƠjd•ƺŠƺÅìnrh\\ĺ¯äɝĦ]èpĄ¦´LƞĬŠ´ƤǬ˼Ēɸ¤rºǼ²¨zÌPðŀbþ¹ļD¢¹œ\\ĜÑŚŸ¶ZƄ³àjĨoâŠȴLʉȮŒĐ­ĚăŽÀêZǚŐ¤qȂ\\L¢ŌİfÆs|zºeªÙæ§΢{Ā´ƐÚ¬¨Ĵà²łhʺKÞºÖTŠiƢ¾ªì°`öøu®Ê¾ãØ"],"encodeOffsets":[[88824,50096]]}},{"type":"Feature","id":"110000","properties":{"id":"110000","cp":[116.405285,39.904989],"name":"北京","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@ĽOÁ›ûtŷmiÍt_H»Ĩ±d`Š¹­{bw…Yr“³S]§§o¹€qGtm_Sŧ€“oa›‹FLg‘QN_•dV€@Zom_ć\\ߚc±x¯oœRcfe…£’o§ËgToÛJíĔóu…|wP¤™XnO¢ÉˆŦ¯rNÄā¤zâŖÈRpŢZŠœÚ{GŠrFt¦Òx§ø¹RóäV¤XdˆżâºWbwŚ¨Ud®bêņ¾‘jnŎGŃŶŠnzÚSeîĜZczî¾i]͜™QaúÍÔiþĩȨWĢ‹ü|Ėu[qb[swP@ÅğP¿{\\‡¥A¨Ï‘Ѩj¯ŠX\\¯œMK‘pA³[H…īu}}"],"encodeOffsets":[[120023,41045]]}},{"type":"Feature","id":"120000","properties":{"id":"120000","cp":[117.190182,39.125596],"name":"天津","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@ŬgX§Ü«E…¶Ḟ“¬O_™ïlÁg“z±AXe™µÄĵ{¶]gitgšIj·›¥îakS€‰¨ÐƎk}ĕ{gB—qGf{¿a†U^fI“ư‹³õ{YƒıëNĿžk©ïËZŏ‘R§òoY×Ógc…ĥs¡bġ«@dekąI[nlPqCnp{ˆō³°`{PNdƗqSÄĻNNâyj]äžÒD ĬH°Æ]~¡HO¾ŒX}ÐxŒgp“gWˆrDGˆŒpù‚Š^L‚ˆrzWxˆZ^¨´T\\|~@I‰zƒ–bĤ‹œjeĊªz£®Ĕvě€L†mV¾Ô_ȔNW~zbĬvG†²ZmDM~”~"],"encodeOffsets":[[120237,41215]]}},{"type":"Feature","id":"310000","properties":{"id":"310000","cp":[121.472644,31.231706],"name":"上海","childNum":6},"geometry":{"type":"MultiPolygon","coordinates":[["@@ɧư¬EpƸÁxc‡"],["@@©„ªƒ"],["@@”MA‹‘š"],["@@Qp݁E§ÉC¾"],["@@bŝՕÕEȣÚƥêImɇǦèÜĠŒÚžÃƌÃ͎ó"],["@@ǜûȬɋŠŭ™×^‰sYŒɍDŋ‘ŽąñCG²«ªč@h–_p¯A{‡oloY€¬j@IJ`•gQڛhr|ǀ^MIJvtbe´R¯Ô¬¨YŽô¤r]ì†Ƭį"]],"encodeOffsets":[[[124702,32062]],[[124547,32200]],[[124808,31991]],[[124726,32110]],[[124903,32376]],[[124438,32149]]]}},{"type":"Feature","id":"500000","properties":{"id":"500000","cp":[107.304962,29.533155],"name":"重庆","childNum":2},"geometry":{"type":"MultiPolygon","coordinates":[["@@vjG~nGŘŬĶȂƀƾ¹¸ØÎezĆT¸}êЖqHŸðqĖ䒊¥^CƒIj–²p…\\_ æüY|[YxƊæuž°xb®…Űb@~¢NQt°¶‚S栓Ê~rljĔëĚ¢~šuf`‘‚†fa‚ĔJåĊ„nÖ]„jƎćÊ@Š£¾a®£Ű{ŶĕF‹ègLk{Y|¡ĜWƔtƬJÑxq‹±ĢN´‰òK‰™–LÈüD|s`ŋ’ć]ƒÃ‰`đŒMûƱ½~Y°ħ`ƏíW‰½eI‹½{aŸ‘OIrÏ¡ĕŇa†p†µÜƅġ‘œ^ÖÛbÙŽŏml½S‹êqDu[R‹ãË»†ÿw`»y‘¸_ĺę}÷`M¯ċfCVµqʼn÷Z•gg“Œ`d½pDO‡ÎCnœ^uf²ènh¼WtƏxRGg¦…pV„†FI±ŽG^ŒIc´ec‡’G•ĹÞ½sëĬ„h˜xW‚}Kӈe­Xsbk”F¦›L‘ØgTkïƵNï¶}Gy“w\\oñ¡nmĈzjŸ•@™Óc£»Wă¹Ój“_m»ˆ¹·~MvÛaqœ»­‰êœ’\\ÂoVnŽÓØ͙²«‹bq¿efE „€‹Ĝ^Qž~ Évý‡ş¤²Į‰pEİ}zcĺƒL‹½‡š¿gņ›¡ýE¡ya£³t\\¨\\vú»¼§·Ñr_oÒý¥u‚•_n»_ƒ•At©Þűā§IVeëƒY}{VPÀFA¨ąB}q@|Ou—\\Fm‰QF݅Mw˜å}]•€|FmϋCaƒwŒu_p—¯sfÙgY…DHl`{QEfNysBŠ¦zG¸rHe‚„N\\CvEsÐùÜ_·ÖĉsaQ¯€}_U‡†xÃđŠq›NH¬•Äd^ÝŰR¬ã°wećJEž·vÝ·Hgƒ‚éFXjÉê`|yŒpxkAwœWĐpb¥eOsmzwqChóUQl¥F^laf‹anòsr›EvfQdÁUVf—ÎvÜ^efˆtET¬ôA\\œ¢sJŽnQTjP؈xøK|nBz‰„œĞ»LY‚…FDxӄvr“[ehľš•vN”¢o¾NiÂxGp⬐z›bfZo~hGi’]öF|‰|Nb‡tOMn eA±ŠtPT‡LjpYQ|†SH††YĀxinzDJ€Ìg¢và¥Pg‰_–ÇzII‹€II•„£®S¬„Øs쐣ŒN"],["@@ifjN@s"]],"encodeOffsets":[[[109628,30765]],[[111725,31320]]]}},{"type":"Feature","id":"810000","properties":{"id":"810000","cp":[114.173355,22.320048],"name":"香港","childNum":5},"geometry":{"type":"MultiPolygon","coordinates":[["@@AlBk"],["@@mŽn"],["@@EpFo"],["@@ea¢pl¸Eõ¹‡hj[ƒ]ÔCΖ@lj˜¡uBXŸ…•´‹AI¹…[‹yDUˆ]W`çwZkmc–…M›žp€Åv›}I‹oJlcaƒfёKŽ°ä¬XJmРđhI®æÔtSHn€Eˆ„ÒrÈc"],["@@rMUw‡AS®€e"]],"encodeOffsets":[[[117111,23002]],[[117072,22876]],[[117045,22887]],[[116975,23082]],[[116882,22747]]]}},{"type":"Feature","id":"820000","properties":{"id":"820000","cp":[113.54909,22.198951],"name":"澳门","childNum":1},"geometry":{"type":"Polygon","coordinates":["@@kÊd°å§s"],"encodeOffsets":[[116279,22639]]}}],"UTF8Encoding":true} \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/403.svg b/yunxi-ui-admin-vue3/src/assets/svgs/403.svg new file mode 100644 index 00000000..45005961 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/403.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/404.svg b/yunxi-ui-admin-vue3/src/assets/svgs/404.svg new file mode 100644 index 00000000..5244d8d4 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/500.svg b/yunxi-ui-admin-vue3/src/assets/svgs/500.svg new file mode 100644 index 00000000..9c020927 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/500.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/icon.svg b/yunxi-ui-admin-vue3/src/assets/svgs/icon.svg new file mode 100644 index 00000000..7024becf --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/login-bg.svg b/yunxi-ui-admin-vue3/src/assets/svgs/login-bg.svg new file mode 100644 index 00000000..bbe06c16 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/login-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/login-box-bg.svg b/yunxi-ui-admin-vue3/src/assets/svgs/login-box-bg.svg new file mode 100644 index 00000000..ab100403 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/login-box-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/member_balance.svg b/yunxi-ui-admin-vue3/src/assets/svgs/member_balance.svg new file mode 100644 index 00000000..5395b236 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/member_balance.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/member_expenditure_balance.svg b/yunxi-ui-admin-vue3/src/assets/svgs/member_expenditure_balance.svg new file mode 100644 index 00000000..02d498cd --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/member_expenditure_balance.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/member_level.svg b/yunxi-ui-admin-vue3/src/assets/svgs/member_level.svg new file mode 100644 index 00000000..cbcc686d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/member_level.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/member_point.svg b/yunxi-ui-admin-vue3/src/assets/svgs/member_point.svg new file mode 100644 index 00000000..b849ddb4 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/member_point.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/member_recharge_balance.svg b/yunxi-ui-admin-vue3/src/assets/svgs/member_recharge_balance.svg new file mode 100644 index 00000000..7519bb23 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/member_recharge_balance.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/message.svg b/yunxi-ui-admin-vue3/src/assets/svgs/message.svg new file mode 100644 index 00000000..14ca8172 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/money.svg b/yunxi-ui-admin-vue3/src/assets/svgs/money.svg new file mode 100644 index 00000000..c1580de1 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_app.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_app.svg new file mode 100644 index 00000000..ebf11883 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_app.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_bar.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_bar.svg new file mode 100644 index 00000000..eb1e1e84 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_bar.svg @@ -0,0 +1,2 @@ + diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_pc.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_pc.svg new file mode 100644 index 00000000..2a752770 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_pc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_qr.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_qr.svg new file mode 100644 index 00000000..48337508 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_qr.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_wap.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_wap.svg new file mode 100644 index 00000000..87075dbb --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/alipay_wap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/mock.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/mock.svg new file mode 100644 index 00000000..27b09ead --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/mock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_app.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_app.svg new file mode 100644 index 00000000..ad40b2a2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_app.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_bar.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_bar.svg new file mode 100644 index 00000000..11292e6e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_bar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_lite.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_lite.svg new file mode 100644 index 00000000..0c925cf3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_lite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_native.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_native.svg new file mode 100644 index 00000000..bf3ba2b6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_native.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_pub.svg b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_pub.svg new file mode 100644 index 00000000..3a6d15b7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/pay/icon/wx_pub.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/peoples.svg b/yunxi-ui-admin-vue3/src/assets/svgs/peoples.svg new file mode 100644 index 00000000..aab852e5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/assets/svgs/shopping.svg b/yunxi-ui-admin-vue3/src/assets/svgs/shopping.svg new file mode 100644 index 00000000..f395bc7f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/assets/svgs/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yunxi-ui-admin-vue3/src/components/Backtop/index.ts b/yunxi-ui-admin-vue3/src/components/Backtop/index.ts new file mode 100644 index 00000000..96de88d6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Backtop/index.ts @@ -0,0 +1,3 @@ +import Backtop from './src/Backtop.vue' + +export { Backtop } diff --git a/yunxi-ui-admin-vue3/src/components/Backtop/src/Backtop.vue b/yunxi-ui-admin-vue3/src/components/Backtop/src/Backtop.vue new file mode 100644 index 00000000..5d79f51a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Backtop/src/Backtop.vue @@ -0,0 +1,17 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Card/index.ts b/yunxi-ui-admin-vue3/src/components/Card/index.ts new file mode 100644 index 00000000..f4c0d86c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Card/index.ts @@ -0,0 +1,3 @@ +import CardTitle from './src/CardTitle.vue' + +export { CardTitle } diff --git a/yunxi-ui-admin-vue3/src/components/Card/src/CardTitle.vue b/yunxi-ui-admin-vue3/src/components/Card/src/CardTitle.vue new file mode 100644 index 00000000..5b122f49 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Card/src/CardTitle.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/components/ConfigGlobal/index.ts b/yunxi-ui-admin-vue3/src/components/ConfigGlobal/index.ts new file mode 100644 index 00000000..dda2462c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/ConfigGlobal/index.ts @@ -0,0 +1,3 @@ +import ConfigGlobal from './src/ConfigGlobal.vue' + +export { ConfigGlobal } diff --git a/yunxi-ui-admin-vue3/src/components/ConfigGlobal/src/ConfigGlobal.vue b/yunxi-ui-admin-vue3/src/components/ConfigGlobal/src/ConfigGlobal.vue new file mode 100644 index 00000000..a0873967 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/ConfigGlobal/src/ConfigGlobal.vue @@ -0,0 +1,63 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/ContentDetailWrap/index.ts b/yunxi-ui-admin-vue3/src/components/ContentDetailWrap/index.ts new file mode 100644 index 00000000..1871cac7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/ContentDetailWrap/index.ts @@ -0,0 +1,3 @@ +import ContentDetailWrap from './src/ContentDetailWrap.vue' + +export { ContentDetailWrap } diff --git a/yunxi-ui-admin-vue3/src/components/ContentDetailWrap/src/ContentDetailWrap.vue b/yunxi-ui-admin-vue3/src/components/ContentDetailWrap/src/ContentDetailWrap.vue new file mode 100644 index 00000000..a9eacc01 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/ContentDetailWrap/src/ContentDetailWrap.vue @@ -0,0 +1,58 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/ContentWrap/index.ts b/yunxi-ui-admin-vue3/src/components/ContentWrap/index.ts new file mode 100644 index 00000000..8c22cc83 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/ContentWrap/index.ts @@ -0,0 +1,3 @@ +import ContentWrap from './src/ContentWrap.vue' + +export { ContentWrap } diff --git a/yunxi-ui-admin-vue3/src/components/ContentWrap/src/ContentWrap.vue b/yunxi-ui-admin-vue3/src/components/ContentWrap/src/ContentWrap.vue new file mode 100644 index 00000000..e3bd5972 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/ContentWrap/src/ContentWrap.vue @@ -0,0 +1,34 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/CountTo/index.ts b/yunxi-ui-admin-vue3/src/components/CountTo/index.ts new file mode 100644 index 00000000..2119f023 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/CountTo/index.ts @@ -0,0 +1,3 @@ +import CountTo from './src/CountTo.vue' + +export { CountTo } diff --git a/yunxi-ui-admin-vue3/src/components/CountTo/src/CountTo.vue b/yunxi-ui-admin-vue3/src/components/CountTo/src/CountTo.vue new file mode 100644 index 00000000..7a19bec7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/CountTo/src/CountTo.vue @@ -0,0 +1,182 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Crontab/index.ts b/yunxi-ui-admin-vue3/src/components/Crontab/index.ts new file mode 100644 index 00000000..6beeef86 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Crontab/index.ts @@ -0,0 +1,2 @@ +import Crontab from './src/Crontab.vue' +export { Crontab } diff --git a/yunxi-ui-admin-vue3/src/components/Crontab/src/Crontab.vue b/yunxi-ui-admin-vue3/src/components/Crontab/src/Crontab.vue new file mode 100644 index 00000000..90b40b2f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Crontab/src/Crontab.vue @@ -0,0 +1,1011 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/components/Cropper/index.ts b/yunxi-ui-admin-vue3/src/components/Cropper/index.ts new file mode 100644 index 00000000..8fcc6183 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Cropper/src/CopperModal.vue b/yunxi-ui-admin-vue3/src/components/Cropper/src/CopperModal.vue new file mode 100644 index 00000000..27052b8a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Cropper/src/CopperModal.vue @@ -0,0 +1,261 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Cropper/src/Cropper.vue b/yunxi-ui-admin-vue3/src/components/Cropper/src/Cropper.vue new file mode 100644 index 00000000..871aed8f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Cropper/src/Cropper.vue @@ -0,0 +1,183 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Cropper/src/CropperAvatar.vue b/yunxi-ui-admin-vue3/src/components/Cropper/src/CropperAvatar.vue new file mode 100644 index 00000000..55a7d34b --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Cropper/src/CropperAvatar.vue @@ -0,0 +1,142 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Cropper/src/types.ts b/yunxi-ui-admin-vue3/src/components/Cropper/src/types.ts new file mode 100644 index 00000000..bcad3b45 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Descriptions/index.ts b/yunxi-ui-admin-vue3/src/components/Descriptions/index.ts new file mode 100644 index 00000000..243bc397 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Descriptions/index.ts @@ -0,0 +1,4 @@ +import Descriptions from './src/Descriptions.vue' +import DescriptionsItemLabel from './src/DescriptionsItemLabel.vue' + +export { Descriptions, DescriptionsItemLabel } diff --git a/yunxi-ui-admin-vue3/src/components/Descriptions/src/Descriptions.vue b/yunxi-ui-admin-vue3/src/components/Descriptions/src/Descriptions.vue new file mode 100644 index 00000000..06e1096a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Descriptions/src/Descriptions.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/components/Descriptions/src/DescriptionsItemLabel.vue b/yunxi-ui-admin-vue3/src/components/Descriptions/src/DescriptionsItemLabel.vue new file mode 100644 index 00000000..4efb2fb7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Descriptions/src/DescriptionsItemLabel.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/components/Dialog/index.ts b/yunxi-ui-admin-vue3/src/components/Dialog/index.ts new file mode 100644 index 00000000..1655dadc --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Dialog/index.ts @@ -0,0 +1,3 @@ +import Dialog from './src/Dialog.vue' + +export { Dialog } diff --git a/yunxi-ui-admin-vue3/src/components/Dialog/src/Dialog.vue b/yunxi-ui-admin-vue3/src/components/Dialog/src/Dialog.vue new file mode 100644 index 00000000..a1eb550c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Dialog/src/Dialog.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/components/DictTag/index.ts b/yunxi-ui-admin-vue3/src/components/DictTag/index.ts new file mode 100644 index 00000000..4db27420 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/DictTag/index.ts @@ -0,0 +1,3 @@ +import DictTag from './src/DictTag.vue' + +export { DictTag } diff --git a/yunxi-ui-admin-vue3/src/components/DictTag/src/DictTag.vue b/yunxi-ui-admin-vue3/src/components/DictTag/src/DictTag.vue new file mode 100644 index 00000000..db37f714 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/DictTag/src/DictTag.vue @@ -0,0 +1,60 @@ + diff --git a/yunxi-ui-admin-vue3/src/components/DocAlert/index.vue b/yunxi-ui-admin-vue3/src/components/DocAlert/index.vue new file mode 100644 index 00000000..3a3feab7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/DocAlert/index.vue @@ -0,0 +1,34 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Echart/index.ts b/yunxi-ui-admin-vue3/src/components/Echart/index.ts new file mode 100644 index 00000000..48220921 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Echart/index.ts @@ -0,0 +1,3 @@ +import Echart from './src/Echart.vue' + +export { Echart } diff --git a/yunxi-ui-admin-vue3/src/components/Echart/src/Echart.vue b/yunxi-ui-admin-vue3/src/components/Echart/src/Echart.vue new file mode 100644 index 00000000..fd3342dd --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Echart/src/Echart.vue @@ -0,0 +1,115 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Editor/index.ts b/yunxi-ui-admin-vue3/src/components/Editor/index.ts new file mode 100644 index 00000000..3fbf0a9c --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Editor/src/Editor.vue b/yunxi-ui-admin-vue3/src/components/Editor/src/Editor.vue new file mode 100644 index 00000000..ec40bca2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Editor/src/Editor.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/components/Error/index.ts b/yunxi-ui-admin-vue3/src/components/Error/index.ts new file mode 100644 index 00000000..a52c6f97 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Error/index.ts @@ -0,0 +1,3 @@ +import Error from './src/Error.vue' + +export { Error } diff --git a/yunxi-ui-admin-vue3/src/components/Error/src/Error.vue b/yunxi-ui-admin-vue3/src/components/Error/src/Error.vue new file mode 100644 index 00000000..3fd7a176 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Error/src/Error.vue @@ -0,0 +1,58 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Form/index.ts b/yunxi-ui-admin-vue3/src/components/Form/index.ts new file mode 100644 index 00000000..484c7a22 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Form/src/Form.vue b/yunxi-ui-admin-vue3/src/components/Form/src/Form.vue new file mode 100644 index 00000000..3acc10ab --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Form/src/Form.vue @@ -0,0 +1,307 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Form/src/componentMap.ts b/yunxi-ui-admin-vue3/src/components/Form/src/componentMap.ts new file mode 100644 index 00000000..5af9b40d --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Form/src/components/useRenderCheckbox.tsx b/yunxi-ui-admin-vue3/src/components/Form/src/components/useRenderCheckbox.tsx new file mode 100644 index 00000000..e1518395 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Form/src/components/useRenderRadio.tsx b/yunxi-ui-admin-vue3/src/components/Form/src/components/useRenderRadio.tsx new file mode 100644 index 00000000..d1005ca5 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Form/src/components/useRenderSelect.tsx b/yunxi-ui-admin-vue3/src/components/Form/src/components/useRenderSelect.tsx new file mode 100644 index 00000000..59b72e68 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Form/src/helper.ts b/yunxi-ui-admin-vue3/src/components/Form/src/helper.ts new file mode 100644 index 00000000..cdfc8caa --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Form/src/helper.ts @@ -0,0 +1,148 @@ +import type { Slots } from 'vue' +import { getSlot } from '@/utils/tsxHelper' +import { PlaceholderModel } from './types' +import { FormSchema } from '@/types/form' +import { ColProps } from '@/types/components' + +/** + * + * @param schema 对应组件数据 + * @returns 返回提示信息对象 + * @description 用于自动设置placeholder + */ +export const setTextPlaceholder = (schema: FormSchema): PlaceholderModel => { + 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') + schema.label + } + } + 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') + schema.label + } + } + } + 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 FormModel + * @returns FormModel + * @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/yunxi-ui-admin-vue3/src/components/Form/src/types.ts b/yunxi-ui-admin-vue3/src/components/Form/src/types.ts new file mode 100644 index 00000000..dcd01e78 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Form/src/types.ts @@ -0,0 +1,17 @@ +import { FormSchema } from '@/types/form' + +export interface PlaceholderModel { + 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/yunxi-ui-admin-vue3/src/components/Highlight/index.ts b/yunxi-ui-admin-vue3/src/components/Highlight/index.ts new file mode 100644 index 00000000..3e2d9ed6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Highlight/index.ts @@ -0,0 +1,3 @@ +import Highlight from './src/Highlight.vue' + +export { Highlight } diff --git a/yunxi-ui-admin-vue3/src/components/Highlight/src/Highlight.vue b/yunxi-ui-admin-vue3/src/components/Highlight/src/Highlight.vue new file mode 100644 index 00000000..ef923a9a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Highlight/src/Highlight.vue @@ -0,0 +1,65 @@ + diff --git a/yunxi-ui-admin-vue3/src/components/IFrame/index.ts b/yunxi-ui-admin-vue3/src/components/IFrame/index.ts new file mode 100644 index 00000000..9f8cf24a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/IFrame/index.ts @@ -0,0 +1,3 @@ +import IFrame from './src/IFrame.vue' + +export { IFrame } diff --git a/yunxi-ui-admin-vue3/src/components/IFrame/src/IFrame.vue b/yunxi-ui-admin-vue3/src/components/IFrame/src/IFrame.vue new file mode 100644 index 00000000..19de51a3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/IFrame/src/IFrame.vue @@ -0,0 +1,32 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/Icon/index.ts b/yunxi-ui-admin-vue3/src/components/Icon/index.ts new file mode 100644 index 00000000..33d1de38 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Icon/src/Icon.vue b/yunxi-ui-admin-vue3/src/components/Icon/src/Icon.vue new file mode 100644 index 00000000..4246539f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Icon/src/Icon.vue @@ -0,0 +1,86 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Icon/src/IconSelect.vue b/yunxi-ui-admin-vue3/src/components/Icon/src/IconSelect.vue new file mode 100644 index 00000000..d4a5b074 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Icon/src/IconSelect.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/components/Icon/src/data.ts b/yunxi-ui-admin-vue3/src/components/Icon/src/data.ts new file mode 100644 index 00000000..2a4ed5a3 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/ImageViewer/index.ts b/yunxi-ui-admin-vue3/src/components/ImageViewer/index.ts new file mode 100644 index 00000000..38681356 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/ImageViewer/src/ImageViewer.vue b/yunxi-ui-admin-vue3/src/components/ImageViewer/src/ImageViewer.vue new file mode 100644 index 00000000..5c4921ed --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/ImageViewer/src/ImageViewer.vue @@ -0,0 +1,35 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/ImageViewer/src/types.ts b/yunxi-ui-admin-vue3/src/components/ImageViewer/src/types.ts new file mode 100644 index 00000000..1932d74d --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Infotip/index.ts b/yunxi-ui-admin-vue3/src/components/Infotip/index.ts new file mode 100644 index 00000000..413fa5f4 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Infotip/index.ts @@ -0,0 +1,3 @@ +import Infotip from './src/Infotip.vue' + +export { Infotip } diff --git a/yunxi-ui-admin-vue3/src/components/Infotip/src/Infotip.vue b/yunxi-ui-admin-vue3/src/components/Infotip/src/Infotip.vue new file mode 100644 index 00000000..0afd6928 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Infotip/src/Infotip.vue @@ -0,0 +1,54 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/InputPassword/index.ts b/yunxi-ui-admin-vue3/src/components/InputPassword/index.ts new file mode 100644 index 00000000..1dcc38e9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/InputPassword/index.ts @@ -0,0 +1,3 @@ +import InputPassword from './src/InputPassword.vue' + +export { InputPassword } diff --git a/yunxi-ui-admin-vue3/src/components/InputPassword/src/InputPassword.vue b/yunxi-ui-admin-vue3/src/components/InputPassword/src/InputPassword.vue new file mode 100644 index 00000000..b8c93e7d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/InputPassword/src/InputPassword.vue @@ -0,0 +1,152 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/components/Pagination/index.vue b/yunxi-ui-admin-vue3/src/components/Pagination/index.vue new file mode 100644 index 00000000..b88997b1 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Pagination/index.vue @@ -0,0 +1,87 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Qrcode/index.ts b/yunxi-ui-admin-vue3/src/components/Qrcode/index.ts new file mode 100644 index 00000000..ce461612 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Qrcode/index.ts @@ -0,0 +1,3 @@ +import Qrcode from './src/Qrcode.vue' + +export { Qrcode } diff --git a/yunxi-ui-admin-vue3/src/components/Qrcode/src/Qrcode.vue b/yunxi-ui-admin-vue3/src/components/Qrcode/src/Qrcode.vue new file mode 100644 index 00000000..f0ce7b79 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Qrcode/src/Qrcode.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/components/RouterSearch/index.vue b/yunxi-ui-admin-vue3/src/components/RouterSearch/index.vue new file mode 100644 index 00000000..c12385af --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/RouterSearch/index.vue @@ -0,0 +1,76 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Search/index.ts b/yunxi-ui-admin-vue3/src/components/Search/index.ts new file mode 100644 index 00000000..fcc6f163 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Search/index.ts @@ -0,0 +1,3 @@ +import Search from './src/Search.vue' + +export { Search } diff --git a/yunxi-ui-admin-vue3/src/components/Search/src/Search.vue b/yunxi-ui-admin-vue3/src/components/Search/src/Search.vue new file mode 100644 index 00000000..3218a63a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Search/src/Search.vue @@ -0,0 +1,157 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Sticky/index.ts b/yunxi-ui-admin-vue3/src/components/Sticky/index.ts new file mode 100644 index 00000000..5e1de45e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Sticky/index.ts @@ -0,0 +1,3 @@ +import Sticky from './src/Sticky.vue' + +export { Sticky } diff --git a/yunxi-ui-admin-vue3/src/components/Sticky/src/Sticky.vue b/yunxi-ui-admin-vue3/src/components/Sticky/src/Sticky.vue new file mode 100644 index 00000000..b958544a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Sticky/src/Sticky.vue @@ -0,0 +1,143 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/Table/index.ts b/yunxi-ui-admin-vue3/src/components/Table/index.ts new file mode 100644 index 00000000..689f64a8 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Table/src/Table.vue b/yunxi-ui-admin-vue3/src/components/Table/src/Table.vue new file mode 100644 index 00000000..279a9fac --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Table/src/Table.vue @@ -0,0 +1,311 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/Table/src/helper.ts b/yunxi-ui-admin-vue3/src/components/Table/src/helper.ts new file mode 100644 index 00000000..d8b34a8a --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Table/src/types.ts b/yunxi-ui-admin-vue3/src/components/Table/src/types.ts new file mode 100644 index 00000000..1c7ff765 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Tooltip/index.ts b/yunxi-ui-admin-vue3/src/components/Tooltip/index.ts new file mode 100644 index 00000000..ab66ddff --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Tooltip/index.ts @@ -0,0 +1,3 @@ +import Tooltip from './src/Tooltip.vue' + +export { Tooltip } diff --git a/yunxi-ui-admin-vue3/src/components/Tooltip/src/Tooltip.vue b/yunxi-ui-admin-vue3/src/components/Tooltip/src/Tooltip.vue new file mode 100644 index 00000000..7490bd70 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Tooltip/src/Tooltip.vue @@ -0,0 +1,17 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/UploadFile/index.ts b/yunxi-ui-admin-vue3/src/components/UploadFile/index.ts new file mode 100644 index 00000000..97c1d665 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadFile.vue b/yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadFile.vue new file mode 100644 index 00000000..c8a3b972 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadFile.vue @@ -0,0 +1,170 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadImg.vue b/yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadImg.vue new file mode 100644 index 00000000..3cfc0a73 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadImg.vue @@ -0,0 +1,271 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadImgs.vue b/yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadImgs.vue new file mode 100644 index 00000000..91bb5e31 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/UploadFile/src/UploadImgs.vue @@ -0,0 +1,309 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/components/Verifition/index.ts b/yunxi-ui-admin-vue3/src/components/Verifition/index.ts new file mode 100644 index 00000000..bcfe6d94 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Verifition/index.ts @@ -0,0 +1,3 @@ +import Verify from './src/Verify.vue' + +export { Verify } diff --git a/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify.vue b/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify.vue new file mode 100644 index 00000000..b7b50486 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify.vue @@ -0,0 +1,441 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/VerifyPoints.vue b/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/VerifyPoints.vue new file mode 100644 index 00000000..9d04f291 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/VerifyPoints.vue @@ -0,0 +1,250 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue b/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue new file mode 100644 index 00000000..8b448b0d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/VerifySlide.vue @@ -0,0 +1,376 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/index.ts b/yunxi-ui-admin-vue3/src/components/Verifition/src/Verify/index.ts new file mode 100644 index 00000000..0daa63a5 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Verifition/src/utils/ase.ts b/yunxi-ui-admin-vue3/src/components/Verifition/src/utils/ase.ts new file mode 100644 index 00000000..d2e6b988 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/Verifition/src/utils/util.ts b/yunxi-ui-admin-vue3/src/components/Verifition/src/utils/util.ts new file mode 100644 index 00000000..15c16270 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/XButton/index.ts b/yunxi-ui-admin-vue3/src/components/XButton/index.ts new file mode 100644 index 00000000..be0f0d4f --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/XButton/src/XButton.vue b/yunxi-ui-admin-vue3/src/components/XButton/src/XButton.vue new file mode 100644 index 00000000..40cba1ac --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/XButton/src/XButton.vue @@ -0,0 +1,50 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/components/XButton/src/XTextButton.vue b/yunxi-ui-admin-vue3/src/components/XButton/src/XTextButton.vue new file mode 100644 index 00000000..b1a922b3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/XButton/src/XTextButton.vue @@ -0,0 +1,49 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue new file mode 100644 index 00000000..3fe21944 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue @@ -0,0 +1,704 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue new file mode 100644 index 00000000..e2cd4679 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue @@ -0,0 +1,635 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/index.ts b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/index.ts new file mode 100644 index 00000000..85228468 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/index2.ts b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/index2.ts new file mode 100644 index 00000000..ebe8ca78 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/contentPadProvider.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/contentPadProvider.js new file mode 100644 index 00000000..87834931 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/index.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/content-pad/index.js new file mode 100644 index 00000000..80009efc --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/defaultEmpty.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/defaultEmpty.js new file mode 100644 index 00000000..f3bc894f --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/activitiDescriptor.json b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/activitiDescriptor.json new file mode 100644 index 00000000..db5e4901 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/camundaDescriptor.json b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/camundaDescriptor.json new file mode 100644 index 00000000..79b86bca --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json new file mode 100644 index 00000000..7fe7ad14 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/activitiExtension.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/activitiExtension.js new file mode 100644 index 00000000..56ef38aa --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/index.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/activiti/index.js new file mode 100644 index 00000000..c22ca345 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/extension.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/extension.js new file mode 100644 index 00000000..b8c37a57 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/index.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/camunda/index.js new file mode 100644 index 00000000..1da1bc70 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/flowableExtension.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/flowableExtension.js new file mode 100644 index 00000000..3dcea677 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/index.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/extension-moddle/flowable/index.js new file mode 100644 index 00000000..6d59b67f --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js new file mode 100644 index 00000000..5e2803b5 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/palette/index.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/palette/index.js new file mode 100644 index 00000000..8e4f3ac9 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js new file mode 100644 index 00000000..7098981c --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/translate/customTranslate.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/translate/customTranslate.js new file mode 100644 index 00000000..c1b99e12 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/translate/zh.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/designer/plugins/translate/zh.js new file mode 100644 index 00000000..777db3e7 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/index.ts b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/index.ts new file mode 100644 index 00000000..ce44a3c5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/index.ts @@ -0,0 +1,11 @@ +import MyProcessDesigner from './designer' +import MyProcessPenal from './penal' +import MyProcessViewer from './designer/index2' + +import './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' + +export { MyProcessDesigner, MyProcessPenal, MyProcessViewer } diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/palette/ProcessPalette.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/palette/ProcessPalette.vue new file mode 100644 index 00000000..ba97d967 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/palette/ProcessPalette.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue new file mode 100644 index 00000000..377592f4 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue @@ -0,0 +1,211 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue new file mode 100644 index 00000000..639c1cb2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/base/ElementBaseInfo.vue @@ -0,0 +1,184 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue new file mode 100644 index 00000000..345670ae --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue @@ -0,0 +1,191 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/form/ElementForm.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/form/ElementForm.vue new file mode 100644 index 00000000..da1d1ae9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/form/ElementForm.vue @@ -0,0 +1,465 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/index.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/index.js new file mode 100644 index 00000000..7fa56170 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue new file mode 100644 index 00000000..45ee8f93 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue @@ -0,0 +1,403 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue new file mode 100644 index 00000000..9464883c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue @@ -0,0 +1,451 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/template.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/template.js new file mode 100644 index 00000000..430dc64b --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/template.js @@ -0,0 +1,178 @@ +export const template = (isTaskListener) => { + return ` +
+ + + + + + + + +
+ 添加监听器 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + ${ + isTaskListener + ? "" + + "" + + "" + + "" + + "" + + "" + + '' + + '' + + "" + + "" + + '' + : '' + } + + +

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

+ + + + + + + + + + +
+ 取 消 + 保 存 +
+
+ + + + + + + + + + + + + + + + + + + + + +
+ ` +} diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/utilSelf.ts b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/listeners/utilSelf.ts new file mode 100644 index 00000000..5f46abd0 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/multi-instance/ElementMultiInstance.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/multi-instance/ElementMultiInstance.vue new file mode 100644 index 00000000..28db5aa7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/multi-instance/ElementMultiInstance.vue @@ -0,0 +1,254 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue new file mode 100644 index 00000000..05532c69 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue @@ -0,0 +1,55 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue new file mode 100644 index 00000000..494b3d97 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue @@ -0,0 +1,169 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue new file mode 100644 index 00000000..f38f31c6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue @@ -0,0 +1,113 @@ + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/ElementTask.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/ElementTask.vue new file mode 100644 index 00000000..33a12a74 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/ElementTask.vue @@ -0,0 +1,86 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue new file mode 100644 index 00000000..83ed24eb --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue @@ -0,0 +1,125 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue new file mode 100644 index 00000000..683fef3b --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue @@ -0,0 +1,99 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue new file mode 100644 index 00000000..7b793dbc --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue @@ -0,0 +1,98 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/element-variables.scss b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/element-variables.scss new file mode 100644 index 00000000..49bd326d --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/index.scss b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/index.scss new file mode 100644 index 00000000..2e60fad4 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/index.scss @@ -0,0 +1,2 @@ +@import './process-designer.scss'; +@import './process-panel.scss'; diff --git a/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/process-designer.scss b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/process-designer.scss new file mode 100644 index 00000000..6af945da --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/process-panel.scss b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/theme/process-panel.scss new file mode 100644 index 00000000..f840cdde --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/utils.ts b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/utils.ts new file mode 100644 index 00000000..bb6c5d52 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/package/utils.ts @@ -0,0 +1,77 @@ +import { toRaw } from 'vue' +const bpmnInstances = () => (window as any)?.bpmnInstances +// 创建监听器实例 +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 = bpmnInstances().moddle.create('bpmn:FormalExpression', { + body: options.eventTimeDefinitions + }) + const TimerEventDefinition = bpmnInstances().moddle.create('bpmn:TimerEventDefinition', { + id: `TimerEventDefinition_${uuid(8)}`, + [`time${options.eventDefinitionType.replace(/^\S/, (s) => s.toUpperCase())}`]: timeDefinition + }) + listenerObj.eventDefinitions = [TimerEventDefinition] + } + return 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 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 bpmnInstances().moddle.create(`${prefix}:Script`, scriptConfig) +} + +// 更新元素扩展属性 +export function updateElementExtensions(element, extensionList) { + const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', { + values: extensionList + }) + 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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/highlight/index.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/highlight/index.js new file mode 100644 index 00000000..5df38c9a --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/custom-renderer/CustomRenderer.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/custom-renderer/CustomRenderer.js new file mode 100644 index 00000000..e8760315 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/custom-renderer/index.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/custom-renderer/index.js new file mode 100644 index 00000000..79d8bd04 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/rules/CustomRules.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/rules/CustomRules.js new file mode 100644 index 00000000..9fa1d14a --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/rules/index.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/modules/rules/index.js new file mode 100644 index 00000000..12cf05a7 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/translations.ts b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/translations.ts new file mode 100644 index 00000000..5f9b9a51 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/utils/directive/clickOutSide.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/utils/directive/clickOutSide.js new file mode 100644 index 00000000..bb71d442 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/utils/index.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/utils/index.js new file mode 100644 index 00000000..7d970ecd --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/utils/xml2json.js b/yunxi-ui-admin-vue3/src/components/bpmnProcessDesigner/src/utils/xml2json.js new file mode 100644 index 00000000..fe1a52fb --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/components/index.ts b/yunxi-ui-admin-vue3/src/components/index.ts new file mode 100644 index 00000000..4d030c37 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/components/index.ts @@ -0,0 +1,6 @@ +import type { App } from 'vue' +import { Icon } from './Icon' + +export const setupGlobCom = (app: App): void => { + app.component('Icon', Icon) +} diff --git a/yunxi-ui-admin-vue3/src/config/axios/config.ts b/yunxi-ui-admin-vue3/src/config/axios/config.ts new file mode 100644 index 00000000..81165087 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/config/axios/errorCode.ts b/yunxi-ui-admin-vue3/src/config/axios/errorCode.ts new file mode 100644 index 00000000..94d719f8 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/config/axios/errorCode.ts @@ -0,0 +1,6 @@ +export default { + '401': '认证失败,无法访问系统资源', + '403': '当前操作没有权限', + '404': '访问资源不存在', + default: '系统未知错误,请反馈给管理员' +} diff --git a/yunxi-ui-admin-vue3/src/config/axios/index.ts b/yunxi-ui-admin-vue3/src/config/axios/index.ts new file mode 100644 index 00000000..79e558da --- /dev/null +++ b/yunxi-ui-admin-vue3/src/config/axios/index.ts @@ -0,0 +1,51 @@ +import { service } from './service' + +import { config } from './config' + +const { default_headers } = config + +const request = (option: any) => { + const { url, method, params, data, headersType, responseType, ...config } = option + return service({ + url: url, + method, + params, + data, + ...config, + 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/yunxi-ui-admin-vue3/src/config/axios/service.ts b/yunxi-ui-admin-vue3/src/config/axios/service.ts new file mode 100644 index 00000000..1a4741b6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/config/axios/service.ts @@ -0,0 +1,239 @@ +import axios, { + AxiosError, + AxiosInstance, + AxiosRequestHeaders, + AxiosResponse, + 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({ + 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'), { + showCancelButton: false, + closeOnClickModal: false, + showClose: false, + confirmButtonText: t('login.relogin'), + type: 'warning' + }).then(() => { + const { wsCache } = useCache() + resetRouter() // 重置静态路由表 + wsCache.clear() + removeToken() + isRelogin.show = false + // 干掉token后再走一次路由让它过router.beforeEach的校验 + window.location.href = window.location.href + }) + } + return Promise.reject(t('sys.api.timeoutMessage')) +} +export { service } diff --git a/yunxi-ui-admin-vue3/src/directives/index.ts b/yunxi-ui-admin-vue3/src/directives/index.ts new file mode 100644 index 00000000..89cc8ba1 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/directives/permission/hasPermi.ts b/yunxi-ui-admin-vue3/src/directives/permission/hasPermi.ts new file mode 100644 index 00000000..d86d2f54 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/directives/permission/hasRole.ts b/yunxi-ui-admin-vue3/src/directives/permission/hasRole.ts new file mode 100644 index 00000000..31a352a7 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/event/useScrollTo.ts b/yunxi-ui-admin-vue3/src/hooks/event/useScrollTo.ts new file mode 100644 index 00000000..92aec872 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useCache.ts b/yunxi-ui-admin-vue3/src/hooks/web/useCache.ts new file mode 100644 index 00000000..6d2a9318 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useConfigGlobal.ts b/yunxi-ui-admin-vue3/src/hooks/web/useConfigGlobal.ts new file mode 100644 index 00000000..afb3db3a --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useCrudSchemas.ts b/yunxi-ui-admin-vue3/src/hooks/web/useCrudSchemas.ts new file mode 100644 index 00000000..458b57ec --- /dev/null +++ b/yunxi-ui-admin-vue3/src/hooks/web/useCrudSchemas.ts @@ -0,0 +1,326 @@ +import { reactive } from 'vue' +import { AxiosPromise } from 'axios' +import { findIndex } from '@/utils' +import { eachTree, filter, treeMap } 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' +import { DictTag } from '@/components/DictTag' +import { cloneDeep, merge } from 'lodash-es' + +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 + // 搜索字段 + field?: string +} & Omit + +type CrudTableParams = { + // 是否显示表头 + show?: boolean + // 列宽配置 + width?: number | string + // 列是否固定在左侧或者右侧 + fixed?: 'left' | 'right' +} & 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' + } + + // updated by AKing: 解决了当使用默认的dict选项时,form中事件不能触发的问题 + const searchSchemaItem = merge( + { + // 默认为 input + component, + ...schemaItem.search, + field: schemaItem.field, + label: schemaItem.search?.label || schemaItem.label + }, + { componentProps: comonentProps } + ) + 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) { + // add by 芋艿:增加对 dict 字典数据的支持 + if (!schema.formatter && schema.dictType) { + schema.formatter = (_: Recordable, __: TableColumn, cellValue: any) => { + return h(DictTag, { + type: schema.dictType!, // ! 表示一定不为空 + value: cellValue + }) + } + } + 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' + } + + // updated by AKing: 解决了当使用默认的dict选项时,form中事件不能触发的问题 + const formSchemaItem = merge( + { + // 默认为 input + component, + value: defaultValue, + ...schemaItem.form, + field: schemaItem.field, + label: schemaItem.form?.label || schemaItem.label + }, + { componentProps: comonentProps } + ) + + 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 + }) +} + +// 将 tableColumns 指定 fields 放到最前面 +export const sortTableColumns = (tableColumns: TableColumn[], field: string) => { + const fieldIndex = tableColumns.findIndex((item) => item.field === field) + const fieldColumn = cloneDeep(tableColumns[fieldIndex]) + tableColumns.splice(fieldIndex, 1) + // 添加到开头 + tableColumns.unshift(fieldColumn) +} diff --git a/yunxi-ui-admin-vue3/src/hooks/web/useDesign.ts b/yunxi-ui-admin-vue3/src/hooks/web/useDesign.ts new file mode 100644 index 00000000..8ee3b38c --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useEmitt.ts b/yunxi-ui-admin-vue3/src/hooks/web/useEmitt.ts new file mode 100644 index 00000000..d4efea72 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useForm.ts b/yunxi-ui-admin-vue3/src/hooks/web/useForm.ts new file mode 100644 index 00000000..53a8a94d --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useI18n.ts b/yunxi-ui-admin-vue3/src/hooks/web/useI18n.ts new file mode 100644 index 00000000..d1ab70fa --- /dev/null +++ b/yunxi-ui-admin-vue3/src/hooks/web/useI18n.ts @@ -0,0 +1,53 @@ +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 + //@ts-ignore + return t(getKey(namespace, key), ...(arg as I18nTranslationRestParameters)) + } + return { + ...methods, + t: tFn + } +} + +export const t = (key: string) => key diff --git a/yunxi-ui-admin-vue3/src/hooks/web/useIcon.ts b/yunxi-ui-admin-vue3/src/hooks/web/useIcon.ts new file mode 100644 index 00000000..35002049 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useIntro.ts b/yunxi-ui-admin-vue3/src/hooks/web/useIntro.ts new file mode 100644 index 00000000..7fe00845 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useLocale.ts b/yunxi-ui-admin-vue3/src/hooks/web/useLocale.ts new file mode 100644 index 00000000..c65070ef --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useMessage.ts b/yunxi-ui-admin-vue3/src/hooks/web/useMessage.ts new file mode 100644 index 00000000..ac2b552e --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useNProgress.ts b/yunxi-ui-admin-vue3/src/hooks/web/useNProgress.ts new file mode 100644 index 00000000..6d8c0b91 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/usePageLoading.ts b/yunxi-ui-admin-vue3/src/hooks/web/usePageLoading.ts new file mode 100644 index 00000000..bb89457d --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useTable.ts b/yunxi-ui-admin-vue3/src/hooks/web/useTable.ts new file mode 100644 index 00000000..361dd678 --- /dev/null +++ b/yunxi-ui-admin-vue3/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 + tableObject.total = (res as unknown as ResponseType).total ?? 0 + } + }, + 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, + // add by 芋艿:返回 tableMethods 属性,和 tableObject 更统一 + tableMethods: methods + } +} diff --git a/yunxi-ui-admin-vue3/src/hooks/web/useTimeAgo.ts b/yunxi-ui-admin-vue3/src/hooks/web/useTimeAgo.ts new file mode 100644 index 00000000..a6da2819 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/hooks/web/useTimeAgo.ts @@ -0,0 +1,49 @@ +import { useTimeAgo as useTimeAgoCore, UseTimeAgoMessages } from '@vueuse/core' +import { useLocaleStoreWithOut } from '@/store/modules/locale' + +const TIME_AGO_MESSAGE_MAP: { + 'zh-CN': UseTimeAgoMessages + en: UseTimeAgoMessages +} = { + // @ts-ignore + '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} 秒` + }, + // @ts-ignore + 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/yunxi-ui-admin-vue3/src/hooks/web/useTitle.ts b/yunxi-ui-admin-vue3/src/hooks/web/useTitle.ts new file mode 100644 index 00000000..020a9b77 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useValidator.ts b/yunxi-ui-admin-vue3/src/hooks/web/useValidator.ts new file mode 100644 index 00000000..0c16fa31 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/hooks/web/useWatermark.ts b/yunxi-ui-admin-vue3/src/hooks/web/useWatermark.ts new file mode 100644 index 00000000..4a313594 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/layout/Layout.vue b/yunxi-ui-admin-vue3/src/layout/Layout.vue new file mode 100644 index 00000000..43f9b69d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/Layout.vue @@ -0,0 +1,78 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/AppView.vue b/yunxi-ui-admin-vue3/src/layout/components/AppView.vue new file mode 100644 index 00000000..ffdf11f5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/AppView.vue @@ -0,0 +1,61 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/index.ts b/yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/index.ts new file mode 100644 index 00000000..93ffe705 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/index.ts @@ -0,0 +1,3 @@ +import Breadcrumb from './src/Breadcrumb.vue' + +export { Breadcrumb } diff --git a/yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/src/Breadcrumb.vue b/yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/src/Breadcrumb.vue new file mode 100644 index 00000000..1852a59b --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/src/Breadcrumb.vue @@ -0,0 +1,129 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/src/helper.ts b/yunxi-ui-admin-vue3/src/layout/components/Breadcrumb/src/helper.ts new file mode 100644 index 00000000..fb3ec197 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/layout/components/Collapse/index.ts b/yunxi-ui-admin-vue3/src/layout/components/Collapse/index.ts new file mode 100644 index 00000000..73f65a3a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Collapse/index.ts @@ -0,0 +1,3 @@ +import Collapse from './src/Collapse.vue' + +export { Collapse } diff --git a/yunxi-ui-admin-vue3/src/layout/components/Collapse/src/Collapse.vue b/yunxi-ui-admin-vue3/src/layout/components/Collapse/src/Collapse.vue new file mode 100644 index 00000000..ecb6890f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Collapse/src/Collapse.vue @@ -0,0 +1,36 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/ContextMenu/index.ts b/yunxi-ui-admin-vue3/src/layout/components/ContextMenu/index.ts new file mode 100644 index 00000000..2a7c1f0d --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/layout/components/ContextMenu/src/ContextMenu.vue b/yunxi-ui-admin-vue3/src/layout/components/ContextMenu/src/ContextMenu.vue new file mode 100644 index 00000000..90eea4c2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/ContextMenu/src/ContextMenu.vue @@ -0,0 +1,76 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Footer/index.ts b/yunxi-ui-admin-vue3/src/layout/components/Footer/index.ts new file mode 100644 index 00000000..bd052e0c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Footer/index.ts @@ -0,0 +1,3 @@ +import Footer from './src/Footer.vue' + +export { Footer } diff --git a/yunxi-ui-admin-vue3/src/layout/components/Footer/src/Footer.vue b/yunxi-ui-admin-vue3/src/layout/components/Footer/src/Footer.vue new file mode 100644 index 00000000..c350e38a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Footer/src/Footer.vue @@ -0,0 +1,24 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/LocaleDropdown/index.ts b/yunxi-ui-admin-vue3/src/layout/components/LocaleDropdown/index.ts new file mode 100644 index 00000000..d02e640f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/LocaleDropdown/index.ts @@ -0,0 +1,3 @@ +import LocaleDropdown from './src/LocaleDropdown.vue' + +export { LocaleDropdown } diff --git a/yunxi-ui-admin-vue3/src/layout/components/LocaleDropdown/src/LocaleDropdown.vue b/yunxi-ui-admin-vue3/src/layout/components/LocaleDropdown/src/LocaleDropdown.vue new file mode 100644 index 00000000..95132db2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/LocaleDropdown/src/LocaleDropdown.vue @@ -0,0 +1,52 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Logo/index.ts b/yunxi-ui-admin-vue3/src/layout/components/Logo/index.ts new file mode 100644 index 00000000..1c4224c9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Logo/index.ts @@ -0,0 +1,3 @@ +import Logo from './src/Logo.vue' + +export { Logo } diff --git a/yunxi-ui-admin-vue3/src/layout/components/Logo/src/Logo.vue b/yunxi-ui-admin-vue3/src/layout/components/Logo/src/Logo.vue new file mode 100644 index 00000000..d241130d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Logo/src/Logo.vue @@ -0,0 +1,88 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Menu/index.ts b/yunxi-ui-admin-vue3/src/layout/components/Menu/index.ts new file mode 100644 index 00000000..a6ec6965 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Menu/index.ts @@ -0,0 +1,3 @@ +import Menu from './src/Menu.vue' + +export { Menu } diff --git a/yunxi-ui-admin-vue3/src/layout/components/Menu/src/Menu.vue b/yunxi-ui-admin-vue3/src/layout/components/Menu/src/Menu.vue new file mode 100644 index 00000000..9033616f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Menu/src/Menu.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Menu/src/components/useRenderMenuItem.tsx b/yunxi-ui-admin-vue3/src/layout/components/Menu/src/components/useRenderMenuItem.tsx new file mode 100644 index 00000000..17a520a6 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/layout/components/Menu/src/components/useRenderMenuTitle.tsx b/yunxi-ui-admin-vue3/src/layout/components/Menu/src/components/useRenderMenuTitle.tsx new file mode 100644 index 00000000..fc30b900 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/layout/components/Menu/src/helper.ts b/yunxi-ui-admin-vue3/src/layout/components/Menu/src/helper.ts new file mode 100644 index 00000000..c26f5f4b --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/layout/components/Message/index.ts b/yunxi-ui-admin-vue3/src/layout/components/Message/index.ts new file mode 100644 index 00000000..dfe02076 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Message/index.ts @@ -0,0 +1,3 @@ +import Message from './src/Message.vue' + +export { Message } diff --git a/yunxi-ui-admin-vue3/src/layout/components/Message/src/Message.vue b/yunxi-ui-admin-vue3/src/layout/components/Message/src/Message.vue new file mode 100644 index 00000000..28f796b3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Message/src/Message.vue @@ -0,0 +1,125 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Screenfull/index.ts b/yunxi-ui-admin-vue3/src/layout/components/Screenfull/index.ts new file mode 100644 index 00000000..faec2d8e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Screenfull/index.ts @@ -0,0 +1,3 @@ +import Screenfull from './src/Screenfull.vue' + +export { Screenfull } diff --git a/yunxi-ui-admin-vue3/src/layout/components/Screenfull/src/Screenfull.vue b/yunxi-ui-admin-vue3/src/layout/components/Screenfull/src/Screenfull.vue new file mode 100644 index 00000000..4c045f25 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Screenfull/src/Screenfull.vue @@ -0,0 +1,32 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Setting/index.ts b/yunxi-ui-admin-vue3/src/layout/components/Setting/index.ts new file mode 100644 index 00000000..b64c9add --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Setting/index.ts @@ -0,0 +1,3 @@ +import Setting from './src/Setting.vue' + +export { Setting } diff --git a/yunxi-ui-admin-vue3/src/layout/components/Setting/src/Setting.vue b/yunxi-ui-admin-vue3/src/layout/components/Setting/src/Setting.vue new file mode 100644 index 00000000..e1908b63 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Setting/src/Setting.vue @@ -0,0 +1,299 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/ColorRadioPicker.vue b/yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/ColorRadioPicker.vue new file mode 100644 index 00000000..fcc5e758 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/ColorRadioPicker.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/InterfaceDisplay.vue b/yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/InterfaceDisplay.vue new file mode 100644 index 00000000..ebbbf4bc --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/InterfaceDisplay.vue @@ -0,0 +1,224 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/LayoutRadioPicker.vue b/yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/LayoutRadioPicker.vue new file mode 100644 index 00000000..801686c2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/Setting/src/components/LayoutRadioPicker.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/SizeDropdown/index.ts b/yunxi-ui-admin-vue3/src/layout/components/SizeDropdown/index.ts new file mode 100644 index 00000000..516488d6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/SizeDropdown/index.ts @@ -0,0 +1,3 @@ +import SizeDropdown from './src/SizeDropdown.vue' + +export { SizeDropdown } diff --git a/yunxi-ui-admin-vue3/src/layout/components/SizeDropdown/src/SizeDropdown.vue b/yunxi-ui-admin-vue3/src/layout/components/SizeDropdown/src/SizeDropdown.vue new file mode 100644 index 00000000..3e152244 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/SizeDropdown/src/SizeDropdown.vue @@ -0,0 +1,40 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/TabMenu/index.ts b/yunxi-ui-admin-vue3/src/layout/components/TabMenu/index.ts new file mode 100644 index 00000000..b5fd71cd --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/TabMenu/index.ts @@ -0,0 +1,3 @@ +import TabMenu from './src/TabMenu.vue' + +export { TabMenu } diff --git a/yunxi-ui-admin-vue3/src/layout/components/TabMenu/src/TabMenu.vue b/yunxi-ui-admin-vue3/src/layout/components/TabMenu/src/TabMenu.vue new file mode 100644 index 00000000..c4f63a3f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/TabMenu/src/TabMenu.vue @@ -0,0 +1,240 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/TabMenu/src/helper.ts b/yunxi-ui-admin-vue3/src/layout/components/TabMenu/src/helper.ts new file mode 100644 index 00000000..cce39323 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/layout/components/TagsView/index.ts b/yunxi-ui-admin-vue3/src/layout/components/TagsView/index.ts new file mode 100644 index 00000000..30e604a8 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/TagsView/index.ts @@ -0,0 +1,3 @@ +import TagsView from './src/TagsView.vue' + +export { TagsView } diff --git a/yunxi-ui-admin-vue3/src/layout/components/TagsView/src/TagsView.vue b/yunxi-ui-admin-vue3/src/layout/components/TagsView/src/TagsView.vue new file mode 100644 index 00000000..7db0cf6f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/TagsView/src/TagsView.vue @@ -0,0 +1,585 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/TagsView/src/helper.ts b/yunxi-ui-admin-vue3/src/layout/components/TagsView/src/helper.ts new file mode 100644 index 00000000..22f6a507 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/layout/components/ThemeSwitch/index.ts b/yunxi-ui-admin-vue3/src/layout/components/ThemeSwitch/index.ts new file mode 100644 index 00000000..823a2765 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/ThemeSwitch/index.ts @@ -0,0 +1,3 @@ +import ThemeSwitch from './src/ThemeSwitch.vue' + +export { ThemeSwitch } diff --git a/yunxi-ui-admin-vue3/src/layout/components/ThemeSwitch/src/ThemeSwitch.vue b/yunxi-ui-admin-vue3/src/layout/components/ThemeSwitch/src/ThemeSwitch.vue new file mode 100644 index 00000000..39a8cfd7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/ThemeSwitch/src/ThemeSwitch.vue @@ -0,0 +1,46 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/ToolHeader.vue b/yunxi-ui-admin-vue3/src/layout/components/ToolHeader.vue new file mode 100644 index 00000000..ec7882de --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/ToolHeader.vue @@ -0,0 +1,90 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/UserInfo/index.ts b/yunxi-ui-admin-vue3/src/layout/components/UserInfo/index.ts new file mode 100644 index 00000000..c3a34aba --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/UserInfo/index.ts @@ -0,0 +1,3 @@ +import UserInfo from './src/UserInfo.vue' + +export { UserInfo } diff --git a/yunxi-ui-admin-vue3/src/layout/components/UserInfo/src/UserInfo.vue b/yunxi-ui-admin-vue3/src/layout/components/UserInfo/src/UserInfo.vue new file mode 100644 index 00000000..a5a92da3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/UserInfo/src/UserInfo.vue @@ -0,0 +1,78 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/layout/components/useRenderLayout.tsx b/yunxi-ui-admin-vue3/src/layout/components/useRenderLayout.tsx new file mode 100644 index 00000000..1110cd86 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/layout/components/useRenderLayout.tsx @@ -0,0 +1,306 @@ +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/yunxi-ui-admin-vue3/src/locales/en.ts b/yunxi-ui-admin-vue3/src/locales/en.ts new file mode 100644 index 00000000..4f4d4895 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/locales/en.ts @@ -0,0 +1,447 @@ +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', + delete: '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', + fileUpload: 'File Upload' + }, + 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/yunxi-ui-admin-vue3/src/locales/zh-CN.ts b/yunxi-ui-admin-vue3/src/locales/zh-CN.ts new file mode 100644 index 00000000..cc4bb47e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/locales/zh-CN.ts @@ -0,0 +1,440 @@ +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: '删除', + delete: '删除', + 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: '登录', + ssoFormTitle: '三方授权', + 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/yunxi-ui-admin-vue3/src/main.ts b/yunxi-ui-admin-vue3/src/main.ts new file mode 100644 index 00000000..76c72473 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/main.ts @@ -0,0 +1,72 @@ +// 引入unocss css +import '@/plugins/unocss' + +// 导入全局的svg图标 +import '@/plugins/svgIcon' + +// 初始化多语言 +import { setupI18n } from '@/plugins/vueI18n' + +// 引入状态管理 +import { setupStore } from '@/store' + +// 全局组件 +import { setupGlobCom } from '@/components' + +// 引入 element-plus +import { setupElementPlus } from '@/plugins/elementPlus' + +// 引入 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 '@/plugins/tongji' // 百度统计 +import Logger from '@/utils/Logger' + +import VueDOMPurifyHTML from 'vue-dompurify-html' // 解决v-html 的安全隐患 + +// 创建实例 +const setupAll = async () => { + const app = createApp(App) + + await setupI18n(app) + + setupStore(app) + + setupGlobCom(app) + + setupElementPlus(app) + + setupFormCreate(app) + + setupRouter(app) + + setupAuth(app) + + await router.isReady() + + app.use(VueDOMPurifyHTML) + + app.mount('#app') +} + +setupAll() + +Logger.prettyPrimary(`欢迎使用`, import.meta.env.VITE_APP_TITLE) diff --git a/yunxi-ui-admin-vue3/src/permission.ts b/yunxi-ui-admin-vue3/src/permission.ts new file mode 100644 index 00000000..0698dc88 --- /dev/null +++ b/yunxi-ui-admin-vue3/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) { + await 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/yunxi-ui-admin-vue3/src/plugins/animate.css/index.ts b/yunxi-ui-admin-vue3/src/plugins/animate.css/index.ts new file mode 100644 index 00000000..3e934513 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/plugins/animate.css/index.ts @@ -0,0 +1 @@ +import 'animate.css' diff --git a/yunxi-ui-admin-vue3/src/plugins/echarts/index.ts b/yunxi-ui-admin-vue3/src/plugins/echarts/index.ts new file mode 100644 index 00000000..c7ad7a17 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/plugins/echarts/index.ts @@ -0,0 +1,47 @@ +import * as echarts from 'echarts/core' + +import { + BarChart, + LineChart, + PieChart, + MapChart, + PictorialBarChart, + RadarChart, + GaugeChart +} from 'echarts/charts' + +import { + TitleComponent, + TooltipComponent, + GridComponent, + PolarComponent, + AriaComponent, + ParallelComponent, + LegendComponent, + ToolboxComponent, + VisualMapComponent +} from 'echarts/components' + +import { CanvasRenderer } from 'echarts/renderers' + +echarts.use([ + LegendComponent, + TitleComponent, + TooltipComponent, + ToolboxComponent, + GridComponent, + PolarComponent, + AriaComponent, + ParallelComponent, + VisualMapComponent, + BarChart, + LineChart, + PieChart, + MapChart, + CanvasRenderer, + PictorialBarChart, + RadarChart, + GaugeChart +]) + +export default echarts diff --git a/yunxi-ui-admin-vue3/src/plugins/elementPlus/index.ts b/yunxi-ui-admin-vue3/src/plugins/elementPlus/index.ts new file mode 100644 index 00000000..0ae2a8b2 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/plugins/formCreate/index.ts b/yunxi-ui-admin-vue3/src/plugins/formCreate/index.ts new file mode 100644 index 00000000..a6cb8213 --- /dev/null +++ b/yunxi-ui-admin-vue3/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, + ElTable, + ElTableColumn, + ElTabPane +} from 'element-plus' + +import formCreate from '@form-create/element-ui' +import install from '@form-create/element-ui/auto-import' + +const components = [ + ElAside, + ElPopconfirm, + ElHeader, + ElMain, + ElContainer, + ElDivider, + ElTransfer, + ElAlert, + ElTabs, + ElTable, + ElTableColumn, + ElTabPane +] + +// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档 +export const setupFormCreate = (app: App) => { + components.forEach((component) => { + app.component(component.name, component) + }) + formCreate.use(install) + app.use(formCreate) +} diff --git a/yunxi-ui-admin-vue3/src/plugins/svgIcon/index.ts b/yunxi-ui-admin-vue3/src/plugins/svgIcon/index.ts new file mode 100644 index 00000000..b5b7f70d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/plugins/svgIcon/index.ts @@ -0,0 +1,3 @@ +import 'virtual:svg-icons-register' + +import '@purge-icons/generated' diff --git a/yunxi-ui-admin-vue3/src/plugins/tongji/index.ts b/yunxi-ui-admin-vue3/src/plugins/tongji/index.ts new file mode 100644 index 00000000..ec261a16 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/plugins/tongji/index.ts @@ -0,0 +1,23 @@ +import router from '@/router' + +// 用于 router push +window._hmt = window._hmt || [] +// HM_ID +const HM_ID = import.meta.env.VITE_APP_BAIDU_CODE +;(function () { + // 有值的时候,才开启 + if (!HM_ID) { + return + } + const hm = document.createElement('script') + hm.src = 'https://hm.baidu.com/hm.js?' + HM_ID + const s = document.getElementsByTagName('script')[0] + s.parentNode.insertBefore(hm, s) +})() + +router.afterEach(function (to) { + if (!HM_ID) { + return + } + _hmt.push(['_trackPageview', to.fullPath]) +}) diff --git a/yunxi-ui-admin-vue3/src/plugins/unocss/index.ts b/yunxi-ui-admin-vue3/src/plugins/unocss/index.ts new file mode 100644 index 00000000..d366b5a2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/plugins/unocss/index.ts @@ -0,0 +1 @@ +import 'virtual:uno.css' diff --git a/yunxi-ui-admin-vue3/src/plugins/vueI18n/helper.ts b/yunxi-ui-admin-vue3/src/plugins/vueI18n/helper.ts new file mode 100644 index 00000000..da6bc8c9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/plugins/vueI18n/helper.ts @@ -0,0 +1,3 @@ +export const setHtmlPageLang = (locale: LocaleType) => { + document.querySelector('html')?.setAttribute('lang', locale) +} diff --git a/yunxi-ui-admin-vue3/src/plugins/vueI18n/index.ts b/yunxi-ui-admin-vue3/src/plugins/vueI18n/index.ts new file mode 100644 index 00000000..f845b13f --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/router/index.ts b/yunxi-ui-admin-vue3/src/router/index.ts new file mode 100644 index 00000000..8f66ca31 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/router/index.ts @@ -0,0 +1,28 @@ +import type { App } from 'vue' +import type { RouteRecordRaw } from 'vue-router' +import { createRouter, createWebHistory } from 'vue-router' +import remainingRouter from './modules/remaining' + +// 创建路由实例 +const router = createRouter({ + history: createWebHistory(), // 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/yunxi-ui-admin-vue3/src/router/modules/remaining.ts b/yunxi-ui-admin-vue3/src/router/modules/remaining.ts new file mode 100644 index 00000000..d8172d27 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/router/modules/remaining.ts @@ -0,0 +1,453 @@ +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: '/dict', + component: Layout, + name: 'dict', + meta: { + hidden: true + }, + children: [ + { + path: 'type/data/:dictType', + component: () => import('@/views/system/dict/data/index.vue'), + name: 'SystemDictData', + meta: { + title: '字典数据', + noCache: true, + hidden: true, + canTo: true, + icon: '', + activeMenu: '/system/dict' + } + } + ] + }, + + { + path: '/codegen', + component: Layout, + name: 'CodegenEdit', + meta: { + hidden: true + }, + children: [ + { + path: 'edit', + component: () => import('@/views/infra/codegen/EditTable.vue'), + name: 'InfraCodegenEditTable', + 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/logger/index.vue'), + name: 'InfraJobLog', + 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: '/sso', + component: () => import('@/views/Login/Login.vue'), + name: 'SSOLogin', + 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/editor/index.vue'), + name: 'BpmFormEditor', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '设计流程表单', + activeMenu: '/bpm/manager/form' + } + }, + { + path: '/manager/model/edit', + component: () => import('@/views/bpm/model/editor/index.vue'), + name: 'BpmModelEditor', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '设计流程', + activeMenu: '/bpm/manager/model' + } + }, + { + path: '/manager/definition', + component: () => import('@/views/bpm/definition/index.vue'), + name: 'BpmProcessDefinition', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '流程定义', + activeMenu: '/bpm/manager/model' + } + }, + { + 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/index.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/index.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' + } + }, + { + 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' + } + } + ] + }, + { + path: '/mall/product', // 商品中心 + component: Layout, + meta: { + hidden: true + }, + children: [ + { + path: 'spu/add', + component: () => import('@/views/mall/product/spu/form/index.vue'), + name: 'ProductSpuAdd', + meta: { + noCache: true, + hidden: true, + canTo: true, + icon: 'ep:edit', + title: '商品添加', + activeMenu: '/mall/product/spu' + } + }, + { + path: 'spu/edit/:id(\\d+)', + component: () => import('@/views/mall/product/spu/form/index.vue'), + name: 'ProductSpuEdit', + meta: { + noCache: true, + hidden: true, + canTo: true, + icon: 'ep:edit', + title: '商品编辑', + activeMenu: '/mall/product/spu' + } + }, + { + path: 'spu/detail/:id(\\d+)', + component: () => import('@/views/mall/product/spu/form/index.vue'), + name: 'ProductSpuDetail', + meta: { + noCache: true, + hidden: true, + canTo: true, + icon: 'ep:view', + title: '商品详情', + activeMenu: '/mall/product/spu' + } + }, + { + path: 'property/value/:propertyId(\\d+)', + component: () => import('@/views/mall/product/property/value/index.vue'), + name: 'ProductPropertyValue', + meta: { + noCache: true, + hidden: true, + canTo: true, + icon: 'ep:view', + title: '商品属性值', + activeMenu: '/product/property' + } + } + ] + }, + { + path: '/mall/trade', // 交易中心 + component: Layout, + meta: { + hidden: true + }, + children: [ + { + path: 'order/detail/:id(\\d+)', + component: () => import('@/views/mall/trade/order/detail/index.vue'), + name: 'TradeOrderDetail', + meta: { title: '订单详情', icon: 'ep:view', activeMenu: '/mall/trade/order' } + }, + { + path: 'after-sale/detail/:id(\\d+)', + component: () => import('@/views/mall/trade/afterSale/detail/index.vue'), + name: 'TradeAfterSaleDetail', + meta: { title: '退款详情', icon: 'ep:view', activeMenu: '/mall/trade/after-sale' } + } + ] + }, + { + path: '/member', + component: Layout, + name: 'member', + meta: { hidden: true }, + children: [ + { + path: 'user/detail/:id', + name: 'MemberUserDetail', + meta: { + title: '会员详情', + noCache: true, + hidden: true + }, + component: () => import('@/views/member/user/detail/index.vue') + } + ] + }, + { + path: '/pay', + component: Layout, + name: 'pay', + meta: { hidden: true }, + children: [ + { + path: 'cashier', + name: 'PayCashier', + meta: { + title: '收银台', + noCache: true, + hidden: true + }, + component: () => import('@/views/pay/cashier/index.vue') + } + ] + } +] + +export default remainingRouter diff --git a/yunxi-ui-admin-vue3/src/store/index.ts b/yunxi-ui-admin-vue3/src/store/index.ts new file mode 100644 index 00000000..65964ea8 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/store/modules/app.ts b/yunxi-ui-admin-vue3/src/store/modules/app.ts new file mode 100644 index 00000000..2489f87e --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/store/modules/dict.ts b/yunxi-ui-admin-vue3/src/store/modules/dict.ts new file mode 100644 index 00000000..822547bf --- /dev/null +++ b/yunxi-ui-admin-vue3/src/store/modules/dict.ts @@ -0,0 +1,104 @@ +import { defineStore } from 'pinia' +import { store } from '../index' +// @ts-ignore +import { DictDataVO } from '@/api/system/dict/types' +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' +const { wsCache } = useCache('sessionStorage') +import { listSimpleDictData } 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 listSimpleDictData() + // 设置数据 + 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 listSimpleDictData() + // 设置数据 + 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/yunxi-ui-admin-vue3/src/store/modules/locale.ts b/yunxi-ui-admin-vue3/src/store/modules/locale.ts new file mode 100644 index 00000000..1fc772a7 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/store/modules/permission.ts b/yunxi-ui-admin-vue3/src/store/modules/permission.ts new file mode 100644 index 00000000..c729cea0 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/store/modules/permission.ts @@ -0,0 +1,67 @@ +import { defineStore } from 'pinia' +import { store } from '../index' +import { cloneDeep } from 'lodash-es' +import remainingRouter from '@/router/modules/remaining' +import { flatMultiLevelRoutes, generateRoute } from '@/utils/routerHelper' +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) => { + // 获得菜单列表,它在登录的时候,setUserInfoAction 方法中已经进行获取 + let res: AppCustomRouteRecordRaw[] = [] + if (wsCache.get(CACHE_KEY.ROLE_ROUTERS)) { + res = wsCache.get(CACHE_KEY.ROLE_ROUTERS) as AppCustomRouteRecordRaw[] + } + const routerMap: AppRouteRecordRaw[] = generateRoute(res) + // 动态路由,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/yunxi-ui-admin-vue3/src/store/modules/tagsView.ts b/yunxi-ui-admin-vue3/src/store/modules/tagsView.ts new file mode 100644 index 00000000..a60d0e45 --- /dev/null +++ b/yunxi-ui-admin-vue3/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.delCachedView() + }, + // 删除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.delCachedView() + }, + // 删除所有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/yunxi-ui-admin-vue3/src/store/modules/user.ts b/yunxi-ui-admin-vue3/src/store/modules/user.ts new file mode 100644 index 00000000..1f801fef --- /dev/null +++ b/yunxi-ui-admin-vue3/src/store/modules/user.ts @@ -0,0 +1,84 @@ +import { store } from '../index' +import { defineStore } from 'pinia' +import { getAccessToken, removeToken } from '@/utils/auth' +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' +import { getInfo, loginOut } 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 getInfo() + } + this.permissions = userInfo.permissions + this.roles = userInfo.roles + this.user = userInfo.user + this.isSetUser = true + wsCache.set(CACHE_KEY.USER, userInfo) + wsCache.set(CACHE_KEY.ROLE_ROUTERS, userInfo.menus) + }, + async loginOut() { + await loginOut() + 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/yunxi-ui-admin-vue3/src/styles/global.module.scss b/yunxi-ui-admin-vue3/src/styles/global.module.scss new file mode 100644 index 00000000..8448a924 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/styles/global.module.scss @@ -0,0 +1,6 @@ +@import './variables.scss'; +// 导出变量 +:export { + namespace: $namespace; + elNamespace: $elNamespace; +} diff --git a/yunxi-ui-admin-vue3/src/styles/index.scss b/yunxi-ui-admin-vue3/src/styles/index.scss new file mode 100644 index 00000000..0952bd07 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/styles/index.scss @@ -0,0 +1,35 @@ +@import './var.css'; +@import 'element-plus/theme-chalk/dark/css-vars.css'; + +.reset-margin [class*='el-icon'] + span { + margin-left: 2px !important; +} + +// 解决抽屉弹出时,body宽度变化的问题 +.el-popup-parent--hidden { + width: 100% !important; +} + +// 解决表格内容超过表格总宽度后,横向滚动条前端顶不到表格边缘的问题 +.el-scrollbar__bar { + display: flex; + justify-content: flex-start; +} + +/* 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/yunxi-ui-admin-vue3/src/styles/theme.scss b/yunxi-ui-admin-vue3/src/styles/theme.scss new file mode 100644 index 00000000..39b03b3d --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/styles/var.css b/yunxi-ui-admin-vue3/src/styles/var.css new file mode 100644 index 00000000..63459ba6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/styles/var.css @@ -0,0 +1,66 @@ +:root { + --login-bg-color: #293146; + + --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 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; + + --tags-view-height: 35px; + /* header start */ + + /* tab menu start */ + --tab-menu-max-width: 80px; + + --tab-menu-min-width: 30px; + + --tab-menu-collapse-height: 36px; + /* tab menu end */ + + --app-content-padding: 20px; + + --app-content-bg-color: #f5f7f9; + + --app-footer-height: 50px; + + --transition-time-02: 0.2s; +} + +.dark { + --app-content-bg-color: var(--el-bg-color); +} + +html, +body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/yunxi-ui-admin-vue3/src/styles/variables.scss b/yunxi-ui-admin-vue3/src/styles/variables.scss new file mode 100644 index 00000000..00b66f1f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/styles/variables.scss @@ -0,0 +1,4 @@ +// 命名空间 +$namespace: v; +// el命名空间 +$elNamespace: el; diff --git a/yunxi-ui-admin-vue3/src/types/components.d.ts b/yunxi-ui-admin-vue3/src/types/components.d.ts new file mode 100644 index 00000000..8de1f335 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/types/configGlobal.d.ts b/yunxi-ui-admin-vue3/src/types/configGlobal.d.ts new file mode 100644 index 00000000..f6d7b3c3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/types/configGlobal.d.ts @@ -0,0 +1,4 @@ +import { ElementPlusSize } from './elementPlus' +export interface ConfigGlobalTypes { + size?: ElementPlusSize +} diff --git a/yunxi-ui-admin-vue3/src/types/contextMenu.d.ts b/yunxi-ui-admin-vue3/src/types/contextMenu.d.ts new file mode 100644 index 00000000..0738d0e3 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/types/descriptions.d.ts b/yunxi-ui-admin-vue3/src/types/descriptions.d.ts new file mode 100644 index 00000000..35c0b81b --- /dev/null +++ b/yunxi-ui-admin-vue3/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 // add by 星语:支持时间的格式化 + dictType?: string // add by 星语:支持 dict 字典数据 +} diff --git a/yunxi-ui-admin-vue3/src/types/elementPlus.d.ts b/yunxi-ui-admin-vue3/src/types/elementPlus.d.ts new file mode 100644 index 00000000..2c6b76e7 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/types/form.d.ts b/yunxi-ui-admin-vue3/src/types/form.d.ts new file mode 100644 index 00000000..980c8cc6 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/types/icon.d.ts b/yunxi-ui-admin-vue3/src/types/icon.d.ts new file mode 100644 index 00000000..d1ffcdb5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/types/icon.d.ts @@ -0,0 +1,5 @@ +export interface IconTypes { + size?: number + color?: string + icon: string +} diff --git a/yunxi-ui-admin-vue3/src/types/infoTip.d.ts b/yunxi-ui-admin-vue3/src/types/infoTip.d.ts new file mode 100644 index 00000000..6eff083d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/types/infoTip.d.ts @@ -0,0 +1,4 @@ +export interface TipSchema { + label: string + keys?: string[] +} diff --git a/yunxi-ui-admin-vue3/src/types/layout.d.ts b/yunxi-ui-admin-vue3/src/types/layout.d.ts new file mode 100644 index 00000000..cad3e2af --- /dev/null +++ b/yunxi-ui-admin-vue3/src/types/layout.d.ts @@ -0,0 +1 @@ +export type LayoutType = 'classic' | 'topLeft' | 'top' | 'cutMenu' diff --git a/yunxi-ui-admin-vue3/src/types/localeDropdown.d.ts b/yunxi-ui-admin-vue3/src/types/localeDropdown.d.ts new file mode 100644 index 00000000..c749dce7 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/types/qrcode.d.ts b/yunxi-ui-admin-vue3/src/types/qrcode.d.ts new file mode 100644 index 00000000..86cdf0b9 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/types/table.d.ts b/yunxi-ui-admin-vue3/src/types/table.d.ts new file mode 100644 index 00000000..9cb4205b --- /dev/null +++ b/yunxi-ui-admin-vue3/src/types/table.d.ts @@ -0,0 +1,44 @@ +export type TableColumn = { + field: string + label?: string + width?: number | string + fixed?: 'left' | 'right' + 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/yunxi-ui-admin-vue3/src/types/theme.d.ts b/yunxi-ui-admin-vue3/src/types/theme.d.ts new file mode 100644 index 00000000..ad649b02 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/utils/Logger.ts b/yunxi-ui-admin-vue3/src/utils/Logger.ts new file mode 100644 index 00000000..ca58df21 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/utils/auth.ts b/yunxi-ui-admin-vue3/src/utils/auth.ts new file mode 100644 index 00000000..7da49b08 --- /dev/null +++ b/yunxi-ui-admin-vue3/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) + 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/yunxi-ui-admin-vue3/src/utils/color.ts b/yunxi-ui-admin-vue3/src/utils/color.ts new file mode 100644 index 00000000..6888583a --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/utils/constants.ts b/yunxi-ui-admin-vue3/src/utils/constants.ts new file mode 100644 index 00000000..41891a4a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/utils/constants.ts @@ -0,0 +1,416 @@ +/** + * Created by 芋道源码 + * + * 枚举类 + */ + +// ========== COMMON 模块 ========== +// 全局通用状态枚举 +export const CommonStatusEnum = { + ENABLE: 0, // 开启 + DISABLE: 1 // 禁用 +} + +// 全局用户类型枚举 +export const UserTypeEnum = { + MEMBER: 1, // 会员 + ADMIN: 2 // 管理员 +} + +// ========== SYSTEM 模块 ========== +/** + * 菜单的类型枚举 + */ +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 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' + } +} + +// ========== INFRA 模块 ========== +/** + * 代码生成模板类型 + */ +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 // 已忽略 +} + +// ========== PAY 模块 ========== +/** + * 支付渠道枚举 + */ +export const PayChannelEnum = { + WX_PUB: { + code: 'wx_pub', + name: '微信 JSAPI 支付' + }, + WX_LITE: { + code: 'wx_lite', + name: '微信小程序支付' + }, + WX_APP: { + code: 'wx_app', + name: '微信 APP 支付' + }, + WX_BAR: { + code: 'wx_bar', + name: '微信条码支付' + }, + 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: '支付宝扫码支付' + }, + ALIPAY_BAR: { + code: 'alipay_bar', + name: '支付宝条码支付' + }, + MOCK: { + code: 'mock', + name: '模拟支付' + } +} + +/** + * 支付的展示模式每局 + */ +export const PayDisplayModeEnum = { + URL: { + mode: 'url' + }, + IFRAME: { + mode: 'iframe' + }, + FORM: { + mode: 'form' + }, + QR_CODE: { + mode: 'qr_code' + }, + APP: { + mode: 'app' + } +} + +/** + * 支付类型枚举 + */ +export const PayType = { + WECHAT: 'WECHAT', + ALIPAY: 'ALIPAY', + MOCK: 'MOCK' +} + +/** + * 支付订单状态枚举 + */ +export const PayOrderStatusEnum = { + WAITING: { + status: 0, + name: '未支付' + }, + SUCCESS: { + status: 10, + name: '已支付' + }, + CLOSED: { + status: 20, + name: '未支付' + } +} + +// ========== MALL - 商品模块 ========== +/** + * 商品 SPU 状态 + */ +export const ProductSpuStatusEnum = { + RECYCLE: { + status: -1, + name: '回收站' + }, + DISABLE: { + status: 0, + name: '下架' + }, + ENABLE: { + status: 1, + name: '上架' + } +} + +// ========== MALL - 营销模块 ========== +/** + * 优惠劵模板的有限期类型的枚举 + */ +export const CouponTemplateValidityTypeEnum = { + DATE: { + type: 1, + name: '固定日期可用' + }, + TERM: { + type: 2, + name: '领取之后可用' + } +} + +/** + * 优惠劵模板的领取方式的枚举 + */ +export const CouponTemplateTakeTypeEnum = { + USER: { + type: 1, + name: '直接领取' + }, + ADMIN: { + type: 2, + name: '指定发放' + }, + REGISTER: { + type: 3, + name: '新人券' + } +} + +/** + * 营销的商品范围枚举 + */ +export const PromotionProductScopeEnum = { + ALL: { + scope: 1, + name: '通用劵' + }, + SPU: { + scope: 2, + name: '商品劵' + }, + CATEGORY: { + scope: 3, + name: '品类劵' + } +} + +/** + * 营销的条件类型枚举 + */ +export const PromotionConditionTypeEnum = { + PRICE: { + type: 10, + name: '满 N 元' + }, + COUNT: { + type: 20, + name: '满 N 件' + } +} + +/** + * 优惠类型枚举 + */ +export const PromotionDiscountTypeEnum = { + PRICE: { + type: 1, + name: '满减' + }, + PERCENT: { + type: 2, + name: '折扣' + } +} + +// ========== MALL - 交易模块 ========== +/** + * 分销关系绑定模式枚举 + */ +export const BrokerageBindModeEnum = { + ANYTIME: { + mode: 1, + name: '首次绑定' + }, + REGISTER: { + mode: 2, + name: '注册绑定' + }, + OVERRIDE: { + mode: 3, + name: '覆盖绑定' + } +} +/** + * 分佣模式枚举 + */ +export const BrokerageEnabledConditionEnum = { + ALL: { + condition: 1, + name: '人人分销' + }, + ADMIN: { + condition: 2, + name: '指定分销' + } +} +/** + * 佣金记录业务类型枚举 + */ +export const BrokerageRecordBizTypeEnum = { + ORDER: { + type: 1, + name: '获得推广佣金' + }, + WITHDRAW: { + type: 2, + name: '提现申请' + } +} +/** + * 佣金提现状态枚举 + */ +export const BrokerageWithdrawStatusEnum = { + AUDITING: { + status: 0, + name: '审核中' + }, + AUDIT_SUCCESS: { + status: 10, + name: '审核通过' + }, + AUDIT_FAIL: { + status: 20, + name: '审核不通过' + }, + WITHDRAW_SUCCESS: { + status: 11, + name: '提现成功' + }, + WITHDRAW_FAIL: { + status: 21, + name: '提现失败' + } +} +/** + * 佣金提现类型枚举 + */ +export const BrokerageWithdrawTypeEnum = { + WALLET: { + type: 1, + name: '钱包' + }, + BANK: { + type: 2, + name: '银行卡' + }, + WECHAT: { + type: 3, + name: '微信' + }, + ALIPAY: { + type: 4, + name: '支付宝' + } +} + +/** + * 配送方式枚举 + */ +export const DeliveryTypeEnum = { + EXPRESS: { + type: 1, + name: '快递发货' + }, + PICK_UP: { + type: 2, + name: '到店自提' + } +} +/** + * 交易订单 - 状态 + */ +export const TradeOrderStatusEnum = { + UNPAID: { + status: 0, + name: '待支付' + }, + UNDELIVERED: { + status: 10, + name: '待发货' + }, + DELIVERED: { + status: 20, + name: '已发货' + }, + COMPLETED: { + status: 30, + name: '已完成' + }, + CANCELED: { + status: 40, + name: '已取消' + } +} diff --git a/yunxi-ui-admin-vue3/src/utils/dict.ts b/yunxi-ui-admin-vue3/src/utils/dict.ts new file mode 100644 index 00000000..1fc0ab27 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/utils/dict.ts @@ -0,0 +1,186 @@ +/** + * 数据字典工具类 + */ +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): DictDataType[] => { + 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' + }) + }) + return dictOption +} + +/** + * 获取指定字典类型的指定值对应的字典对象 + * @param dictType 字典类型 + * @param value 字典值 + * @return DictDataType 字典对象 + */ +export const getDictObj = (dictType: string, value: any): DictDataType | undefined => { + const dictOptions: DictDataType[] = getDictOptions(dictType) + for (const dict of dictOptions) { + if (dict.value === value + '') { + return dict + } + } +} + +/** + * 获得字典数据的文本展示 + * + * @param dictType 字典类型 + * @param value 字典数据的值 + * @return 字典名称 + */ +export const getDictLabel = (dictType: string, value: any): string => { + const dictOptions: DictDataType[] = getDictOptions(dictType) + const dictLabel = ref('') + dictOptions.forEach((dict: DictDataType) => { + if (dict.value === value + '') { + dictLabel.value = dict.label + } + }) + return dictLabel.value +} + +export enum DICT_TYPE { + USER_TYPE = 'user_type', + COMMON_STATUS = 'common_status', + SYSTEM_TENANT_PACKAGE_ID = 'system_tenant_package_id', + TERMINAL = 'terminal', // 终端 + + // ========== 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_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_FRONT_TYPE = 'infra_codegen_front_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_CODE = 'pay_channel_code', // 支付渠道编码类型 + PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态 + PAY_REFUND_STATUS = 'pay_refund_status', // 退款订单状态 + PAY_NOTIFY_STATUS = 'pay_notify_status', // 商户支付回调状态 + PAY_NOTIFY_TYPE = 'pay_notify_type', // 商户支付回调状态 + + // ========== MP 模块 ========== + MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型 + MP_MESSAGE_TYPE = 'mp_message_type', // 消息类型 + + // ========== MALL - 会员模块 ========== + MEMBER_POINT_BIZ_TYPE = 'member_point_biz_type', // 积分的业务类型 + MEMBER_EXPERIENCE_BIZ_TYPE = 'member_experience_biz_type', // 会员经验业务类型 + + // ========== MALL - 商品模块 ========== + PRODUCT_UNIT = 'product_unit', // 商品单位 + PRODUCT_SPU_STATUS = 'product_spu_status', //商品状态 + + // ========== MALL - 交易模块 ========== + EXPRESS_CHARGE_MODE = 'trade_delivery_express_charge_mode', //快递的计费方式 + TRADE_AFTER_SALE_STATUS = 'trade_after_sale_status', // 售后 - 状态 + TRADE_AFTER_SALE_WAY = 'trade_after_sale_way', // 售后 - 方式 + TRADE_AFTER_SALE_TYPE = 'trade_after_sale_type', // 售后 - 类型 + TRADE_ORDER_TYPE = 'trade_order_type', // 订单 - 类型 + TRADE_ORDER_STATUS = 'trade_order_status', // 订单 - 状态 + TRADE_ORDER_ITEM_AFTER_SALE_STATUS = 'trade_order_item_after_sale_status', // 订单项 - 售后状态 + TRADE_DELIVERY_TYPE = 'trade_delivery_type', // 配送方式 + BROKERAGE_ENABLED_CONDITION = 'brokerage_enabled_condition', // 分佣模式 + BROKERAGE_BIND_MODE = 'brokerage_bind_mode', // 分销关系绑定模式 + BROKERAGE_BANK_NAME = 'brokerage_bank_name', // 佣金提现银行 + BROKERAGE_WITHDRAW_TYPE = 'brokerage_withdraw_type', // 佣金提现类型 + BROKERAGE_RECORD_BIZ_TYPE = 'brokerage_record_biz_type', // 佣金业务类型 + BROKERAGE_RECORD_STATUS = 'brokerage_record_status', // 佣金状态 + BROKERAGE_WITHDRAW_STATUS = 'brokerage_withdraw_status', // 佣金提现状态 + + // ========== MALL - 营销模块 ========== + PROMOTION_DISCOUNT_TYPE = 'promotion_discount_type', // 优惠类型 + PROMOTION_PRODUCT_SCOPE = 'promotion_product_scope', // 营销的商品范围 + PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE = 'promotion_coupon_template_validity_type', // 优惠劵模板的有限期类型 + PROMOTION_COUPON_STATUS = 'promotion_coupon_status', // 优惠劵的状态 + PROMOTION_COUPON_TAKE_TYPE = 'promotion_coupon_take_type', // 优惠劵的领取方式 + PROMOTION_ACTIVITY_STATUS = 'promotion_activity_status', // 优惠活动的状态 + PROMOTION_CONDITION_TYPE = 'promotion_condition_type', // 营销的条件类型枚举 + PROMOTION_BARGAIN_RECORD_STATUS = 'promotion_bargain_record_status', // 砍价记录的状态 + PROMOTION_COMBINATION_RECORD_STATUS = 'promotion_combination_record_status' // 拼团记录的状态 +} diff --git a/yunxi-ui-admin-vue3/src/utils/domUtils.ts b/yunxi-ui-admin-vue3/src/utils/domUtils.ts new file mode 100644 index 00000000..dbc1989c --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/utils/download.ts b/yunxi-ui-admin-vue3/src/utils/download.ts new file mode 100644 index 00000000..ab200149 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/utils/filt.ts b/yunxi-ui-admin-vue3/src/utils/filt.ts new file mode 100644 index 00000000..b1a7b2c7 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/utils/formCreate.ts b/yunxi-ui-admin-vue3/src/utils/formCreate.ts new file mode 100644 index 00000000..6d7dbc7f --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/utils/formRules.ts b/yunxi-ui-admin-vue3/src/utils/formRules.ts new file mode 100644 index 00000000..2989867f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/utils/formRules.ts @@ -0,0 +1,7 @@ +const { t } = useI18n() + +// 必填项 +export const required = { + required: true, + message: t('common.required') +} diff --git a/yunxi-ui-admin-vue3/src/utils/formatTime.ts b/yunxi-ui-admin-vue3/src/utils/formatTime.ts new file mode 100644 index 00000000..53ccda11 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/utils/formatTime.ts @@ -0,0 +1,339 @@ +import dayjs from 'dayjs' + +/** + * 日期快捷选项适用于 el-date-picker + */ +export const defaultShortcuts = [ + { + text: '今天', + value: () => { + return new Date() + } + }, + { + text: '昨天', + value: () => { + const date = new Date() + date.setTime(date.getTime() - 3600 * 1000 * 24) + return [date, date] + } + }, + { + text: '最近七天', + value: () => { + const date = new Date() + date.setTime(date.getTime() - 3600 * 1000 * 24 * 7) + return [date, new Date()] + } + }, + { + text: '最近 30 天', + value: () => { + const date = new Date() + date.setTime(date.getTime() - 3600 * 1000 * 24 * 30) + return [date, new Date()] + } + }, + { + text: '本月', + value: () => { + const date = new Date() + date.setDate(1) // 设置为当前月的第一天 + return [date, new Date()] + } + }, + { + text: '今年', + value: () => { + const date = new Date() + return [new Date(`${date.getFullYear()}-01-01`), date] + } + } +] + +/** + * 时间日期转换 + * @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: dayjs.ConfigType, format?: string): string { + // 日期不存在,则返回空 + if (!date) { + return '' + } + // 日期存在,则进行格式化 + if (format === undefined) { + format = 'YYYY-MM-DD HH:mm:ss' + } + return dayjs(date).format(format) +} + +/** + * 获取当前的日期+时间 + */ +export function getNowDateTime() { + return dayjs() +} + +/** + * 获取当前日期是第几周 + * @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) + return Math.ceil(d / 7) +} + +/** + * 将时间转换为 `几秒前`、`几分钟前`、`几小时前`、`几天前` + * @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 + '秒' + } +} + +/** + * element plus 的时间 Formatter 实现,使用 YYYY-MM-DD HH:mm:ss 格式 + * + * @param row 行数据 + * @param column 字段 + * @param cellValue 字段值 + */ +// @ts-ignore +export const dateFormatter = (row, column, cellValue) => { + if (!cellValue) { + return + } + return formatDate(cellValue) +} + +/** + * element plus 的时间 Formatter 实现,使用 YYYY-MM-DD 格式 + * + * @param row 行数据 + * @param column 字段 + * @param cellValue 字段值 + */ +// @ts-ignore +export const dateFormatter2 = (row, column, cellValue) => { + if (!cellValue) { + return + } + return formatDate(cellValue, 'YYYY-MM-DD') +} + +/** + * 设置起始日期,时间为00:00:00 + * @param param 传入日期 + * @returns 带时间00:00:00的日期 + */ +export function beginOfDay(param: Date) { + return new Date(param.getFullYear(), param.getMonth(), param.getDate(), 0, 0, 0) +} + +/** + * 设置结束日期,时间为23:59:59 + * @param param 传入日期 + * @returns 带时间23:59:59的日期 + */ +export function endOfDay(param: Date) { + return new Date(param.getFullYear(), param.getMonth(), param.getDate(), 23, 59, 59) +} + +/** + * 计算两个日期间隔天数 + * @param param1 日期1 + * @param param2 日期2 + */ +export function betweenDay(param1: Date, param2: Date) { + param1 = convertDate(param1) + param2 = convertDate(param2) + // 计算差值 + return Math.floor((param2.getTime() - param1.getTime()) / (24 * 3600 * 1000)) +} + +/** + * 日期计算 + * @param param1 日期 + * @param param2 添加的时间 + */ +export function addTime(param1: Date, param2: number) { + param1 = convertDate(param1) + return new Date(param1.getTime() + param2) +} + +/** + * 日期转换 + * @param param 日期 + */ +export function convertDate(param: Date | string) { + if (typeof param === 'string') { + return new Date(param) + } + return param +} + +/** + * 指定的两个日期, 是否为同一天 + * @param a 日期 A + * @param b 日期 B + */ +export function isSameDay(a: dayjs.ConfigType, b: dayjs.ConfigType): boolean { + if (!a || !b) return false + + const aa = dayjs(a) + const bb = dayjs(b) + return aa.year() == bb.year() && aa.month() == bb.month() && aa.day() == bb.day() +} + +/** + * 获取一天的开始时间、截止时间 + * @param date 日期 + * @param days 天数 + */ +export function getDayRange( + date: dayjs.ConfigType, + days: number +): [dayjs.ConfigType, dayjs.ConfigType] { + const day = dayjs(date).add(days, 'd') + return getDateRange(day, day) +} + +/** + * 获取最近7天的开始时间、截止时间 + */ +export function getLast7Days(): [dayjs.ConfigType, dayjs.ConfigType] { + const lastWeekDay = dayjs().subtract(7, 'd') + const yesterday = dayjs().subtract(1, 'd') + return getDateRange(lastWeekDay, yesterday) +} + +/** + * 获取最近30天的开始时间、截止时间 + */ +export function getLast30Days(): [dayjs.ConfigType, dayjs.ConfigType] { + const lastMonthDay = dayjs().subtract(30, 'd') + const yesterday = dayjs().subtract(1, 'd') + return getDateRange(lastMonthDay, yesterday) +} + +/** + * 获取最近1年的开始时间、截止时间 + */ +export function getLast1Year(): [dayjs.ConfigType, dayjs.ConfigType] { + const lastYearDay = dayjs().subtract(1, 'y') + const yesterday = dayjs().subtract(1, 'd') + return getDateRange(lastYearDay, yesterday) +} + +/** + * 获取指定日期的开始时间、截止时间 + * @param beginDate 开始日期 + * @param endDate 截止日期 + */ +export function getDateRange( + beginDate: dayjs.ConfigType, + endDate: dayjs.ConfigType +): [dayjs.ConfigType, dayjs.ConfigType] { + return [dayjs(beginDate).startOf('d'), dayjs(endDate).endOf('d')] +} diff --git a/yunxi-ui-admin-vue3/src/utils/formatter.ts b/yunxi-ui-admin-vue3/src/utils/formatter.ts new file mode 100644 index 00000000..8777f322 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/utils/formatter.ts @@ -0,0 +1,7 @@ +import { floatToFixed2 } from '@/utils' + +// 格式化金额【分转元】 +// @ts-ignore +export const fenToYuanFormat = (_, __, cellValue: any, ___) => { + return `¥${floatToFixed2(cellValue)}` +} diff --git a/yunxi-ui-admin-vue3/src/utils/index.ts b/yunxi-ui-admin-vue3/src/utils/index.ts new file mode 100644 index 00000000..6c9a5df2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/utils/index.ts @@ -0,0 +1,235 @@ +/** + * + * @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) + }) +} + +/** + * element plus 的文件大小 Formatter 实现 + * + * @param row 行数据 + * @param column 字段 + * @param cellValue 字段值 + */ +// @ts-ignore +export const fileSizeFormatter = (row, column, cellValue) => { + const fileSize = cellValue + const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + const srcSize = parseFloat(fileSize) + const index = Math.floor(Math.log(srcSize) / Math.log(1024)) + const size = srcSize / Math.pow(1024, index) + const sizeStr = size.toFixed(2) //保留的小数位数 + return sizeStr + ' ' + unitArr[index] +} + +/** + * 将值复制到目标对象,且以目标对象属性为准,例:target: {a:1} source:{a:2,b:3} 结果为:{a:2} + * @param target 目标对象 + * @param source 源对象 + */ +export const copyValueToTarget = (target, source) => { + const newObj = Object.assign({}, target, source) + // 删除多余属性 + Object.keys(newObj).forEach((key) => { + // 如果不是target中的属性则删除 + if (Object.keys(target).indexOf(key) === -1) { + delete newObj[key] + } + }) + // 更新目标对象值 + Object.assign(target, newObj) +} + +/** + * 将一个整数转换为分数保留两位小数 + * @param num + */ +export const formatToFraction = (num: number | string | undefined): number => { + if (typeof num === 'undefined') return 0 + const parsedNumber = typeof num === 'string' ? parseFloat(num) : num + return parseFloat((parsedNumber / 100).toFixed(2)) +} + +/** + * 将一个数转换为 1.00 这样 + * 数据呈现的时候使用 + * + * @param num 整数 + */ +export const floatToFixed2 = (num: number | string | undefined): string => { + let str = '0.00' + if (typeof num === 'undefined') { + return str + } + const f = formatToFraction(num) + const decimalPart = f.toString().split('.')[1] + const len = decimalPart ? decimalPart.length : 0 + switch (len) { + case 0: + str = f.toString() + '.00' + break + case 1: + str = f.toString() + '0' + break + } + return str +} + +/** + * 将一个分数转换为整数 + * @param num + */ +export const convertToInteger = (num: number | string | undefined): number => { + if (typeof num === 'undefined') return 0 + const parsedNumber = typeof num === 'string' ? parseFloat(num) : num + // TODO 分转元后还有小数则四舍五入 + return Math.round(parsedNumber * 100) +} + +/** + * 元转分 + */ +export const yuanToFen = (amount: string | number): number => { + return convertToInteger(amount) +} + +/** + * 分转元 + */ +export const fenToYuan = (price: string | number): number => { + return formatToFraction(price) +} diff --git a/yunxi-ui-admin-vue3/src/utils/is.ts b/yunxi-ui-admin-vue3/src/utils/is.ts new file mode 100644 index 00000000..37529859 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/utils/jsencrypt.ts b/yunxi-ui-admin-vue3/src/utils/jsencrypt.ts new file mode 100644 index 00000000..374d5f64 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/utils/permission.ts b/yunxi-ui-admin-vue3/src/utils/permission.ts new file mode 100644 index 00000000..a63ee628 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/utils/permission.ts @@ -0,0 +1,45 @@ +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' + +const { t } = useI18n() // 国际化 + +/** + * 字符权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export function checkPermi(value: string[]) { + if (value && value instanceof Array && value.length > 0) { + const { wsCache } = useCache() + const permissionDatas = value + const all_permission = '*:*:*' + const permissions = wsCache.get(CACHE_KEY.USER).permissions + const hasPermission = permissions.some((permission) => { + return all_permission === permission || permissionDatas.includes(permission) + }) + return !!hasPermission + } else { + console.error(t('permission.hasPermission')) + return false + } +} + +/** + * 角色权限校验 + * @param {string[]} value 校验值 + * @returns {Boolean} + */ +export function checkRole(value: string[]) { + if (value && value instanceof Array && value.length > 0) { + const { wsCache } = useCache() + const permissionRoles = value + const super_admin = 'admin' + const roles = wsCache.get(CACHE_KEY.USER).roles + const hasRole = roles.some((role) => { + return super_admin === role || permissionRoles.includes(role) + }) + return !!hasRole + } else { + console.error(t('permission.hasRole')) + return false + } +} diff --git a/yunxi-ui-admin-vue3/src/utils/propTypes.ts b/yunxi-ui-admin-vue3/src/utils/propTypes.ts new file mode 100644 index 00000000..fb8f84e7 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/utils/routerHelper.ts b/yunxi-ui-admin-vue3/src/utils/routerHelper.ts new file mode 100644 index 00000000..d9fe42aa --- /dev/null +++ b/yunxi-ui-admin-vue3/src/utils/routerHelper.ts @@ -0,0 +1,241 @@ +import type { RouteLocationNormalized, Router, RouteRecordNormalized } from 'vue-router' +import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' +import { isUrl } from '@/utils/is' +import { cloneDeep, omit } from 'lodash-es' + +const modules = import.meta.glob('../views/**/*.{vue,tsx}') +/** + * 注册一个异步组件 + * @param componentPath 例:/bpm/oa/leave/detail + */ +export const registerComponent = (componentPath: string) => { + for (const item in modules) { + if (item.includes(componentPath)) { + // 使用异步组件的方式来动态加载组件 + // @ts-ignore + return defineAsyncComponent(modules[item]) + } + } +} +/* 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: + route.componentName && route.componentName.length > 0 + ? route.componentName + : 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 || '') + .replace(/-(.)/g, function (group1: string) { + return group1.toUpperCase() + }) + .replaceAll('-', '') + + if (upperCaseFirst && str) { + str = str.charAt(0).toUpperCase() + str.slice(1) + } + + return str +} diff --git a/yunxi-ui-admin-vue3/src/utils/tree.ts b/yunxi-ui-admin-vue3/src/utils/tree.ts new file mode 100644 index 00000000..91059ef9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/utils/tree.ts @@ -0,0 +1,400 @@ +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', + isLeaf: 'leaf', + emitPath: false // 用于 cascader 组件:在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值 +} + +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 + */ +// @ts-ignore +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 +} + +/** + * 校验选中的节点,是否为指定 level + * + * @param tree 要操作的树结构数据 + * @param nodeId 需要判断在什么层级的数据 + * @param level 检查的级别, 默认检查到二级 + * @return true 是;false 否 + */ +export const checkSelectedNode = (tree: any[], nodeId: any, level = 2): boolean => { + if (typeof tree === 'undefined' || !Array.isArray(tree) || tree.length === 0) { + console.warn('tree must be an array') + return false + } + + // 校验是否是一级节点 + if (tree.some((item) => item.id === nodeId)) { + return false + } + + // 递归计数 + let count = 1 + + // 深层次校验 + function performAThoroughValidation(arr: any[]): boolean { + count += 1 + for (const item of arr) { + if (item.id === nodeId) { + return true + } else if (typeof item.children !== 'undefined' && item.children.length !== 0) { + if (performAThoroughValidation(item.children)) { + return true + } + } + } + return false + } + + for (const item of tree) { + count = 1 + if (performAThoroughValidation(item.children)) { + // 找到后对比是否是期望的层级 + if (count >= level) { + return true + } + } + } + + return false +} + +/** + * 获取节点的完整结构 + * @param tree 树数据 + * @param nodeId 节点 id + */ +export const treeToString = (tree: any[], nodeId) => { + if (typeof tree === 'undefined' || !Array.isArray(tree) || tree.length === 0) { + console.warn('tree must be an array') + return '' + } + // 校验是否是一级节点 + const node = tree.find((item) => item.id === nodeId) + if (typeof node !== 'undefined') { + return node.name + } + let str = '' + + function performAThoroughValidation(arr) { + for (const item of arr) { + if (item.id === nodeId) { + str += ` / ${item.name}` + return true + } else if (typeof item.children !== 'undefined' && item.children.length !== 0) { + str += ` / ${item.name}` + if (performAThoroughValidation(item.children)) { + return true + } + } + } + return false + } + + for (const item of tree) { + str = `${item.name}` + if (performAThoroughValidation(item.children)) { + break + } + } + return str +} diff --git a/yunxi-ui-admin-vue3/src/utils/tsxHelper.ts b/yunxi-ui-admin-vue3/src/utils/tsxHelper.ts new file mode 100644 index 00000000..6087fa30 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/views/Error/403.vue b/yunxi-ui-admin-vue3/src/views/Error/403.vue new file mode 100644 index 00000000..a3ec4877 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Error/403.vue @@ -0,0 +1,8 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Error/404.vue b/yunxi-ui-admin-vue3/src/views/Error/404.vue new file mode 100644 index 00000000..f6a08de2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Error/404.vue @@ -0,0 +1,7 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Error/500.vue b/yunxi-ui-admin-vue3/src/views/Error/500.vue new file mode 100644 index 00000000..998487d2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Error/500.vue @@ -0,0 +1,7 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Home/Index.vue b/yunxi-ui-admin-vue3/src/views/Home/Index.vue new file mode 100644 index 00000000..121ec6a8 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Home/Index.vue @@ -0,0 +1,381 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Home/Index2.vue b/yunxi-ui-admin-vue3/src/views/Home/Index2.vue new file mode 100644 index 00000000..c9429ab1 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Home/Index2.vue @@ -0,0 +1,319 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/Home/echarts-data.ts b/yunxi-ui-admin-vue3/src/views/Home/echarts-data.ts new file mode 100644 index 00000000..56093f4b --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/views/Home/types.ts b/yunxi-ui-admin-vue3/src/views/Home/types.ts new file mode 100644 index 00000000..e6313d36 --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/views/Login/Login.vue b/yunxi-ui-admin-vue3/src/views/Login/Login.vue new file mode 100644 index 00000000..19ffe2d7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Login/Login.vue @@ -0,0 +1,104 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/Login/components/LoginForm.vue b/yunxi-ui-admin-vue3/src/views/Login/components/LoginForm.vue new file mode 100644 index 00000000..a4eb0b92 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Login/components/LoginForm.vue @@ -0,0 +1,328 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/Login/components/LoginFormTitle.vue b/yunxi-ui-admin-vue3/src/views/Login/components/LoginFormTitle.vue new file mode 100644 index 00000000..cdf4facc --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Login/components/LoginFormTitle.vue @@ -0,0 +1,26 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Login/components/MobileForm.vue b/yunxi-ui-admin-vue3/src/views/Login/components/MobileForm.vue new file mode 100644 index 00000000..ff9069f4 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Login/components/MobileForm.vue @@ -0,0 +1,225 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/Login/components/QrCodeForm.vue b/yunxi-ui-admin-vue3/src/views/Login/components/QrCodeForm.vue new file mode 100644 index 00000000..31d28453 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Login/components/QrCodeForm.vue @@ -0,0 +1,30 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Login/components/RegisterForm.vue b/yunxi-ui-admin-vue3/src/views/Login/components/RegisterForm.vue new file mode 100644 index 00000000..23b3bd42 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Login/components/RegisterForm.vue @@ -0,0 +1,142 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Login/components/SSOLogin.vue b/yunxi-ui-admin-vue3/src/views/Login/components/SSOLogin.vue new file mode 100644 index 00000000..f31ab0e5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Login/components/SSOLogin.vue @@ -0,0 +1,199 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Login/components/index.ts b/yunxi-ui-admin-vue3/src/views/Login/components/index.ts new file mode 100644 index 00000000..204ad73d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Login/components/index.ts @@ -0,0 +1,8 @@ +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' +import SSOLoginVue from './SSOLogin.vue' + +export { LoginForm, MobileForm, LoginFormTitle, RegisterForm, QrCodeForm, SSOLoginVue } diff --git a/yunxi-ui-admin-vue3/src/views/Login/components/useLogin.ts b/yunxi-ui-admin-vue3/src/views/Login/components/useLogin.ts new file mode 100644 index 00000000..b4a02f8f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Login/components/useLogin.ts @@ -0,0 +1,42 @@ +import { Ref } from 'vue' + +export enum LoginStateEnum { + LOGIN, + REGISTER, + RESET_PASSWORD, + MOBILE, + QR_CODE, + SSO +} + +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/yunxi-ui-admin-vue3/src/views/Profile/Index.vue b/yunxi-ui-admin-vue3/src/views/Profile/Index.vue new file mode 100644 index 00000000..b05f93cc --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Profile/Index.vue @@ -0,0 +1,64 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/Profile/components/BasicInfo.vue b/yunxi-ui-admin-vue3/src/views/Profile/components/BasicInfo.vue new file mode 100644 index 00000000..e2189b15 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Profile/components/BasicInfo.vue @@ -0,0 +1,92 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Profile/components/ProfileUser.vue b/yunxi-ui-admin-vue3/src/views/Profile/components/ProfileUser.vue new file mode 100644 index 00000000..b493499f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Profile/components/ProfileUser.vue @@ -0,0 +1,99 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/Profile/components/ResetPwd.vue b/yunxi-ui-admin-vue3/src/views/Profile/components/ResetPwd.vue new file mode 100644 index 00000000..477be91f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Profile/components/ResetPwd.vue @@ -0,0 +1,73 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Profile/components/UserAvatar.vue b/yunxi-ui-admin-vue3/src/views/Profile/components/UserAvatar.vue new file mode 100644 index 00000000..c20168fa --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Profile/components/UserAvatar.vue @@ -0,0 +1,39 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/Profile/components/UserSocial.vue b/yunxi-ui-admin-vue3/src/views/Profile/components/UserSocial.vue new file mode 100644 index 00000000..2f021abf --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Profile/components/UserSocial.vue @@ -0,0 +1,94 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/Profile/components/index.ts b/yunxi-ui-admin-vue3/src/views/Profile/components/index.ts new file mode 100644 index 00000000..9e1883cf --- /dev/null +++ b/yunxi-ui-admin-vue3/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/yunxi-ui-admin-vue3/src/views/Redirect/Redirect.vue b/yunxi-ui-admin-vue3/src/views/Redirect/Redirect.vue new file mode 100644 index 00000000..f7717ce7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/Redirect/Redirect.vue @@ -0,0 +1,28 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/definition/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/definition/index.vue new file mode 100644 index 00000000..1c179e78 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/definition/index.vue @@ -0,0 +1,174 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/form/editor/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/form/editor/index.vue new file mode 100644 index 00000000..b7c45cab --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/form/editor/index.vue @@ -0,0 +1,119 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/form/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/form/index.vue new file mode 100644 index 00000000..0cdf0456 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/form/index.vue @@ -0,0 +1,193 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/group/UserGroupForm.vue b/yunxi-ui-admin-vue3/src/views/bpm/group/UserGroupForm.vue new file mode 100644 index 00000000..35d833ea --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/group/UserGroupForm.vue @@ -0,0 +1,132 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/group/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/group/index.vue new file mode 100644 index 00000000..957ffc89 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/group/index.vue @@ -0,0 +1,189 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/model/ModelForm.vue b/yunxi-ui-admin-vue3/src/views/bpm/model/ModelForm.vue new file mode 100644 index 00000000..0bd54091 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/model/ModelForm.vue @@ -0,0 +1,230 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/model/ModelImportForm.vue b/yunxi-ui-admin-vue3/src/views/bpm/model/ModelImportForm.vue new file mode 100644 index 00000000..74f10ffd --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/model/ModelImportForm.vue @@ -0,0 +1,140 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/model/editor/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/model/editor/index.vue new file mode 100644 index 00000000..f5c0ec6e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/model/editor/index.vue @@ -0,0 +1,105 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/model/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/model/index.vue new file mode 100644 index 00000000..e5da7519 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/model/index.vue @@ -0,0 +1,404 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/oa/leave/create.vue b/yunxi-ui-admin-vue3/src/views/bpm/oa/leave/create.vue new file mode 100644 index 00000000..a47228c9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/oa/leave/create.vue @@ -0,0 +1,89 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/oa/leave/detail.vue b/yunxi-ui-admin-vue3/src/views/bpm/oa/leave/detail.vue new file mode 100644 index 00000000..87036d8e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/oa/leave/detail.vue @@ -0,0 +1,51 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/oa/leave/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/oa/leave/index.vue new file mode 100644 index 00000000..72966db8 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/oa/leave/index.vue @@ -0,0 +1,235 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/processInstance/create/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/create/index.vue new file mode 100644 index 00000000..a10e0208 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/create/index.vue @@ -0,0 +1,133 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/ProcessInstanceBpmnViewer.vue b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/ProcessInstanceBpmnViewer.vue new file mode 100644 index 00000000..0a2057dd --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/ProcessInstanceBpmnViewer.vue @@ -0,0 +1,57 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/ProcessInstanceTaskList.vue b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/ProcessInstanceTaskList.vue new file mode 100644 index 00000000..6f4557ae --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/ProcessInstanceTaskList.vue @@ -0,0 +1,100 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskDelegateForm.vue b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskDelegateForm.vue new file mode 100644 index 00000000..dc757a0c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskDelegateForm.vue @@ -0,0 +1,86 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskReturnDialogForm.vue b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskReturnDialogForm.vue new file mode 100644 index 00000000..f93bf2c5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskReturnDialogForm.vue @@ -0,0 +1,90 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskUpdateAssigneeForm.vue b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskUpdateAssigneeForm.vue new file mode 100644 index 00000000..6adf1de8 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/TaskUpdateAssigneeForm.vue @@ -0,0 +1,83 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/index.vue new file mode 100644 index 00000000..585c60db --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/detail/index.vue @@ -0,0 +1,288 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/processInstance/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/index.vue new file mode 100644 index 00000000..a9dd46e9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/processInstance/index.vue @@ -0,0 +1,250 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/task/done/TaskDetail.vue b/yunxi-ui-admin-vue3/src/views/bpm/task/done/TaskDetail.vue new file mode 100644 index 00000000..5bc06f19 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/task/done/TaskDetail.vue @@ -0,0 +1,51 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/task/done/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/task/done/index.vue new file mode 100644 index 00000000..d2033d5d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/task/done/index.vue @@ -0,0 +1,148 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/task/todo/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/task/todo/index.vue new file mode 100644 index 00000000..c8876887 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/task/todo/index.vue @@ -0,0 +1,137 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/taskAssignRule/TaskAssignRuleForm.vue b/yunxi-ui-admin-vue3/src/views/bpm/taskAssignRule/TaskAssignRuleForm.vue new file mode 100644 index 00000000..9b215e0f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/taskAssignRule/TaskAssignRuleForm.vue @@ -0,0 +1,250 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/bpm/taskAssignRule/index.vue b/yunxi-ui-admin-vue3/src/views/bpm/taskAssignRule/index.vue new file mode 100644 index 00000000..0fe9bde6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/bpm/taskAssignRule/index.vue @@ -0,0 +1,136 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/apiAccessLog/ApiAccessLogDetail.vue b/yunxi-ui-admin-vue3/src/views/infra/apiAccessLog/ApiAccessLogDetail.vue new file mode 100644 index 00000000..43a34dc4 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/apiAccessLog/ApiAccessLogDetail.vue @@ -0,0 +1,67 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/apiAccessLog/index.vue b/yunxi-ui-admin-vue3/src/views/infra/apiAccessLog/index.vue new file mode 100644 index 00000000..b196da11 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/apiAccessLog/index.vue @@ -0,0 +1,219 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/apiErrorLog/ApiErrorLogDetail.vue b/yunxi-ui-admin-vue3/src/views/infra/apiErrorLog/ApiErrorLogDetail.vue new file mode 100644 index 00000000..7340ca82 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/apiErrorLog/ApiErrorLogDetail.vue @@ -0,0 +1,81 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/apiErrorLog/index.vue b/yunxi-ui-admin-vue3/src/views/infra/apiErrorLog/index.vue new file mode 100644 index 00000000..ca145a76 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/apiErrorLog/index.vue @@ -0,0 +1,252 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/build/index.vue b/yunxi-ui-admin-vue3/src/views/infra/build/index.vue new file mode 100644 index 00000000..11bfc999 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/build/index.vue @@ -0,0 +1,143 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/codegen/EditTable.vue b/yunxi-ui-admin-vue3/src/views/infra/codegen/EditTable.vue new file mode 100644 index 00000000..9c4e7657 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/codegen/EditTable.vue @@ -0,0 +1,83 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/codegen/ImportTable.vue b/yunxi-ui-admin-vue3/src/views/infra/codegen/ImportTable.vue new file mode 100644 index 00000000..6cd4610a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/codegen/ImportTable.vue @@ -0,0 +1,151 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/codegen/PreviewCode.vue b/yunxi-ui-admin-vue3/src/views/infra/codegen/PreviewCode.vue new file mode 100644 index 00000000..b04775ec --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/codegen/PreviewCode.vue @@ -0,0 +1,222 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/codegen/components/BasicInfoForm.vue b/yunxi-ui-admin-vue3/src/views/infra/codegen/components/BasicInfoForm.vue new file mode 100644 index 00000000..18593004 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/codegen/components/BasicInfoForm.vue @@ -0,0 +1,87 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/codegen/components/ColumInfoForm.vue b/yunxi-ui-admin-vue3/src/views/infra/codegen/components/ColumInfoForm.vue new file mode 100644 index 00000000..737c2e2b --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/codegen/components/ColumInfoForm.vue @@ -0,0 +1,153 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/codegen/components/GenerateInfoForm.vue b/yunxi-ui-admin-vue3/src/views/infra/codegen/components/GenerateInfoForm.vue new file mode 100644 index 00000000..744edfe6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/codegen/components/GenerateInfoForm.vue @@ -0,0 +1,391 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/codegen/components/index.ts b/yunxi-ui-admin-vue3/src/views/infra/codegen/components/index.ts new file mode 100644 index 00000000..1634a76f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/codegen/components/index.ts @@ -0,0 +1,4 @@ +import BasicInfoForm from './BasicInfoForm.vue' +import ColumInfoForm from './ColumInfoForm.vue' +import GenerateInfoForm from './GenerateInfoForm.vue' +export { BasicInfoForm, ColumInfoForm, GenerateInfoForm } diff --git a/yunxi-ui-admin-vue3/src/views/infra/codegen/index.vue b/yunxi-ui-admin-vue3/src/views/infra/codegen/index.vue new file mode 100644 index 00000000..e06281c7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/codegen/index.vue @@ -0,0 +1,256 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/config/ConfigForm.vue b/yunxi-ui-admin-vue3/src/views/infra/config/ConfigForm.vue new file mode 100644 index 00000000..19b5bf1e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/config/ConfigForm.vue @@ -0,0 +1,131 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/config/index.vue b/yunxi-ui-admin-vue3/src/views/infra/config/index.vue new file mode 100644 index 00000000..c7838c27 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/config/index.vue @@ -0,0 +1,228 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/dataSourceConfig/DataSourceConfigForm.vue b/yunxi-ui-admin-vue3/src/views/infra/dataSourceConfig/DataSourceConfigForm.vue new file mode 100644 index 00000000..e2a4eaab --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/dataSourceConfig/DataSourceConfigForm.vue @@ -0,0 +1,111 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/dataSourceConfig/index.vue b/yunxi-ui-admin-vue3/src/views/infra/dataSourceConfig/index.vue new file mode 100644 index 00000000..92bd3013 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/dataSourceConfig/index.vue @@ -0,0 +1,106 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/infra/dbDoc/index.vue b/yunxi-ui-admin-vue3/src/views/infra/dbDoc/index.vue new file mode 100644 index 00000000..fa9364b0 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/infra/dbDoc/index.vue @@ -0,0 +1,59 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mall/trade/delivery/pickUpStore/index.vue b/yunxi-ui-admin-vue3/src/views/mall/trade/delivery/pickUpStore/index.vue new file mode 100644 index 00000000..6ffb3c42 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mall/trade/delivery/pickUpStore/index.vue @@ -0,0 +1,188 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mall/trade/order/detail/index.vue b/yunxi-ui-admin-vue3/src/views/mall/trade/order/detail/index.vue new file mode 100644 index 00000000..58939dbc --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mall/trade/order/detail/index.vue @@ -0,0 +1,421 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderDeliveryForm.vue b/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderDeliveryForm.vue new file mode 100644 index 00000000..2411f1ce --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderDeliveryForm.vue @@ -0,0 +1,99 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdateAddressForm.vue b/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdateAddressForm.vue new file mode 100644 index 00000000..9c857064 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdateAddressForm.vue @@ -0,0 +1,98 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdatePriceForm.vue b/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdatePriceForm.vue new file mode 100644 index 00000000..eb932ffa --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdatePriceForm.vue @@ -0,0 +1,92 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdateRemarkForm.vue b/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdateRemarkForm.vue new file mode 100644 index 00000000..8be1b603 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mall/trade/order/form/OrderUpdateRemarkForm.vue @@ -0,0 +1,70 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mall/trade/order/index.vue b/yunxi-ui-admin-vue3/src/views/mall/trade/order/index.vue new file mode 100644 index 00000000..33d98548 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mall/trade/order/index.vue @@ -0,0 +1,537 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/config/index.vue b/yunxi-ui-admin-vue3/src/views/member/config/index.vue new file mode 100644 index 00000000..38196690 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/config/index.vue @@ -0,0 +1,119 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/group/GroupForm.vue b/yunxi-ui-admin-vue3/src/views/member/group/GroupForm.vue new file mode 100644 index 00000000..14510b0f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/group/GroupForm.vue @@ -0,0 +1,112 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/group/components/MemberGroupSelect.vue b/yunxi-ui-admin-vue3/src/views/member/group/components/MemberGroupSelect.vue new file mode 100644 index 00000000..78a993a8 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/group/components/MemberGroupSelect.vue @@ -0,0 +1,45 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/group/index.vue b/yunxi-ui-admin-vue3/src/views/member/group/index.vue new file mode 100644 index 00000000..552a72a6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/group/index.vue @@ -0,0 +1,174 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/level/LevelForm.vue b/yunxi-ui-admin-vue3/src/views/member/level/LevelForm.vue new file mode 100644 index 00000000..7e6873cc --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/level/LevelForm.vue @@ -0,0 +1,175 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/level/components/MemberLevelSelect.vue b/yunxi-ui-admin-vue3/src/views/member/level/components/MemberLevelSelect.vue new file mode 100644 index 00000000..2a603e69 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/level/components/MemberLevelSelect.vue @@ -0,0 +1,45 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/level/index.vue b/yunxi-ui-admin-vue3/src/views/member/level/index.vue new file mode 100644 index 00000000..1347b7ef --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/level/index.vue @@ -0,0 +1,169 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/point/record/index.vue b/yunxi-ui-admin-vue3/src/views/member/point/record/index.vue new file mode 100644 index 00000000..dc8a35ec --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/point/record/index.vue @@ -0,0 +1,159 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/signin/config/SignInConfigForm.vue b/yunxi-ui-admin-vue3/src/views/member/signin/config/SignInConfigForm.vue new file mode 100644 index 00000000..616fd8fc --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/signin/config/SignInConfigForm.vue @@ -0,0 +1,132 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/signin/config/index.vue b/yunxi-ui-admin-vue3/src/views/member/signin/config/index.vue new file mode 100644 index 00000000..da38dad6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/signin/config/index.vue @@ -0,0 +1,104 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/signin/record/index.vue b/yunxi-ui-admin-vue3/src/views/member/signin/record/index.vue new file mode 100644 index 00000000..754663e5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/signin/record/index.vue @@ -0,0 +1,132 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/tag/TagForm.vue b/yunxi-ui-admin-vue3/src/views/member/tag/TagForm.vue new file mode 100644 index 00000000..d45ea589 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/tag/TagForm.vue @@ -0,0 +1,91 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/tag/components/MemberTagSelect.vue b/yunxi-ui-admin-vue3/src/views/member/tag/components/MemberTagSelect.vue new file mode 100644 index 00000000..ebff61ea --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/tag/components/MemberTagSelect.vue @@ -0,0 +1,68 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/tag/index.vue b/yunxi-ui-admin-vue3/src/views/member/tag/index.vue new file mode 100644 index 00000000..05a886a7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/tag/index.vue @@ -0,0 +1,153 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/UserForm.vue b/yunxi-ui-admin-vue3/src/views/member/user/UserForm.vue new file mode 100644 index 00000000..0da4ef61 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/UserForm.vue @@ -0,0 +1,179 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/UserLevelUpdateForm.vue b/yunxi-ui-admin-vue3/src/views/member/user/UserLevelUpdateForm.vue new file mode 100644 index 00000000..e583f4a9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/UserLevelUpdateForm.vue @@ -0,0 +1,101 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/UserPointUpdateForm.vue b/yunxi-ui-admin-vue3/src/views/member/user/UserPointUpdateForm.vue new file mode 100644 index 00000000..967ebe03 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/UserPointUpdateForm.vue @@ -0,0 +1,128 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/components/balance-list.vue b/yunxi-ui-admin-vue3/src/views/member/user/components/balance-list.vue new file mode 100644 index 00000000..3e9d1785 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/components/balance-list.vue @@ -0,0 +1,14 @@ + + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/detail/UserAccountInfo.vue b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserAccountInfo.vue new file mode 100644 index 00000000..13daff48 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserAccountInfo.vue @@ -0,0 +1,87 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/detail/UserAddressList.vue b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserAddressList.vue new file mode 100644 index 00000000..a37caba1 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserAddressList.vue @@ -0,0 +1,54 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/detail/UserBasicInfo.vue b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserBasicInfo.vue new file mode 100644 index 00000000..075450e9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserBasicInfo.vue @@ -0,0 +1,85 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/detail/UserBrokerageList.vue b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserBrokerageList.vue new file mode 100644 index 00000000..db88787b --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserBrokerageList.vue @@ -0,0 +1,125 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/detail/UserCouponList.vue b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserCouponList.vue new file mode 100644 index 00000000..2279b8aa --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserCouponList.vue @@ -0,0 +1,190 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/detail/UserExperienceRecordList.vue b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserExperienceRecordList.vue new file mode 100644 index 00000000..64414ad1 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserExperienceRecordList.vue @@ -0,0 +1,158 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/detail/UserOrderList.vue b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserOrderList.vue new file mode 100644 index 00000000..86c121bc --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserOrderList.vue @@ -0,0 +1,403 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/detail/UserPointList.vue b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserPointList.vue new file mode 100644 index 00000000..9754b297 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserPointList.vue @@ -0,0 +1,152 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/detail/UserSignList.vue b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserSignList.vue new file mode 100644 index 00000000..c8972741 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/detail/UserSignList.vue @@ -0,0 +1,135 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/detail/index.vue b/yunxi-ui-admin-vue3/src/views/member/user/detail/index.vue new file mode 100644 index 00000000..1bac010e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/detail/index.vue @@ -0,0 +1,132 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/member/user/index.vue b/yunxi-ui-admin-vue3/src/views/member/user/index.vue new file mode 100644 index 00000000..34ff230d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/member/user/index.vue @@ -0,0 +1,302 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/account/AccountForm.vue b/yunxi-ui-admin-vue3/src/views/mp/account/AccountForm.vue new file mode 100644 index 00000000..c721013c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/account/AccountForm.vue @@ -0,0 +1,160 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/account/index.vue b/yunxi-ui-admin-vue3/src/views/mp/account/index.vue new file mode 100644 index 00000000..212035a2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/account/index.vue @@ -0,0 +1,195 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/autoReply/components/ReplyForm.vue b/yunxi-ui-admin-vue3/src/views/mp/autoReply/components/ReplyForm.vue new file mode 100644 index 00000000..1c9dee49 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/autoReply/components/ReplyForm.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/autoReply/components/ReplyTable.vue b/yunxi-ui-admin-vue3/src/views/mp/autoReply/components/ReplyTable.vue new file mode 100644 index 00000000..2abe9f24 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/autoReply/components/ReplyTable.vue @@ -0,0 +1,115 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/autoReply/components/types.ts b/yunxi-ui-admin-vue3/src/views/mp/autoReply/components/types.ts new file mode 100644 index 00000000..68bc5c94 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/autoReply/components/types.ts @@ -0,0 +1,7 @@ +// 消息类型(Follow: 关注时回复;Message: 消息回复;Keyword: 关键词回复) +// 作为 tab.name,enum 的数字不能随意修改,与 api 参数相关 +export enum MsgType { + Follow = 1, + Message = 2, + Keyword = 3 +} diff --git a/yunxi-ui-admin-vue3/src/views/mp/autoReply/index.vue b/yunxi-ui-admin-vue3/src/views/mp/autoReply/index.vue new file mode 100644 index 00000000..0b006470 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/autoReply/index.vue @@ -0,0 +1,241 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-account-select/index.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-account-select/index.ts new file mode 100644 index 00000000..97556b2f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-account-select/index.ts @@ -0,0 +1,3 @@ +import WxAccountSelect from './main.vue' + +export default WxAccountSelect diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-account-select/main.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-account-select/main.vue new file mode 100644 index 00000000..2a6ca50f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-account-select/main.vue @@ -0,0 +1,47 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-location/index.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-location/index.ts new file mode 100644 index 00000000..14ba8644 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-location/index.ts @@ -0,0 +1,3 @@ +import WxLocation from './main.vue' + +export default WxLocation diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-location/main.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-location/main.vue new file mode 100644 index 00000000..0b68d49d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-location/main.vue @@ -0,0 +1,73 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/index.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/index.ts new file mode 100644 index 00000000..eeda31d5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/index.ts @@ -0,0 +1,6 @@ +import WxMaterialSelect from './main.vue' +import { NewsType, MaterialType } from './types' + +export { NewsType, MaterialType } + +export default WxMaterialSelect diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/main.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/main.vue new file mode 100644 index 00000000..aad25ea8 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/main.vue @@ -0,0 +1,279 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/types.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/types.ts new file mode 100644 index 00000000..d4add1d5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-material-select/types.ts @@ -0,0 +1,11 @@ +export enum NewsType { + Draft = '2', + Published = '1' +} + +export enum MaterialType { + Image = 'image', + Voice = 'voice', + Video = 'video', + News = 'news' +} diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/card.scss b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/card.scss new file mode 100644 index 00000000..7fbbe802 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/card.scss @@ -0,0 +1,116 @@ +.avue-card { + &__item { + margin-bottom: 16px; + border: 1px solid #e8e8e8; + background-color: #fff; + box-sizing: border-box; + color: rgba(0, 0, 0, 0.65); + font-size: 14px; + font-variant: tabular-nums; + line-height: 1.5; + list-style: none; + font-feature-settings: 'tnum'; + cursor: pointer; + height: 200px; + + &:hover { + border-color: rgba(0, 0, 0, 0.09); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09); + } + + &--add { + border: 1px dashed #000; + width: 100%; + color: rgba(0, 0, 0, 0.45); + background-color: #fff; + border-color: #d9d9d9; + border-radius: 2px; + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + + i { + margin-right: 10px; + } + + &:hover { + color: #40a9ff; + background-color: #fff; + border-color: #40a9ff; + } + } + } + + &__body { + display: flex; + padding: 24px; + } + + &__detail { + flex: 1; + } + + &__avatar { + width: 48px; + height: 48px; + border-radius: 48px; + overflow: hidden; + margin-right: 12px; + + img { + width: 100%; + height: 100%; + } + } + + &__title { + color: rgba(0, 0, 0, 0.85); + margin-bottom: 12px; + font-size: 16px; + + &:hover { + color: #1890ff; + } + } + + &__info { + color: rgba(0, 0, 0, 0.45); + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: hidden; + height: 64px; + } + + &__menu { + display: flex; + justify-content: space-around; + height: 50px; + background: #f7f9fa; + color: rgba(0, 0, 0, 0.45); + text-align: center; + line-height: 50px; + + &:hover { + color: #1890ff; + } + } +} + +/** joolun 额外加的 */ +.avue-comment__main { + flex: unset !important; + border-radius: 5px !important; + margin: 0 8px !important; +} + +.avue-comment__header { + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +.avue-comment__body { + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; +} diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/comment.scss b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/comment.scss new file mode 100644 index 00000000..7812c2a3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/comment.scss @@ -0,0 +1,126 @@ +/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */ +.avue-comment { + margin-bottom: 30px; + display: flex; + align-items: flex-start; + + &--reverse { + flex-direction: row-reverse; + + .avue-comment__main { + &:before, + &:after { + left: auto; + right: -8px; + border-width: 8px 0 8px 8px; + } + + &:before { + border-left-color: #dedede; + } + + &:after { + border-left-color: #f8f8f8; + margin-right: 1px; + margin-left: auto; + } + } + } + + &__avatar { + width: 48px; + height: 48px; + border-radius: 50%; + border: 1px solid transparent; + box-sizing: border-box; + vertical-align: middle; + } + + &__header { + padding: 5px 15px; + background: #f8f8f8; + border-bottom: 1px solid #eee; + display: flex; + align-items: center; + justify-content: space-between; + } + + &__author { + font-weight: 700; + font-size: 14px; + color: #999; + } + + &__main { + flex: 1; + margin: 0 20px; + position: relative; + border: 1px solid #dedede; + border-radius: 2px; + + &:before, + &:after { + position: absolute; + top: 10px; + left: -8px; + right: 100%; + width: 0; + height: 0; + display: block; + content: ' '; + border-color: transparent; + border-style: solid solid outset; + border-width: 8px 8px 8px 0; + pointer-events: none; + } + + &:before { + border-right-color: #dedede; + z-index: 1; + } + + &:after { + border-right-color: #f8f8f8; + margin-left: 1px; + z-index: 2; + } + } + + &__body { + padding: 15px; + overflow: hidden; + background: #fff; + font-family: + Segoe UI, + Lucida Grande, + Helvetica, + Arial, + Microsoft YaHei, + FreeSans, + Arimo, + Droid Sans, + wenquanyi micro hei, + Hiragino Sans GB, + Hiragino Sans GB W3, + FontAwesome, + sans-serif; + color: #333; + font-size: 14px; + } + + blockquote { + margin: 0; + font-family: + Georgia, + Times New Roman, + Times, + Kai, + Kaiti SC, + KaiTi, + BiauKai, + FontAwesome, + serif; + padding: 1px 0 1px 15px; + border-left: 4px solid #ddd; + } +} diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/Msg.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/Msg.vue new file mode 100644 index 00000000..c35e268e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/Msg.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/MsgEvent.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/MsgEvent.vue new file mode 100644 index 00000000..77beda48 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/MsgEvent.vue @@ -0,0 +1,49 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/MsgList.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/MsgList.vue new file mode 100644 index 00000000..ce7063b2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/components/MsgList.vue @@ -0,0 +1,62 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/index.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/index.ts new file mode 100644 index 00000000..fd9eddd7 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/index.ts @@ -0,0 +1,6 @@ +import WxMsg from './main.vue' +import { MsgType } from './types' + +export { MsgType } + +export default WxMsg diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/main.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/main.vue new file mode 100644 index 00000000..8b7cc3a2 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/main.vue @@ -0,0 +1,192 @@ + + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/types.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/types.ts new file mode 100644 index 00000000..38a0ff86 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-msg/types.ts @@ -0,0 +1,17 @@ +export enum MsgType { + Event = 'event', + Text = 'text', + Voice = 'voice', + Image = 'image', + Video = 'video', + Link = 'link', + Location = 'location', + Music = 'music', + News = 'news' +} + +export interface User { + nickname: string + avatar: string + accountId: number +} diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-music/index.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-music/index.ts new file mode 100644 index 00000000..c4211261 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-music/index.ts @@ -0,0 +1,3 @@ +import WxMusic from './main.vue' + +export default WxMusic diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-music/main.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-music/main.vue new file mode 100644 index 00000000..6b44f449 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-music/main.vue @@ -0,0 +1,62 @@ + + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-news/index.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-news/index.ts new file mode 100644 index 00000000..e68f4d5d --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-news/index.ts @@ -0,0 +1,3 @@ +import WxNews from './main.vue' + +export default WxNews diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-news/main.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-news/main.vue new file mode 100644 index 00000000..154291b3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-news/main.vue @@ -0,0 +1,119 @@ + + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabImage.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabImage.vue new file mode 100644 index 00000000..a2915779 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabImage.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabMusic.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabMusic.vue new file mode 100644 index 00000000..c7caecb9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabMusic.vue @@ -0,0 +1,116 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabNews.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabNews.vue new file mode 100644 index 00000000..565b1fba --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabNews.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabText.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabText.vue new file mode 100644 index 00000000..307e48f4 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabText.vue @@ -0,0 +1,22 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabVideo.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabVideo.vue new file mode 100644 index 00000000..7d67d2fa --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabVideo.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabVoice.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabVoice.vue new file mode 100644 index 00000000..5a7a42d5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/TabVoice.vue @@ -0,0 +1,160 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/types.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/types.ts new file mode 100644 index 00000000..3e07d6e5 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/components/types.ts @@ -0,0 +1,54 @@ +enum ReplyType { + News = 'news', + Image = 'image', + Voice = 'voice', + Video = 'video', + Music = 'music', + Text = 'text' +} + +interface _Reply { + accountId: number + type: ReplyType + name?: string | null + content?: string | null + mediaId?: string | null + url?: string | null + title?: string | null + description?: string | null + thumbMediaId?: string | null + thumbMediaUrl?: string | null + musicUrl?: string | null + hqMusicUrl?: string | null + introduction?: string | null + articles?: any[] +} + +type Reply = _Reply //Partial<_Reply> + +enum NewsType { + Published = '1', + Draft = '2' +} + +/** 利用旧的reply[accountId, type]初始化新的Reply */ +const createEmptyReply = (old: Reply | Ref): Reply => { + return { + accountId: unref(old).accountId, + type: unref(old).type, + name: null, + content: null, + mediaId: null, + url: null, + title: null, + description: null, + thumbMediaId: null, + thumbMediaUrl: null, + musicUrl: null, + hqMusicUrl: null, + introduction: null, + articles: [] + } +} + +export { Reply, NewsType, ReplyType, createEmptyReply } diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/index.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/index.ts new file mode 100644 index 00000000..d1da217e --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/index.ts @@ -0,0 +1,7 @@ +import { Reply, NewsType, ReplyType, createEmptyReply } from './components/types' + +import WxReplySelect from './main.vue' + +export type { Reply } +export { createEmptyReply, NewsType, ReplyType } +export default WxReplySelect diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/main.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/main.vue new file mode 100644 index 00000000..2c9d5f21 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-reply/main.vue @@ -0,0 +1,208 @@ + + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-video-play/index.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-video-play/index.ts new file mode 100644 index 00000000..91e00efa --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-video-play/index.ts @@ -0,0 +1,3 @@ +import WxVideoPlayer from './main.vue' + +export default WxVideoPlayer diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-video-play/main.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-video-play/main.vue new file mode 100644 index 00000000..d544bbea --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-video-play/main.vue @@ -0,0 +1,73 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-voice-play/index.ts b/yunxi-ui-admin-vue3/src/views/mp/components/wx-voice-play/index.ts new file mode 100644 index 00000000..9eb78e02 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-voice-play/index.ts @@ -0,0 +1,3 @@ +import WxVoicePlayer from './main.vue' + +export default WxVoicePlayer diff --git a/yunxi-ui-admin-vue3/src/views/mp/components/wx-voice-play/main.vue b/yunxi-ui-admin-vue3/src/views/mp/components/wx-voice-play/main.vue new file mode 100644 index 00000000..fe7f0cab --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/components/wx-voice-play/main.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/draft/components/CoverSelect.vue b/yunxi-ui-admin-vue3/src/views/mp/draft/components/CoverSelect.vue new file mode 100644 index 00000000..499f1a64 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/draft/components/CoverSelect.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/draft/components/DraftTable.vue b/yunxi-ui-admin-vue3/src/views/mp/draft/components/DraftTable.vue new file mode 100644 index 00000000..bb512d88 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/draft/components/DraftTable.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/draft/components/NewsForm.vue b/yunxi-ui-admin-vue3/src/views/mp/draft/components/NewsForm.vue new file mode 100644 index 00000000..9b1e4745 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/draft/components/NewsForm.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/draft/components/index.ts b/yunxi-ui-admin-vue3/src/views/mp/draft/components/index.ts new file mode 100644 index 00000000..51e843d3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/draft/components/index.ts @@ -0,0 +1,7 @@ +import type { Article, NewsItem, NewsItemList } from './types' +import { createEmptyNewsItem } from './types' +import DraftTable from './DraftTable.vue' +import NewsForm from './NewsForm.vue' + +export { DraftTable, NewsForm, createEmptyNewsItem } +export type { Article, NewsItem, NewsItemList } diff --git a/yunxi-ui-admin-vue3/src/views/mp/draft/components/types.ts b/yunxi-ui-admin-vue3/src/views/mp/draft/components/types.ts new file mode 100644 index 00000000..a8cf00c3 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/draft/components/types.ts @@ -0,0 +1,40 @@ +interface NewsItem { + title: string + thumbMediaId: string + author: string + digest: string + showCoverPic: string + content: string + contentSourceUrl: string + needOpenComment: string + onlyFansCanComment: string + thumbUrl: string +} + +interface NewsItemList { + newsItem: NewsItem[] +} + +interface Article { + mediaId: string + content: NewsItemList + updateTime: number +} + +const createEmptyNewsItem = (): NewsItem => { + return { + title: '', + thumbMediaId: '', + author: '', + digest: '', + showCoverPic: '', + content: '', + contentSourceUrl: '', + needOpenComment: '', + onlyFansCanComment: '', + thumbUrl: '' + } +} + +export type { Article, NewsItem, NewsItemList } +export { createEmptyNewsItem } diff --git a/yunxi-ui-admin-vue3/src/views/mp/draft/editor-config.ts b/yunxi-ui-admin-vue3/src/views/mp/draft/editor-config.ts new file mode 100644 index 00000000..ee3b95ec --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/draft/editor-config.ts @@ -0,0 +1,75 @@ +import { IEditorConfig } from '@wangeditor/editor' +import { getAccessToken, getTenantId } from '@/utils/auth' + +const message = useMessage() + +type InsertFnType = (url: string, alt: string, href: string) => void + +export const createEditorConfig = ( + server: string, + accountId: number | undefined +): Partial => { + return { + MENU_CONF: { + ['uploadImage']: { + server, + // 单个文件的最大体积限制,默认为 2M + maxFileSize: 5 * 1024 * 1024, + // 最多可上传几个文件,默认为 100 + maxNumberOfFiles: 10, + // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 [] + allowedFileTypes: ['image/*'], + + // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。 + meta: { + accountId: accountId, + type: 'image' + }, + // 将 meta 拼接到 url 参数中,默认 false + metaWithUrl: true, + + // 自定义增加 http header + headers: { + Accept: '*', + Authorization: 'Bearer ' + getAccessToken(), + 'tenant-id': getTenantId() + }, + + // 跨域是否传递 cookie ,默认为 false + withCredentials: true, + + // 超时时间,默认为 10 秒 + timeout: 5 * 1000, // 5 秒 + + // form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image + fieldName: 'file', + + // 上传之前触发 + onBeforeUpload(file: File) { + console.log(file) + return file + }, + // 上传进度的回调函数 + onProgress(progress: number) { + // progress 是 0-100 的数字 + console.log('progress', progress) + }, + onSuccess(file: File, res: any) { + console.log('onSuccess', file, res) + }, + onFailed(file: File, res: any) { + message.alertError(res.message) + console.log('onFailed', file, res) + }, + onError(file: File, err: any, res: any) { + message.alertError(err.message) + console.error('onError', file, err, res) + }, + // 自定义插入图片 + customInsert(res: any, insertFn: InsertFnType) { + insertFn(res.data.url, 'image', res.data.url) + } + } + } + } +} diff --git a/yunxi-ui-admin-vue3/src/views/mp/draft/index.vue b/yunxi-ui-admin-vue3/src/views/mp/draft/index.vue new file mode 100644 index 00000000..db24596a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/draft/index.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/draft/mock.js b/yunxi-ui-admin-vue3/src/views/mp/draft/mock.js new file mode 100644 index 00000000..e8493f6c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/draft/mock.js @@ -0,0 +1,151 @@ +export default { + list: [ + { + mediaId: 'r6ryvl6LrxBU0miaST4Y-q-G9pdsmZw0OYG4FzHQkKfpLfEwIH51wy2bxisx8PvW', + content: { + newsItem: [ + { + title: '我是标题(OOO)', + author: '我是作者', + digest: '我是摘要', + content: '我是内容', + contentSourceUrl: 'https://www.iocoder.cn', + thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn', + showCoverPic: 0, + needOpenComment: 0, + onlyFansCanComment: 0, + url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9XaFphcmtJVFh3VEc4Q1MxQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuN2QxTE56SFBCYXc2RE9NcUxIeS1CQjJuUHhTWjBlN2VOeGRpRi1fZUhwN1FNQjdrQV9yRU9EU0hibHREZmZoVW5acnZrN3ZjaWsxejR3RGpKczBzTHFIM0dFNFZWVkpBc0dWWlAzUEhlVmpnfn4%3D&chksm=1f6354802814dd969ef83c0f3babe555c614270b30bc383beaf7ffd13b0257f0fe5ced9af694#rd', + thumbUrl: + 'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn.png' + }, + { + title: '我是标题(XXX)', + author: '我是作者', + digest: '我是摘要', + content: '我是内容', + contentSourceUrl: 'https://www.iocoder.cn', + thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn', + showCoverPic: 0, + needOpenComment: 0, + onlyFansCanComment: 0, + url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9yTlYwOEs1clpwcE5OUEhCQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuN0NSMjFqN3N1aUZMbFNVLTZHN2ZDME9qOGp2THk2RFNlSTlKZ3Y1czFVZDdQQm5IeUg3dEppSUtpQUh5SExOOTRkT3dHNUdBdHdWSWlOendlREV3dS1jUEVQbFpiVTZmVW5iRWhZcGdkNTFRfn4%3D&chksm=1f6354802814dd96a403151cd44c7da4eecf0e475d25423e46ecd795b513bafd829a75daef9b#rd', + thumbUrl: + 'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn.png' + } + ] + }, + updateTime: 1673655730 + }, + { + mediaId: 'r6ryvl6LrxBU0miaST4Y-jGpXnO73ihN0lsNXknCRQHapp2xgHMRxHKG50LituFe', + content: { + newsItem: [ + { + title: '我是标题(修改)', + author: '我是作者', + digest: '我是摘要', + content: '我是内容', + contentSourceUrl: 'https://www.iocoder.cn', + thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn', + showCoverPic: 0, + needOpenComment: 0, + onlyFansCanComment: 0, + url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl95WVFXYndIZnZJd0t5cjgvQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuN1dlNURPbWswbEF4RDd5dVJTdjQ4cm9Cc0Q1TWhpMUh6SE1hVEE3ZHljaHhlZjZYSGF5N2JNSHpDTlh6ajNZbkpGTGpTcUQ4M3NMdW41ZUpXNFZZQ1VKbVlaMVp5ekxEV1czREdsY1dOYTZnfn4%3D&chksm=1f6354be2814dda8e6238037c2ebd52b1c8e80e93249a861ad80e4d40e5ca7207233475ca689#rd', + thumbUrl: + 'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn.png' + } + ] + }, + updateTime: 1673655584 + }, + { + mediaId: 'r6ryvl6LrxBU0miaST4Y-v5SrbNCPpD6M_p3TmSrYwTjKogs-0DMJgmjMyNZPeMO', + content: { + newsItem: [ + { + title: '1321', + author: '3232', + digest: '1333', + content: '

444

', + contentSourceUrl: 'http://www.iocoder.cn', + thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-tlQmcl3RdC-Jcgns6IQtf7zenGy3b86WLT7GzUcrb1T', + showCoverPic: 0, + needOpenComment: 0, + onlyFansCanComment: 0, + url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9jelJiaDAzbmdpSkJOZ2M2QWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuNDNXVVc2ZDRYeTY0Zm1weXR6dE9vQWh1TzEwbEpUVnRfVzJyaGFDNXBkZ0ZXM2JFOTNaRHNhOHRUeFdEanhMeS01X01kMUNWQ1BpRER3cjYwTl9pMnpFLUJhZXFucVVfM1pDUXlTUEl1S25nfn4%3D&chksm=1f6354bc2814ddaa56a90ad5bc3d078601c8d1589ba01827a8170587bc830ff9747b5f59c3a0#rd', + thumbUrl: + 'http://mmbiz.qpic.cn/mmbiz_png/btUmCVHwbJUoicwBiacjVeQbu6QxgBVrukfSJXz509boa21SpH8OVHAqXCJiaiaAaHQJNxwwsa0gHRXVr0G5EZYamw/0?wx_fmt=png' + } + ] + }, + updateTime: 1673628969 + }, + { + mediaId: 'r6ryvl6LrxBU0miaST4Y-vdWrisK5EZbk4Y3tzh8P0PG0eEUbnQrh0BcsEb3WNP0', + content: { + newsItem: [ + { + title: 'tudou', + author: 'haha', + digest: '312', + content: '

132312

', + contentSourceUrl: 'http://www.iocoder.cn', + thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-pgFtUNLu1foMSAMkoOsrQrTZ8EtTMssBLfTtzP0dfjG', + showCoverPic: 0, + needOpenComment: 0, + onlyFansCanComment: 0, + url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9qdkJ1ZjBoUmg2Uk9TS3RlQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuNVg2aTJsaC1fMkU2eXNacUplN3VDTTZFZkhtMjhuTUZvWkxsNDBRSXExY2tiVXRHb09TaHgtREhzY3doZ0JYeC1TSTZ5eWZldXJsOWtfbV8yMi1aYkcyZ2pOY0haM0Ntb3VSWEtxUGVFRlNBfn4%3D&chksm=1f6354ba2814ddacf0184b24d310483641ef190b1faac098c285eb416c70017e2f54decfa1af#rd', + thumbUrl: + 'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-pgFtUNLu1foMSAMkoOsrQrTZ8EtTMssBLfTtzP0dfjG.png' + } + ] + }, + updateTime: 1673628760 + }, + { + mediaId: 'r6ryvl6LrxBU0miaST4Y-u9kTIm1DhWZDdXyxsxUVv2Z5DAB99IPxkIRTUUD206k', + content: { + newsItem: [ + { + title: '12', + author: '333', + digest: '123', + content: '123', + contentSourceUrl: 'https://www.iocoder.cn', + thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-jVixJGgnBnkBPRbuVptOW0CHYuQFyiOVNtamctS8xU8', + showCoverPic: 0, + needOpenComment: 0, + onlyFansCanComment: 0, + url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9qVVhpSDZUaFJWTzBBWWRVQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuNWRnTDJWYmF2NER0clV1bThmQ0xUR3hqQnJkZ3BJSUNmNDJmc0lCZ1dadkVnZ3Z5bkN4YWtVUjhoaWZWYzZURUR4NnpMd0Y4Z3U5aUdib0lkMzI4Rjg3SG9JX2FycTMxbUctOHplaTlQVVhnfn4%3D&chksm=1f6354b62814dda076c778af33f06580165d8aa81f7798d55cfabb1886b5c74d9b2124a3535c#rd', + thumbUrl: + 'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-jVixJGgnBnkBPRbuVptOW0CHYuQFyiOVNtamctS8xU8.jpg' + } + ] + }, + updateTime: 1673626494 + }, + { + mediaId: 'r6ryvl6LrxBU0miaST4Y-sO24upobaENDmeByfBTfaozB3aOqSMAV0lGy-UkHXE7', + content: { + newsItem: [ + { + title: '我是标题', + author: '我是作者', + digest: '我是摘要', + content: '我是内容', + contentSourceUrl: 'https://www.iocoder.cn', + thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn', + showCoverPic: 0, + needOpenComment: 0, + onlyFansCanComment: 0, + url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9LT2dqRnpMNUpsR0hjYWtBQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuNGNmazZTdlE5WkxvU0tfX2V5cjV2WjJiR0xjQUhyREFSZWo2eWNrUW9EYVh6ZkpWRXBLR3FmTEV6YldBMno3Q2ZvVXBSdzlaVDc3aFhndEpQWUwzWmFMUWt0YVVURE1VZ1FsQTdPMlRtc3JBfn4%3D&chksm=1f6354aa2814ddbcc2637382f963a8742993ac38ebcebe6e3411df5ac82ac7bbdb391be6494a#rd', + thumbUrl: + 'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn.png' + } + ] + }, + updateTime: 1673534279 + } + ], + total: 6 +} diff --git a/yunxi-ui-admin-vue3/src/views/mp/freePublish/index.vue b/yunxi-ui-admin-vue3/src/views/mp/freePublish/index.vue new file mode 100644 index 00000000..2ed8ae77 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/freePublish/index.vue @@ -0,0 +1,336 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/hooks/useUpload.ts b/yunxi-ui-admin-vue3/src/views/mp/hooks/useUpload.ts new file mode 100644 index 00000000..b0e70531 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/hooks/useUpload.ts @@ -0,0 +1,50 @@ +import type { UploadRawFile } from 'element-plus' + +const message = useMessage() // 消息 + +enum UploadType { + Image = 'image', + Voice = 'voice', + Video = 'video' +} + +const useBeforeUpload = (type: UploadType, maxSizeMB: number) => { + const fn = (rawFile: UploadRawFile): boolean => { + let allowTypes: string[] = [] + let name = '' + + switch (type) { + case UploadType.Image: + allowTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/jpg'] + maxSizeMB = 2 + name = '图片' + break + case UploadType.Voice: + allowTypes = ['audio/mp3', 'audio/mpeg', 'audio/wma', 'audio/wav', 'audio/amr'] + maxSizeMB = 2 + name = '语音' + break + case UploadType.Video: + allowTypes = ['video/mp4'] + maxSizeMB = 10 + name = '视频' + break + } + // 格式不正确 + if (!allowTypes.includes(rawFile.type)) { + message.error(`上传${name}格式不对!`) + return false + } + // 大小不正确 + if (rawFile.size / 1024 / 1024 > maxSizeMB) { + message.error(`上传${name}大小不能超过${maxSizeMB}M!`) + return false + } + + return true + } + + return fn +} + +export { UploadType, useBeforeUpload } diff --git a/yunxi-ui-admin-vue3/src/views/mp/material/components/ImageTable.vue b/yunxi-ui-admin-vue3/src/views/mp/material/components/ImageTable.vue new file mode 100644 index 00000000..52c608f6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/material/components/ImageTable.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/material/components/UploadFile.vue b/yunxi-ui-admin-vue3/src/views/mp/material/components/UploadFile.vue new file mode 100644 index 00000000..1476917a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/material/components/UploadFile.vue @@ -0,0 +1,74 @@ + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/material/components/UploadVideo.vue b/yunxi-ui-admin-vue3/src/views/mp/material/components/UploadVideo.vue new file mode 100644 index 00000000..28f050c0 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/material/components/UploadVideo.vue @@ -0,0 +1,126 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/material/components/VideoTable.vue b/yunxi-ui-admin-vue3/src/views/mp/material/components/VideoTable.vue new file mode 100644 index 00000000..cbaa9024 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/material/components/VideoTable.vue @@ -0,0 +1,59 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/material/components/VoiceTable.vue b/yunxi-ui-admin-vue3/src/views/mp/material/components/VoiceTable.vue new file mode 100644 index 00000000..76fab7af --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/material/components/VoiceTable.vue @@ -0,0 +1,51 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/material/components/upload.ts b/yunxi-ui-admin-vue3/src/views/mp/material/components/upload.ts new file mode 100644 index 00000000..7158ab12 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/material/components/upload.ts @@ -0,0 +1,31 @@ +import type { UploadProps, UploadRawFile } from 'element-plus' +import { getAccessToken } from '@/utils/auth' +import { UploadType, useBeforeUpload } from '@/views/mp/hooks/useUpload' + +const HEADERS = { Authorization: 'Bearer ' + getAccessToken() } // 请求头 +const UPLOAD_URL = import.meta.env.VITE_BASE_URL + '/admin-api/mp/material/upload-permanent' // 上传地址 + +interface UploadData { + type: UploadType + title: string + introduction: string +} + +const beforeImageUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => + useBeforeUpload(UploadType.Image, 2)(rawFile) + +const beforeVoiceUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => + useBeforeUpload(UploadType.Voice, 2)(rawFile) + +const beforeVideoUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => + useBeforeUpload(UploadType.Video, 10)(rawFile) + +export { + HEADERS, + UPLOAD_URL, + UploadType, + UploadData, + beforeImageUpload, + beforeVoiceUpload, + beforeVideoUpload +} diff --git a/yunxi-ui-admin-vue3/src/views/mp/material/index.vue b/yunxi-ui-admin-vue3/src/views/mp/material/index.vue new file mode 100644 index 00000000..b72c9ad6 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/material/index.vue @@ -0,0 +1,154 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/menu/assets/iphone_backImg.png b/yunxi-ui-admin-vue3/src/views/mp/menu/assets/iphone_backImg.png new file mode 100644 index 0000000000000000000000000000000000000000..bb09591a7912730aab07287e0bb2bc4e3383abc5 GIT binary patch literal 34272 zcmeFZ`9E9f|34hnRu@Y<2DMfD?o2nelv-*_mnk!CQAO+&wTq=lY{Ar^rD&%`iBfG* zA~aPA2}xT^T5FXcl8B|&kkpa{k>oyR9-sI9{e1s{`-i(84~KJd<(%t!Ua#f(dY$Wf z-M?XDC9_Xu9|#1J`Q!KBZh}CP0U(gno;|yPD~%OR8^E8PQJ4O3*aQ3|?zw#rIG2dJ zX>|!y+p9Le{b9gG>x&=|CU^gq_b%XkZ^Z8|Q6P|P+xD+So~EoSaPesL73b*7cl^Ag zgTlj(I0QlcKzh1o&ztC;H#vLmh@P&Afu6}ZJ=y2%V<1qP@gKikbcpp_WVk)J%|~dg z+f!PPUbz$d==VqXYuHe7$jR@1({o3a{wVv|xuQkx?18q6o^N%VU3Lh#msLs2R4Is(r(i$h&W0}|UO2>kk?@YC z817X=$PiHEWV44D<+W}MnwAor&w_34A|M5|4q$V-B&b-Dg_`tvFs8T>4e45V zP2q6C=CFf~_7Dp^rG}wnsQd2Ek_cGu){K5%b21#h;WWfp(8>9e zCA9zIIz}@(SPcT%cxZf2zcnm{zrxRhK6I-qb@e3habRv$ZnK%;H`(LGj6c)iL~Y{o z@HLQIjS~1vMjag9#cdVh1d}7*E|{8jvA+uWa6U6396s?et&FY0hHu6o7qRv3JeM%4 z)>ZB*i`Xgj!OSIR)MO-pQdVzKNW7{c6vddLqfrSL2p9|od$fVUCe)F}m|iT;xTOX* zfr)5Im|9q?pvj;iPMbV;x&aPJ7~(i)nZGi`rq*5?@!+BaJg z2q%W%ZDY$k955|#4sX>91Uf*0Q;EV!I9|ysK2H4JFuj2rzkJPtke?a>L+158&{AZ1 zIoBvAH@U%bv$LeQtKOX0#eQ}Pfuq;~=&qmN_ebFfLA$VZv_kcpMod;am|NIDvoa>g zXoH1rU?zGPJghaOk;%zvhj(au;Jx_20kU;{Z%7|v3dtBQ6~^Vb9>foV`91t`pTT6nSm4arx>YnpxAs=FwR!+WB&y0Yj ze7B*>PrYE5NogFqR2cHN1nB(-;(A`4>`Qav=4c3V7mLk@Yu6-O!pXwD0s4ler`)z0 zlI~RQ#7sPU%WtWqon8132t@z+=u*UA_2%jQN(3%*skCSUeU|eCxL5zuFe8$IPHvye zk)H^S-WVv|_+0BIsyF$C*th-pGV173dx7Ty3=S9k%Ce}^5=#yLA>W(cmxpG@3-yKi zXWh+4=dysASgBQIL{iL2uF>F_5-FMU!xNzcX&B9hC^3Q$3rEbAv7hCJ*ZnF30u{e$ z8U90a_;*3o35(79-ZRDL3npr$vq^%P$$DR^-rU5wjKo@C%EBJ&1%Aq6##JQ< zvyJd!4fwA*n=IR;l_?U@CBdSYP$s3bEm2EsZ40lvF7qEpXmNsLxqJM94ym?q7@P&7 zIDr(f{?rcV?zzBPsWTTY+D|P+P0*X843n^ne1X;02R-p8H>L^D0rxZbD}b(Eko8Jn zZ?IK$5?S<{=yQk&-# z!EP0-=NhJ6QAQ_!1?KqCRbxTtaEYqa{`nFwPr|I=UoTQ?F~EKu0o$kw5H}|Ora`At z=PkaE!C4)9Kp^<@3glF9j9-JZXu4F?W|a(gNBA=iPMk?1UN-{z)VSIb$z3?`g}D0l zdj&O2o-_@J^Jyj?j&`Qh8Y8N|KFa@=67$7=|rFNx-C zMbY|yPvHf^6X4~3alCuX@#3Zk#Z;;k_`Ah1y&WKs+po`s^Gc+VmX(E&U!NmtFdXLOjni)uY3R`vGI{!e1dX&%Y&IQ zExerU@MR0h-rtN>@45^NTl9pfO#a2~R>of}B$oIcjzQ&e0ST$pR5|z4j{(jkRV}e3 z60o%-mdHs<^8uwQPUTNF^#i72y0r^}{<*tC^p*2PN1?4na?Jg;?XS4GCe$~Ly+ zkSFjj+hSZvm}L{X2==yHqXT_gD~+e~o=hwnmB!5oyV^IIyvL@~I|gcf0mGwqMJCYH z)D&^SZEJCTxys>YdOgEvSX{+YRO&M5Ce9)_$^`%(bo|lhh(4)~Cbm*WuHCPHYZuG3 zx@#8{biO{VabSB2d>t&b9ph)YF#PnSSi6OnuCVvjzJSZjzAO9>N1wLv%^AIVOIDLW zOa1D#Ud@)ZtKlsSBc)QtnHJ&KSEXIH;-S6*VrjWt;9mb}Ei0hi;~VkwGQ$@DC+F9+ z+cmRnt;_8`fsob5eQc_+^G+c_PpWLhdjjWVjZvvWg53CGBW9 z69I?M6B7sm4x!HL$!7Ig5Z%)6o)9dYR1%O*)WxvEgqmP(8(GxRxPbkAS&6k$30}Rk zQS7qU@7H39_ZMr|$`+A2iKv9t9*c+S)#==9?#x9)$?Ez)1^@Jk`g3;kplDDqrRb-txf9s75Xt6S}c)??!oFHnC zO?IOXMHsrf-Rc*P2>FZ<&;d}`>%VHkN$bxd+21=2cc_4(I%D#TO2KZP_-CbH0yHR4 z|Mv7ICx4#by!vc6NMihg6Tmd`@47f*wFICP;S-~*PNd~@&r4d&p9?g_URa%0$?3~Z z`F(|XAu^!%#d_;xBhOgxVnU3chMyPcjZ<9x>*kqOw*L)4!Yg9n8H^zP&g@vnibQ-HyD)uH#8CeJ}BpW@5 zUtz<|YE6%UGO*4M=%3s=Y#D+@FqEo-~fxmLtCLE|8R_ zhiV4A58vMIm9R;b=ZwW(UT*-TI+*yUTkT_v09PIP0b2^w4T}Bpz#^=ty{{8#F50?tY3fqbGGzWG|NQ@hsZDMgTeuGS;BlZ++$9wc?jeZr;qj0J@?B-=wsk1SyWqp&hz)3G# zy#}@h8O!FHZ+`kNwNgt6Gy5ee$bK{*3L1Znz09hEi3@t#mQ5bz@c-d3Oom5dZ7F@@ zk(}`1K@|yO7>kQ1taPFnc}`wSW3f}&KwuHlx^hRoUe7&AIS(T(n$Y(Lsa0;P7+WlA zz9~8&tg}s;^d;$PTPU7b0kqY=Pvl9O)zBQ?wLUI2W~2>-p~f^%1Q3&e#RcF+JY}Kt zap~5255EEY9s>_KQ)<9UrOFk|%Qg1}^Z*vd)Jl0SVwGAyUEXC27GzCL`bI7S?rbPD zAnrx#MZoG66r)(ApS7?j}u4dH=3h!_{woS7QWH*@Z$zR;2O_9rw)ZB~-TZ8%}2c|*P zzn}BJr%v6<&hjfw%VDpNop-AfLz)r*#WLvLHo%|9mPcqt@>y)j*4JFC=ra?cv8v%s z%?@J*nwPI_H<(wc(MK1xJ#F8CRKfeWXm`m}wWVkJl79ksRO@#bIHp z_vCTLrfC3++m}aJ`t-|oCr1d(<@E@XGg?H~jeHztUYzZr!gRGd4s_I*41>XP1{;?1 z0rlPX!5%8WRadL|(by19F`7VfVK2~N+xWG%TTZQ_!WoMZPGDfHGZ z!NgHb8gjrh-&<^3Amn}S=B%N-5zKxf6yv!S+my^Vl_>*muRA*-r9r=5VPTZ_%M^(FRj=YpfqOmGC+Uu^Ij(q0~sy=BsLWb3cO1>WfKUY1e@<*Jp5>Ubg{{Uq?K@v;GC0v@z4b z&P$4O5ufl{pZr1$l>$AqYZ;^B-NfCxktvs9A?{lnE8Iso3$tzswMuS012L7Rgx5*j zKruAyh>)ZNb=M#-Y=Q`>BQ1~sefsv{8uD=Vc4oqR5|pt&U@C0@s}U$^e0;dpgS8z@ z!E};lrz1b3H+rw>$O%pfBtf5iW9U3V)03k&s*i*1@xMnAS!{^@%Y zeYtD&%oj?6Zhy}qA6Bn4^o{EVnxd3SlUS{CGOHjfF5rBnOAng<;U}Ip27z!iAQ>`x zy@mh}KhA@XXMT+)+VRrlstCvkle^Gj`L|F2prKxB6Kx&3+>c5EEa(~iyWXHoU z1YfpPku{XnE+-=|FxD-mJW}bB%9hA!Z3w9p=xkOZng>=o)#)COUuvedf*y7OsiJ^J zuNwS6&10YBau+an;KOPAThF|=d&VP}!xsqG{2VNwMpY7hr6cmKCC}6vhLgYtWQ@wMH{JAMyHXnH(-ho|pr6ia!lyb!6fd2l>X+ufMK(tXBT zwz<}XRp_RAF8-Y2(0Ovb-T1U9#=n(XXTe>}=t2m`Ro+K78jEh4Xl-|Nvi`kV8#64W zc;V!FjKfw7(C|*b@VBPq@xQ&-{+zN0ODDM60Em}MlTlsA1*gDDK*;_fQa0r}n-so9 zuIDaD8Ofumq(y*VOHPlGh!W)auct^;QKMv2t#an(RCsc9aq1r%O(RLQ5-AQqMs7wU zdp3Qb6mXYZpFIHpG>7DOjyta!kq z608LJ<^bMLa)!&v$tS1{p75WOpYUR)O!2Tdwm>j9@@yzFta9bjR>By6jE$B9pGze6 zu3OJ`y^y;N&^nCRB{^{YJP^R#p~;TCV~@2I2KI=QNk9bJ?4BOC^<7TvA|Yc8>q}lu zW1H%&!Mj=K_{+tSEW5+n_f^er@hp zxT>gK{L7PnHS%o_#CK?vmGW&`CE)$%QzW-BVGfx=&MJP3z@s&wE$2n3 zcq|EgR~ht>>ednRhD~fDh8YVHz|>*Z`|s!!OH_JeoTXFY&0=%TsC0d@y!lVp1bi+n zZz;xaC+IC9c6h`$uqldhFI%6XGMt-Nr-E-6C)EY%3mB5s7lLPCe8 zvt)6_oj*G=G!ow=3{U08XvA_7@Ek!29}fQhfuqdsYAEhpc}w*CbPzON^IGGBvku-W zMK*IS+pN`uK_B4A#A1M2@Y&n$HA|_xiKEvp`!0}?lBS4x~q5LI|aC=#(07; zEY$p)2>fsJ`9*sD^dx?N16!lFBb_F*%@yqQ=R?j)AANhMV*4y8Dm)wdk$TDOpJ#PJ zJ6zq>PGHyE0GIyzh3yXfIR5_#fm>&R00hV-=Z8pbWUw?rL(x}6EM^CT1uf1hn{ik}VD zE(7TEMlsHPQh!^H_wM;|{DHs^1b!g!1A!k1{6OFb0zVM=fxr(0ejxAzfgcF`K;Q=g zKM?qVzz+m|An*f$9|-(F;0FRf5cq+>4+MT7@B@J#2>gGCKyd<#8*dllz7tew^S{?d zjJuBxwkUPhFj}!yTFMj#& zF~WbVKR5=Cj-u&-db`Q7|J~%F>tg3RBC8$QFvQ~O4UKF$KU)(?99ba)Pdg&9 zz1?g3m}0L$IF*wn`!hi=Cu?`)&bG%YyUwzc)wNFx=}}IHX>}bvvS?W083XMd<7%s9 z;BEZ>_1(i2ZcL53VK^m|Ax)+TOuCZmRd{BfNR)ht zy}c!`-s3vxlb`v30VaBv+6gHPV=RWUU#I!ISN3BDZH$NQ%YN*)0sHpJ>TujQ!qjaH z!Dxg6%5bIw_swc9Ret#@XN+)5i%zretG3=Us!v1^eHdRbg zKOxt!^ap`TzH@W(d)V@EpAF=WA_DlC8@oohB7?fmnjYdd+xRd5n7rOCv*wF&;C!N0BL z9Ab|kI9{it@Jp_)b{K3Z+N-#?Zizk8t(Gf4GW{$>(4)e7wJ`&#wEe#{6%@tzbZM9r zh}E2WF!BrJfRe^we~pI{cHiTy_IS6Y-DP$JpJYs2%}e82AssWxpHAUe$Np!wDkgr?S8NcxM@Ca@wI$gWxv2|K)P*%HxcVzNCE>^LuVoJ_5*nrj9^uQoz z(0K5)!{S|PpN->Cwt~~SuGC2G4LkS4EZs0HB6uLRFu^%8t|s*XlTkJaS>g@QtHngh zK5#Io>i=3U^RB2ix$Ke>#y@WHm|^YVB59@cF$X2qfg*O!#Sw(K9QFs@p+0PV6{1;l zia@Yy=V2}(XMBd1Z(JOh(Y;C#2KQQ28V^<#$+{B`@wQF@HftRAuUZ4g-p5TPP7O?#s-`5*NQaJ* z3U*y6@hM)f<2c5;CU)e!??BmA9kOcz#a9M3<3HIu`C-a;e??A|u(WFl25o=z>r0ah zx2t0JS*NM@ECoR7OCK9Nn;t6)t7k|f!$;rwE&4aQ)?q1N30h{~XP=hP;IZfjEB4Oe z=(h-Tm9SV9ce3+rumrs*HHiRpEaK{4zz&6!oqy@4m@-DLguWiRh*}#*DV+283L)5Q z2{v3W4&+77C1I;9skcjArC&}%PiF?G7zH`4C4cm}g7I(=D9bw9+^VrOA07O)mj0ru z{V?}wxvbx)y;_+b`#5X~Nh!WT-#kz>Gaz?G%{o%BI^bkh+KvY^F)A_1hz79y!2Nux zYh+xpJFo8w+GJ%KyScmJ#an{wfbH-gY#=FEf!3F`ROu%EgfmK+`mgQ(*ALJqd~mH{ z3e{_E)e2{Z2()0lOozy;Y%*-L*4J+2`Fx)t)Vx+b7~=`mg!$26m(Sz|^l8;*6&X&% zW>5DzMCHbHc;m4KguVLSZx1GTjn&{uEyxb`JxqT!_!D;XL=)uaBF=p9q3Yx<8V7;M z6u5F(!R^8}%Pi4I!;4LXChkpF*=9Xs=-u#5d(;T=U=i7HIkxN%Qkd9HcvgE?gxV&_ z4D^tUA@ypR*6jMn9?=z{kfO1w#-+cEL1FR}(&Mr?XKRD4p*qUQgD<$Mk$%nUS*vp6PU9-ztk|ud^VfZx zS}Qbt8{4BwIR3_t=5;rh-?L)o|I=7iVSOXlC4AZHeAvJPS!K{=2GH({BchrPiL-cw!ZVm3d=FMBf)cA@_nRyFUUADzd^aDgIP|V7 zb)>R}2d26CA)7KM4I|0rF6igc-Zc#abv2GwYKx_qum%DR*lDn8s8$zxZpgcB;GSy6 zXMcZoq(KK3VQr6WT0)zB!_4rM33@hyK#IMN0!2H$b$?c4ihA7f9q3gvVt661k@+8Dp+ z2^i?Sa0nE^0@SDeI1g1+HDaJs8;sY}R-|S6%+^#$Iy1vegKyPf>qdXU?w!Hw88X0M1%yA;(E!JAI=Ri#~w)|Tf$Ir#X^e^ z^hYgrK#|D>gzRRfRhFPMpO;&ieE$Zjrph2->y&bEFyf1%of-R_l2s%<#KbjjYrtI$ z9f6qIm(*MLlz2EF$mpW6+GQ?5bH9}GE9!g^+Np0!M-*Zb zau8`AJU82CGE*zC<4;`?xMD3hcx*H~+FQ~BkfoZ%AEPoOuYE0es$lLyP`IRm^)L!bx{ zK<`wIOUquQznhBBeU%%~`bE%+GzPtq161$&$+|gmW{XmWc7*RPYk2Kf#G4_*;5A1> zDuh(;8Z+_mKUbk=AVN_`CeFX0O!n8cI8Bd^;C46MVqZc{yk)CPo^RXfSyPDqPk!}w z#rGB3+FPUC*aC6-BV~c$rcjbT(h~Zp_nXpX7`wHFg47)EuoVkR|@+ydFP!#IdqlwVhOR4jky*%)9U@m^{ zI#^lM<|~IB-q)AV40r)?ImoDTt9*A#zSC0y?Rd(|$cbPO51_(d{#LeWkC4&tGXVVw zOsZlV(X%`Yf)ijch-Q(A3r|kZu&8RrEOo?D z7pC<<;V|HRpPI_*Uy+BQo|{DQ*q? zz5}4y!S6r4L#W5w_pWArJ=SuPQ@UzV`#>L58~|{vyVNDfj-tff5o|#0ahKzcdTM5N zIiad1S1g_Fn8Z_#Nrd5jUA{UkVbtXL5RXxJ9Mf(DL60q3B>imtmkfaaNXm2vvLq{koe1sF1ZPvNxWqh?Vi!QCVJoe3glu&%jU< zjPI}LhFqCiD4UFOJbk*pp@MTg5puO*DY#t`gFuNpLPJbYjip0_yT|>XF!KQ_X_YV^ zE4+5Iv;Hesr$hx5wms1{(7RN0hyMK35v#p{2~6`H<68jrxOu9s+laFEOO}tun~Kt5 z2h1O0Ce{bJ8Ke43M=%~nVI#^r@8!l+(#-RV1`oq8_hA7aX)Rd3P_8H}Epxr7f64cp z;ME#o4mng^d47=B|0EOlHq5?f^^lQl(WBi0Si`22v11`x!7w)~Bg-XMA+6Tt|(drZg=ibhhnJ3)P8@Dl&EPcyC^|p+dM_sCsW`-`60v$%NSg=4v~1 zvIrJd%foF@n}{>*^}ue~mSmv7iqwo(`r-GiZnxPc$?*e#*^QdXzpHGe*DUqRbBPGa zkIL_Z3R&g~mhul68=WB=u07Y6IvXlSp;OR>dyeUn?hhz9`q-v=Xh<+a@PKx-|?4U8JjE_>A(d)ByLgXG+tnBYgNqWGPCda|dl1 z)YxaiT7`sR_WQV7RMu2ciiV!7qABlma!HV%Rb-eYuEH0t{Ugz@{ZnB*%@K#ELqmF8 zxJ$uT{K80?eZFG#eK;Wvq$UN}rnqT^ljND95*wW@iQfHyO=|wG0!}jzr>pgTXDfr? zCcveg?b@NpXa|(%7udpA=hLt+MUDaou7b{{8sSsBA+rB~;QsU=c97?=8C=9a;T94W z(Kdeu=a#JiQ)!FJ(%Sr$Mx$fR(KH_fG{!ugX%Hw<$lXbH-$^ya& zbEbm}r|)!^PiD?wc4di{r5k3Q{lE>K?{gar`}|w-Hmuuy4HViqAsZ#b!P>g&*lZ|P zN@b>thYYq>HKHB9v}EBN-XjsvM%K5so_$mG$JV2#C9S-V6U&PKj&(d|mVtORieEUr?*USD<^tN`UWZG-@O zkl#arP={XDX(~Z4Obn4=UkBu|=VV=GNKavWe+-oWu8DoaJ8sce;mKG;!zIqgq>Ha# zm+GMSkPvaU(q*G&3}BV5S7#!3^=xG9W9T8JbZf^P#N`n4xi7C^*Z1$gwp93xC!fz% zAm~|Q_Pd#t>lre!n^DxEH$4>#8`1_PcdAb2+)y@P92A&eH&FM+(mY;wyqh1qW&2Xk zNz*WQ5~IS+2!E|ds>0{qvVE8 zVtV=@#f^|y4fB7yke)AZLdx1=~xb! z11%SNa-saXFZEbv1>|QEKeBYy4Ww3w5aA^vl43D?L6t#*-6QMi&gbNN^Xu~JG&$Q5 ztQzvaq9?Kt?_b=x=jwfYY;rCO zLj%~v+kT4$Jn>_bLD6*CP<_bL8e3XK5r#*p-9wn~^qRX_kK|S4Z za@`xk1&dssiQxgd?euDfQdUnX^j0w^9PH?@ik=tZRTmUH zKHQ*E$tf>}wm@?7rL;aNdJ57Op@TGJs^Z*;{V~L3^n-%chl&I707#OCc zdcI;Lx^UmKsv?mq#{aM5J@-;0!I)fn#9`kwL*X#gpi;5jG7_AP_f9^HAh>!*f=N%S zygzjU1lI6n)bi|*hvRWcr%O>ULeq{ zYu5WeMh+liv0ZPD9>YQr2-BAy87%<0v-I{_yMFiNH_lPCUaeBMgPYOq3|r{12Td|E zm=c{Ty@U)4jAs&)2YH|$6=-+g6w>XjW= zm3^JE2in1qQAiUGYp0ZtHr=F|8_=#_>q>n3%+%BQFki#yaJ)i}g3;3op2L7ek*mSd zlvbE3$_t17-fU!jw_^WdBlX6I#w^Sx`8=e)5ARX+I_PKrU~t>bi>q2i8_89ND;z?> z4lafehh7*yjpIdknG05S>syi^Zw#%cJZ<0=$pi>FlR8fXMq|1@#{f@dM+tSTAM#w~ z6lXitB(07jTh#z)aABPgB!EFEfI(NXJ==@8vY;y$LB7X8zKEEZ4{I=Wd>Q6^dXEt8 z10?<$&?6NG1%IcZeG6)++G)a3CtYuUhDm@7_zu4t%%~q&ZJq zbQ%dodFc+V&0Tx>Jj|(x3vYNKG^H6J4OPiu&!Z->%R6S51Ij%-N1@fF&ypNoycy6{ zwncj!)O=SBmP4LiMFxlYCDp*d{qKAN4I#&paxXRDf^3oc4-61qG}r)C|6SP?!d%;xRo*0S6r#oQ1xgpFLuD z-wl@~sPkZxo7@VUrYK1|Vl?HX)#5lSiZhL&sHq3nHqCh&UGRJ>uYM-^g z!7+B;KAa^f%uSfwkS{zxG^!R<<9}z>{Ir4QVttsAL%q*WwkN8!_-DeTZJ|xfZkvUV zZ-|3iHUW_CXLNT#>ph`L>U`l6$yc}>d}aA{0?MKTR)_X$l%R(h;X7O+$HP$~+acYy zsr}5P$bZCO1~EHbkRiA-Ead58awS`-D+>(q3%Xa_be{KNAS;UGujE-*xB|5-)sFKb zZ-l3O0A9(&y$pvflatpn$h8`vfE)U6?B;)VOk;+4>pggVPMmdz4fh#AEPi&>KIsm{ z|KwTcqJp=pZ6%dAxTPEV-6o-heI&%Ufg=C7;`>FC8HK#R?(zbSekv*9RY6PImWlUN z*NR@I!RiIj>zano9O{PE;+vVMIJPY2;=n+-KdrHE3w=I3w^6}ZPaLb!j2z>{{o zy9XTk!F#mr7=47^7Z~~UrNr$ODpM@iDtv647|nAU$u{+_Q@mW-;gNb5P;>gb`LDN; z*YbLrA^}DJ#|b1xJp~Bt`1e{*b$x35a(PdiC`_#v&n0PjdBtyZ>Pkh>@{$pwkit60 z0*b4(qsf6#l=vo!`T={96x@V<(+IpSA<}l!usz?t6{-5_79!{c&z+!L@ zhC9;Y)9wh>G&V#SUU9=->2<2+JwYae3Z%~4dmS*=?uTh)_LGUmvKX)`Pv0P@Jql^Z zH*#RIEh(99wxa`kp_AWy8qk)314TR)!cLy))8Gz#bQ&E}$Q&FaLtsv*l1o(^>{s>% zA2X94L$8#RZ=nOly@{)gQ?yK6P@Jj#;n|8wbfQu)x89el`WJL+Y*>hQ^-iro)U5cF zK>}N>=qKOpvxPKUL%WpQo2n+0YQ3Z)4Ao$|4<{ogLbt|8d8wh>0`bLLO7fJfLhpjq zFaW^*Rx{wD5pPL}q)%rC{@b`^z5I?>U9e1q50B?Dih~G-$I>J99Q5tAZ9GuX%he5w z$>u|uK49EPUgRN4WX%@^RP=OFuvuTrtO{TG^t(ADxtDq!uw_h}(5S02vtBq{_3<`c zJb1shX3ykQ@RJRUKd)(HpuTFvFEYUg`IzQZkGy=^I;1B^&Kzpz9|HLM8}^kgqM_1n z4#FsBRJbcDdJcv2M#i&jZ@VE{U9T{(%-hw^7efed4B48CEne!uG$K<^Tednl5nhWnUvnZQR>Gvg{kaCRH21?iGI6OF^j>vNr zXP~w+fyo~~@~f!_ReJYZHz`-MPh}sewIbvcOp4J3 zjAU&-b885f5tkx=P;mANOK;y~#bo5P0$Iy;|^cW)CRbiFsyMSLMWq@@30)% z5LGYRA6ol|TFV=#evg4%rNzbayA{^DOXcXV25-hxysf5BpdlNNn01r$p}av`X$6X3 z!r`uWjc63%d1(#=B$ZJM$)p{#B~y>8QJ7J`E}s@EM=M zZE-O4NQ__B$+)j-9Br&B_??|^@z??mypYwc+i0fBXj^!wn-t4*5dU^!ZX1(EziDPH z4>hv?H0ZsK>#hZ4V+`BxN`$xh9DEMz?ctO)%^&vk>#>K)c%!RS9OgSj}F zE1r{scaxl-h2RSh&mVJM zO%UA}%F@Xr07VNYb@u}#>f6<&AY=c^R{TZ;)_JS-j#Eb3&T&IRAx>pdRi#*&2n7Xn zZI}D?_vsg|_OwmVZ*u+wYOuL!$F!`@Caq-%fR`paB2uVMc^R=yO_>b2R!&h95o(Pi zcseT$cMFGtu_fm%2V~F(X&GVB`b)H^kz^Vcb$bioWN^A&iSDs|Tc@(srLKDhTl^jJZ~2K`J7!1Ny_O7)Ko*%O+y zjxX7EcK2CF{Uzj|AAZK~aAOBW-Lu4)zjtvG@x+9(Yre{t#4nZ7(=vWe4$&AhR{Ivh z6taQg7e7!Xp9Ser{a+yuT_WKd00_ih)9fxU!R9tN7e4*&>yKB){oI=$PMlWi62`WN zq{NkzZlYHdFQ8w$95y_pooEJWiMUhs$cVj4AfVVljaF5lmMGb;v|HcyBcLv~U;h2*DnvzZz#3m3VR(pHA^2E8 z+73g>^I!UFPCS#i{ieqf_oW0pja_}`6%B$DK3miBdwsLt zSg)EGozrAUp!`{_TQ(s!zp$fCI5CZ%VrVS@S?YM#9L+@)4Mmn>+-`^dT*BggB$f5+ zEuJVDJ-XxL7h(9%r7KMwDxI12ox$$Y3#D01elD@kB0zJEY}R<8ddILu=V9jtnm3kB z+b-YpFi;Y9(8vc$$;(9N3=Zps_&hC4UCuCy-q^T-k+upX*;>E66+BQ(i}j*!l6FWI zYfT%pqjSDGt1rQ4@Pfo9LH_=mlEvqvbZ||=u$ct?#IT6s-Qzr}|BJK77@05A^p#%i zPVpVGsz+Y!eW83|H|_d4|5t92-vsZT{wSwi}4v1QF`6nacQcwRIL7ZDjV?nU*-KgxMNr z_+@gZ4uTufdIl8myExN?_~_M?@=ar%)VIa_WBN(@l$l9bqP z&
Jyk)BN!sWQpD&x@DYO(5dsT6z1`Wt{?EI22jyhLmDecZDgjK&sG4kl+25#+uv zBIWz(bHpRhIFZIZ=NPPypY9EFKWDODRM?KuyC$lv7qRfCY+BErhx+79D=4ikDu=vp zAWB>_6urU9y&?A#-lDcuq-4z`nbX43qGV^4-Ed1Z1Hpat%R7Hg1!Dz1i~J@l{WF$< zL$G7T{yzNn#^ISyQ5nedEt0%o3BZ+=r>c4U0mLc*65 zuP$$yN`NYFA^+^S9{n@Zh^&+oaQ3!-a> zySI$yNjtp#a$H`$2P1@e!Lxu+9xQIxky{02IF3oYHu#q6>PLh)d*Vry(OMlx)zA>j z`B5j}umTBb%o%j9zY4E+<$hRm?0eCOjUVGLL}tZIc+GlNNzWW$S4*QB4i6dNW}bCB8s;3YthJCtFeYR|g)xG_3j z(a6nVns6f=se!d_5J)}W+$u1nR{EE{JWYzpZQm}qo+q%rv-3z6GT|bF_oroqt-B5q-hwFxt#>lHPRECy$B)$)^ zV6PDu=brcrS+O@$G$@UdQ|tBK{3Fi^-QfR@(e<(0|AWJFY&miqySYmct$}>DSD^D! zFAT^~u&(80Ne?f`{Zx(BHgM4?pk<^s&aBO&Yf%lBQpbSAUR9a4 zv%v*9MHLBi`^Yz@A`J~rysMHr9g`9ZTW;EuurRzxLXA{JpRJ+nLw~$|>`Zu$LO$7` z6^|YX47RtV&>>=dw~+XELUcGD5r4UzbCLQ266#>-R~(GL61g8_`0@J&*z~$uDo~0P zEWDp93KJs*QQMT3-!NOE0a#-zO2@AtphQ}&%V)D+1=7lMPt`(d+UtNbvI63`0zuS*Y=aM7FCyC*10Faoibi zTGRj2)KvyV)ivP@NC+Y-DAFn^A>B)ffV4=r)Y4t)HaS3*Yoz?IpRah^`5qSRG!g5$5*h(0E93ru1RF+ba! z=)uG=n|r~lmV(xtzMi>a^mnuIestc6gkItOd2|j^X1nA(U2+l_aF>XmKl#TMZH|y4fsYFci;cyBAkX7r-~M7|9jfr1Pxdfhq50HxR)s$Yp=8%bj_HW%BI^)fYr)lD!V+6X*Np2O?D# zRer+stF}jDJ8w5b82oBy4q_~?KbJ~%!2STP4azW+cboPGIM z`(&~7VZ5|u<#mI=WjHTg9u^LNx-%La7OTEV@y5uDcC60lXUzMLi1aCxb)C0amJ z{|Jb-JU`)E@l$#UpXR;7WzQoPE}(&HbMxty9T#8=b>W_%V%;@9n?oxt zzb&+^1)2IzV~O8U0Bgg>+BI#;J{Z^>KQ;6uvqi_&c5F5tyUx8{d-iAi3;@)1^>w1W+|osw zh3-lfFT>PNM>!H7gQROh)HW%SSZr;Rj8vKNnr&arN-t8p`;g9Y<0SwvdC?*^hK-!3 z?njNi8D%YAPJdQv;K`n*_o?tFw@GVM2wNl z8y++{nHT3w$q1#oElRri%nR>XUE$}b>!H$5MKz*|WQC^Hw`Yeg4sr}pX{_nctH!9w zk7$~Xk4larv)?sc#1R4_*^y=o%S;tYu_`hGa1>N>)UdaDqq)FlT}N@GG>`OEZ2Qam`-DR1e1X?`W+$W~Jrc*l!fWdtp#v2i;Nw1EiQ zT!uB>s(Fd1oavds{&}_+l=Wz-c!WcVf!vdQEfwLd@(cjfwiZ9@Ua8l-W$xp~DJVrW zs%pBbWs>lk2C_F&f^D>f=yWgI<0@UtM1iy@xz0v zes)|2+y`;VfWc>ad{}Xu%B%PK zgg~woM<`31TB!w9SdNC`v+kemG}pP)`lZ*=AF16hX#iQw=mW*x4@N0Y36b=F$;F4Y z0ATQ5lGHC&J@cH2SoJ2M#VnE*)YbY~&Ewf8pi6DkabIni3ndX1%JLi(mmJznH=Ib< zVl(`z$e^hqeHlu}dFyz__fIhVpt~LN0LTqqIz#X*`%7aCQlCas$H_6@37Y@VQ70?M z()p3>)5Vv+F~n;~67-V^bLm#8NE1B>HFjaSY93h`qiSi!g*@$CZ^S-txxL|hnr_m1 z_~%#sFUaJ4c+vE1tXN9wbc-atN6zc%Uq8o%-4`Ffp9`a@uM|%lh*o`)>g`|aG#SnR zOBM(|Yi{0mfKOk%tjIp;9SViO^Js@WpttIYnZ`Qe0?$7MT7luVDNUl9O4%3qfSo83 z?=Tze_`o2c1p1W~6=BABIBnpdUcqAk zsHGLYfu;-QUOetKp@n@Hhn@B6d+pV=y~zzcO=Z-U+%?o-2Y^wzEEOmM&UPG?uD$B8 z0NK`XgAkn`yd-wNom~fS4DQ+`6JreVY#QI}NWo~dsWN^@SD-M!^x$rGVD%2(;-AjY zPcwe-TaBC?p@#7#O{dToY#7F#Hlq{Ll$<7RhxU^b0I~|AuCuRsn-4r0Nv~K=w^xYl zbuj62)Jq$o2fh*QdGM972{&RTAozAQeiS30FC@$NA^)`SH2~yha%eZ7^r%SO>6&*i z;$x1!WxHI;9&;I3K5CEjBb`+s#3KiCvBPgfNL6PhEqEli+PNu9_--Y-TEA$;|UdGP&GR|{C!^LbC{b7FCS)>!QRkW`az8FM7X0+kX_k_y48?c8OsEo_7y zTkv3~QXcL~C=7Z>w*NR??_64;_xRDzVHq;URnNNGF#gmaRyLcH!&Qrei6HgGTr!yb zkO5UsAXwauGiy+y&w)H;YQR2T;k8fPHDB<2b94blNy$l}RS21?b=#%`#*+hWO?s*R zMB3tSJhq+hmy~4kV|~gTcUxm0fTXtI^;dE7oxlx3K}2u1qX(!rg)PhJEFPGg`|qZ8 z2#pO1L$96&z08@IrbD?NLezbKLon4V%O5Bf!f3E;#D1_aDxpKVHf*2pg6 z=xWk)omc0-){zsBg|NPwqtS9vJyIFBkM>~1*1kB8?-`K_&}ga+;+qnlNJ9yKEBDTL znwnu!Fq0zHVa5ec6=wFdfG~DpBQB%*Y zYP{LxArq_dqBn|0e$wIM9qz}Kh|P8inNQmEc)6y((w)bZU9=IGY}9f4`VKkcV$uTw zU%^ft@H?l|)q%uDB<0#nxSUUQtk|6x6R1Hkr93XXny(SC16S z^dF6{wcQ-euQB!1QE7W66mK4u40+mIU}6|D^(g zBPj2~%UsN}$w<2i3onwNWVx3<2J?Grr<2R{$9fuZ%B^!e4FYNLvRCTLPHFr_P$??0_;Lyz0FpqYg*9jaxU`^mnE^SlKk4~eIjH2sf$f17}L6nkB{ z?OpC(ySdhG_j8@S%m4#<^X~{YyW1m2A8lEKnChhqYvFkGAY~$qaEa^dvTJ4|!AzRB zdKxyLuMK`uIVPE-e-*X`x8#2wA=v0nli<%7AsqYAE&LUY(cNwUC`xRkW8QW(K0mi< zfqSlt!8X%fHw!j$Hs+RHV(b(Mg7xBW=I&=3bANIZS;X;`!+rr^V!{unp^cAayUm$% z(OMmJoF8O@76n_cP+ktchns_Yt^*KU_g|O4^0oPRve@n$uV4c_4;01d_FxakA(mkB zJMc!N+;}KHBPc`iB%7P`v|x(_?MkUlrxM4Tn3^)TNaE{pftH zwebb+_Yhs5zu-Fr!F}8?O$Zw|ApQ zY+LeW@Xg10JBDLEe-|EF zqMoo6OSljfAC&ELkT1c7fOY0D!JVPuE?!Kk^+?7+WsvzpRZ zCriGGccmtB@J|3+^k?srpC|^!+MuFCDer{6KVmr0g;q$WaRm9WfE>N>{ahO;NpE9 z!1U6k4Qv%+d|Hb{>v8IzCvK(kcq$O{Ob9jlbBJ;nR9MEqp72vtst^Hxrfm~oVbD=^t)tZBDQ)){v zGS!8%!D)Rlbh>78y-h~yS_+tKlw>cyV+YEl>9t+DLcrqOs1IQDPX7U2O8 z?ZgQHyJCY~(RnjedFAYPiyY*`Qr-JsTF3IXs^0egQZt>oA%qrgQI(kYfQIOqJq=BR z71Xr1FgE)RFwsN49uuIQPc~+zI8-v;=3KW@k6kza6|D2vxmZ62^S0oT9(R1HJoY8f z!rA*t8`Woqs9$=~+E}Ns_0nPlbEwFZFpSQ28T%4SyVUCx(HjcED-)fUCz8~gA zGOB^6E4W-l=?5i7uE_WAoCs0}*i3M*R@#4%X9KyrPf(i8XLEj=2;VAb5Eg9gQaDg1 zcDC6@;Fq)NUKfA(*Xd5;;n7y@vyFqh9OrC33AQxu(K!#oYf)G%h12aZ^Cu;EvaY8r z?8J>F^#5_{mkhXx>6Go7XiZFv*ArF0G|d}%;JRaSMihP0@%eL~wTgPr=9Pjeu=Y9(~i!kaE z5fK^9q~>L9Rf3uaQo5Fiy_F+uo1dL&yv%`F=Skpa-m(%%vgtzGn~rQO#bcuj{|%UT ziL%?IQ;z4Pwo>Yo3r17TplSCD)7q5mJnebF!SU2dYE05Z%!9TQ&z^ zw+QY1_4p=VUvB65mX>df+?SOfmSl$KE4!AT#zka5ZG<{sqrlhhQhuYd#9}u@06;R# zqKX7CRg`o3-dO4~+maaem2P)lM|8a0vD1LI)9Vx^_At=u>o*}%6P?}Vxff=px-qKI z{a5;AM%19*yD^4r!KXJwsB_+=P$T_c@h#2q6aLJaSI=?TTSQ z_bD9h%q8DwV1Rp1d)txyywgSwzzM)j4jJZR^x|BY)M6Ch=e!@WT*N^2YkXp2B%NC# zN?{LK3AE(=HG*Gv8Fq!d(tvYXH_PyTr8(y2Ef!^%tT868-&S^`du2Dm5H5Pg_ve`n z=!1c~)!>6wmv0Ajjicn=zStvY2Bb*Bo%$kzhsiEfze0`|U!m@loCFncTm{}y5pE^BXSPF9AE9;=46y;i7c!Pp|XUMiC3uDhZ(k^G#n8Z*X%Nq-1+F zbX`isa{cJVI-J+?=Y!dzxfybPa#QSP{~2;hO+%>}@rjpz)REye$q1dlcGYPJDbY0} zQD!j=UTHx;pLg~o8RAe_8O)}dU8}$_ku!9%B_>|MC1F_LgL2my_L?*g*ZfFRdT-FA zjhi>A@n;$pIdR7{d8qlw{PuiQsGBD3%qSyjRO7a*mumNW7cym0uD!dXd+`_|xhc7R z4tfzOuP2n}ZA=treHU8qU$Net(!6@c@Ll-QCIfqJ&|W`GNv7>p5KX)B=d(h*nf+T; z{p9fjV7Buri!o3^VK0bnL^UilPmo^y-tZ=*Fa9UZSV6J90@jGcb3-?pm9G0sgpj_N zpj^2FhaeN2W{@LkDD_2zAkHR%RZ}I2NT6tbYuIt6`<23R ze3(zkD_8Jm+?jh-5MXyJy^DC^?QyNR8deqnv=b;sT$y?6Gs{C=qebHpsLGCcSpa7 zL_W`0pc^l=50&qDG;tC)e2}|F_HK~;@tw2VdFYA_IrKyp!B}ClIrhst)r@7SihG@f z)y|DZz1ElfbUiQUo<{2$^{dV5r$FqP>O z;4h<}NW|N2D_MCbg%$gLQkHEcT(H0aW2nW)Y$uK@IJUX8cs?m4SHYdDdlUNF3`^rq zSj&B?f0uDZJwljruID*@9>L?|afbvw34yXM?fc;`BxHN}xm94(g$N5~vrNu2DsH?t z?nV#Y?1FZtsx);^5np4z_ZX2XPN_`Ga`fzT^5JzSn4wL%(p!+@jlWS9bB8+dTZ2h6)C0b1w!&qi}H`}CZ zQ8`DuUPDOv#}nA$H_kRLO)au?i;(o)r_qHRamseWxMG>a%Ri(}y6qX77#a$rVhOz! z%3&7I$7?KYecgc@WjE?1)G7BrEN=%-LsKj~hLC@+YRU26*3^~m&)u&frB&zu7%Zq+ z^ERl6{PnYzpugEdPB!Thxtb(`y{y8C^(lKLJcsiT-F8DW1fp|oY^krda@|SH6J;;d zi=xEYOC|P3>VYaHe0j$cd&_+!qER^wFsN(K@7GsYjk@YowFn1>k}yeKLA;?$+cIxuC}Ld z3wD1{uBD3C#8SZ%fUH1cPfCIey>LpLFe5~~;rpEKA&WWpkaXny)%!51@v5)$+$ z{aT3WauW1!g5fj%c`FUjLinr3qMV#CUE`!a^Ll@VB69P@?ue>q!~&(%#lpy*YFI+D zkZhvytO;wMzDR5q^@FZsS#DJ`FmOY9vZD>c2O)Q`>*Z$^beT(-!VMm3eFjL?vVsKD zFnrbnI+)puB1?6C^30Lb#mKEJb5|o)#q5UBuZD!-js$c3;7;77n(w#eRKVzGLyIhE zNkbdFa)$uVY?HP!Y+moWf*6Kv*U3;svpRvW?2I*kV`lp0Bx0%AH@kBCesVL1?CzCpX)z<*lgYistwkTMhFv^mU;s z)}|VfDrV&mTBeFjP5X<%bb_~q|hoRl}G)=BU#AYPzhJ}ZgS%+#e08tRmf%%ue+hQ()TVaAWL_; zv$Se>wsVeCF(S@eF#PWuMNgx*+!B|^P}K~5Yud#auFR5>>}_q$;%e-m2issKuM9n` zr>KE2Aa4U>thc{qW=FFVejCgem-u?0VOgulW>9sT+AjZVF|9{5C|9+U|$O>+d@;!*F|r{b99FS|o32?^`Ah?az=6ink;SC2>p} z?03=UY8O?EP>m&5v+|BBS0by+jd3MC-UrX*G3kHK9u4vxyPW3QM4tEhDkbs2_DTnl z->Jbv5d2F&TtRELLwA1>wfcn%lfWLmIR_yzs%lTihFUHM1b?46`-ArGhUI4M_E(lK zkBxm8??ZutuWAe#TR(2zvSFu6~hG6C6MSD@k(r;N@ z>3d^$`x`*vK62+&KhT?8b)@V25|otu%UqbCZh3B!gi;P3>N ztkH?}{&qDZd9(f$R4uF^cguRC8!sSR7#SEE=o=X68ye{vnp+tgSs558K!Fm_wxX0Ys~{IQs9ivwtx`rwNr9EV zetCJhUb(Seeo?xx^|#0uTKVr7^KE~&-IMVSR9nfZANAQKal z@=Hr>m4GgVcpucqiS6q^qmz?V9Vygtyua}vbVr6J*;^buLXku>WW@2dQYU*fd;OJ=PYT@W& zWNu+4ZC?Vt)0Bv?jEy^rQO>ryA&s6|>*eVmZTa0j; z2i2Q`+bza8_38s1qYsK!q-cf-0n-kM2~V^@4m{bX<^j`s5in63MQrc{=CEC!E{-7; zx8B^@?IjZ^bKqg=MuXNxPC^A+BMJ?34TM}5|KJg8%1==Jq0ltde}Shjhgc zBwd^Eh40UUSHG-2NqJa45HBsAc->*@l|O%r7uM_O@qhV#tbX=VTN5OB_OZHB?|2jQmF8j?|o=~3P@tBV%pzUd(y_X5agFMj+9?Z;SpbF_kAc9B628 n1Y$yl2Y%)kp^j~lTlnWvqUJ7=Y>Ul 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 X!lw4y|2_Uwf${mHs$8XvQQ-do;aw2N literal 0 HcmV?d00001 diff --git a/yunxi-ui-admin-vue3/src/views/mp/menu/components/MenuEditor.vue b/yunxi-ui-admin-vue3/src/views/mp/menu/components/MenuEditor.vue new file mode 100644 index 00000000..5df1785c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/menu/components/MenuEditor.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/menu/components/MenuPreviewer.vue b/yunxi-ui-admin-vue3/src/views/mp/menu/components/MenuPreviewer.vue new file mode 100644 index 00000000..93a19800 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/menu/components/MenuPreviewer.vue @@ -0,0 +1,226 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/menu/components/menuOptions.ts b/yunxi-ui-admin-vue3/src/views/mp/menu/components/menuOptions.ts new file mode 100644 index 00000000..d86dd789 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/menu/components/menuOptions.ts @@ -0,0 +1,42 @@ +export default [ + { + value: 'view', + label: '跳转网页' + }, + { + value: 'miniprogram', + label: '跳转小程序' + }, + { + value: 'click', + label: '点击回复' + }, + { + value: 'article_view_limited', + label: '跳转图文消息' + }, + { + value: 'scancode_push', + label: '扫码直接返回结果' + }, + { + value: 'scancode_waitmsg', + label: '扫码回复' + }, + { + value: 'pic_sysphoto', + label: '系统拍照发图' + }, + { + value: 'pic_photo_or_album', + label: '拍照或者相册' + }, + { + value: 'pic_weixin', + label: '微信相册' + }, + { + value: 'location_select', + label: '选择地理位置' + } +] diff --git a/yunxi-ui-admin-vue3/src/views/mp/menu/components/types.ts b/yunxi-ui-admin-vue3/src/views/mp/menu/components/types.ts new file mode 100644 index 00000000..b9f76597 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/menu/components/types.ts @@ -0,0 +1,73 @@ +export interface Replay { + title: string + description: string + picUrl: string + url: string +} + +export type MenuType = + | '' + | 'click' + | 'view' + | 'scancode_waitmsg' + | 'scancode_push' + | 'pic_sysphoto' + | 'pic_photo_or_album' + | 'pic_weixin' + | 'location_select' + | 'article_view_limited' + +interface _RawMenu { + // db + id: number + parentId: number + accountId: number + appId: string + createTime: number + + // mp-native + name: string + menuKey: string + type: MenuType + url: string + miniProgramAppId: string + miniProgramPagePath: string + articleId: string + replyMessageType: string + replyContent: string + replyMediaId: string + replyMediaUrl: string + replyThumbMediaId: string + replyThumbMediaUrl: string + replyTitle: string + replyDescription: string + replyArticles: Replay + replyMusicUrl: string + replyHqMusicUrl: string +} + +export type RawMenu = Partial<_RawMenu> + +interface _Reply { + type: string + accountId: number + content: string + mediaId: string + url: string + thumbMediaId: string + thumbMediaUrl: string + title: string + description: string + articles: null | Replay[] + musicUrl: string + hqMusicUrl: string +} + +export type Reply = Partial<_Reply> + +interface _Menu extends RawMenu { + children: _Menu[] + reply: Reply +} + +export type Menu = Partial<_Menu> diff --git a/yunxi-ui-admin-vue3/src/views/mp/menu/index.vue b/yunxi-ui-admin-vue3/src/views/mp/menu/index.vue new file mode 100644 index 00000000..8cc8f586 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/menu/index.vue @@ -0,0 +1,401 @@ + + + + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/message/MessageTable.vue b/yunxi-ui-admin-vue3/src/views/mp/message/MessageTable.vue new file mode 100644 index 00000000..ebc3d749 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/message/MessageTable.vue @@ -0,0 +1,145 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/message/index.vue b/yunxi-ui-admin-vue3/src/views/mp/message/index.vue new file mode 100644 index 00000000..adceec56 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/message/index.vue @@ -0,0 +1,152 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/statistics/index.vue b/yunxi-ui-admin-vue3/src/views/mp/statistics/index.vue new file mode 100644 index 00000000..37ca2a00 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/statistics/index.vue @@ -0,0 +1,368 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/tag/TagForm.vue b/yunxi-ui-admin-vue3/src/views/mp/tag/TagForm.vue new file mode 100644 index 00000000..9a85bec9 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/tag/TagForm.vue @@ -0,0 +1,98 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/tag/index.vue b/yunxi-ui-admin-vue3/src/views/mp/tag/index.vue new file mode 100644 index 00000000..df76ce98 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/tag/index.vue @@ -0,0 +1,154 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/user/UserForm.vue b/yunxi-ui-admin-vue3/src/views/mp/user/UserForm.vue new file mode 100644 index 00000000..818fdd83 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/user/UserForm.vue @@ -0,0 +1,102 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/mp/user/index.vue b/yunxi-ui-admin-vue3/src/views/mp/user/index.vue new file mode 100644 index 00000000..6147351a --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/mp/user/index.vue @@ -0,0 +1,181 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/app/components/AppForm.vue b/yunxi-ui-admin-vue3/src/views/pay/app/components/AppForm.vue new file mode 100644 index 00000000..b99766c1 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/app/components/AppForm.vue @@ -0,0 +1,130 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/app/components/channel/AlipayChannelForm.vue b/yunxi-ui-admin-vue3/src/views/pay/app/components/channel/AlipayChannelForm.vue new file mode 100644 index 00000000..46dc4312 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/app/components/channel/AlipayChannelForm.vue @@ -0,0 +1,316 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/app/components/channel/MockChannelForm.vue b/yunxi-ui-admin-vue3/src/views/pay/app/components/channel/MockChannelForm.vue new file mode 100644 index 00000000..49cb3abc --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/app/components/channel/MockChannelForm.vue @@ -0,0 +1,122 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/app/components/channel/WeixinChannelForm.vue b/yunxi-ui-admin-vue3/src/views/pay/app/components/channel/WeixinChannelForm.vue new file mode 100644 index 00000000..bafa4bfe --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/app/components/channel/WeixinChannelForm.vue @@ -0,0 +1,342 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/app/index.vue b/yunxi-ui-admin-vue3/src/views/pay/app/index.vue new file mode 100644 index 00000000..c949637b --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/app/index.vue @@ -0,0 +1,429 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/cashier/index.vue b/yunxi-ui-admin-vue3/src/views/pay/cashier/index.vue new file mode 100644 index 00000000..12723dba --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/cashier/index.vue @@ -0,0 +1,482 @@ + + + + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/demo/index.vue b/yunxi-ui-admin-vue3/src/views/pay/demo/index.vue new file mode 100644 index 00000000..374464eb --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/demo/index.vue @@ -0,0 +1,240 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/notify/NotifyDetail.vue b/yunxi-ui-admin-vue3/src/views/pay/notify/NotifyDetail.vue new file mode 100644 index 00000000..938a3eeb --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/notify/NotifyDetail.vue @@ -0,0 +1,86 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/notify/index.vue b/yunxi-ui-admin-vue3/src/views/pay/notify/index.vue new file mode 100644 index 00000000..5daf754f --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/notify/index.vue @@ -0,0 +1,224 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/order/OrderDetail.vue b/yunxi-ui-admin-vue3/src/views/pay/order/OrderDetail.vue new file mode 100644 index 00000000..5f2a496c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/order/OrderDetail.vue @@ -0,0 +1,111 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/order/index.vue b/yunxi-ui-admin-vue3/src/views/pay/order/index.vue new file mode 100644 index 00000000..16026599 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/order/index.vue @@ -0,0 +1,273 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/refund/RefundDetail.vue b/yunxi-ui-admin-vue3/src/views/pay/refund/RefundDetail.vue new file mode 100644 index 00000000..72f7a8c1 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/refund/RefundDetail.vue @@ -0,0 +1,93 @@ + + diff --git a/yunxi-ui-admin-vue3/src/views/pay/refund/index.vue b/yunxi-ui-admin-vue3/src/views/pay/refund/index.vue new file mode 100644 index 00000000..eaa17b4c --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/pay/refund/index.vue @@ -0,0 +1,298 @@ + + + diff --git a/yunxi-ui-admin-vue3/src/views/report/goview/index.vue b/yunxi-ui-admin-vue3/src/views/report/goview/index.vue new file mode 100644 index 00000000..1bac2869 --- /dev/null +++ b/yunxi-ui-admin-vue3/src/views/report/goview/index.vue @@ -0,0 +1,10 @@ +