commit 73b22cbe03b8f905d7150b4837af46c885bbc23e Author: eroter <331864945@qq.com> Date: Wed May 22 17:51:50 2024 +0800 初始化 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5b1a804 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# 告诉EditorConfig插件,这是根文件,不用继续往上查找 +root = true + +# 匹配全部文件 +[*] +# 缩进风格,可选space、tab +indent_style = space +# 缩进的空格数 +indent_size = 2 +# 设置字符集 +charset = utf-8 +# 结尾换行符,可选lf、cr、crlf +end_of_line = lf +# 在文件结尾插入新行 +trim_trailing_whitespace = true +# 删除一行中的前后空格 +insert_final_newline = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..e1f7af6 --- /dev/null +++ b/.env.development @@ -0,0 +1,32 @@ +# 页面标题 +VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统 + +# 开发环境配置 +VITE_APP_ENV = 'development' + +# 开发环境 +VITE_APP_BASE_API = '/dev-api' + +# 应用访问路径 例如使用前缀 /admin/ +VITE_APP_CONTEXT_PATH = '/' + +# 监控地址 +VITE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications' + +# SnailJob 控制台地址 +VITE_APP_SNAILJOB_ADMIN = 'http://localhost:8800/snail-job' + +VITE_APP_PORT = 80 + +# 接口加密功能开关(如需关闭 后端也必须对应关闭) +VITE_APP_ENCRYPT = true +# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换 +VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' +# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换 +VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=' + +# 客户端id +VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' + +# websocket 开关 +VITE_APP_WEBSOCKET = true diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..2700ee0 --- /dev/null +++ b/.env.production @@ -0,0 +1,35 @@ +# 页面标题 +VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统 + +# 生产环境配置 +VITE_APP_ENV = 'production' + +# 应用访问路径 例如使用前缀 /admin/ +VITE_APP_CONTEXT_PATH = '/' + +# 监控地址 +VITE_APP_MONITRO_ADMIN = '/admin/applications' + +# SnailJob 控制台地址 +VITE_APP_SNAILJOB_ADMIN = 'http://localhost:8800/snail-job' + +# 生产环境 +VITE_APP_BASE_API = '/prod-api' + +# 是否在打包时开启压缩,支持 gzip 和 brotli +VITE_BUILD_COMPRESS = gzip + +VITE_APP_PORT = 80 + +# 接口加密功能开关(如需关闭 后端也必须对应关闭) +VITE_APP_ENCRYPT = true +# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换 +VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' +# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换 +VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=' + +# 客户端id +VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' + +# websocket 开关 +VITE_APP_WEBSOCKET = true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e74db40 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,17 @@ +*.sh +node_modules +*.md +*.woff +*.ttf +.vscode +.idea +dist +/public +/docs +.husky +.local +/bin +.eslintrc.cjs +prettier.config.js +src/assets +tailwind.config.js diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json new file mode 100644 index 0000000..a6661b6 --- /dev/null +++ b/.eslintrc-auto-import.json @@ -0,0 +1,312 @@ +{ + "globals": { + "ComponentInternalInstance": true, + "TransferKey": true, + "ElFormRules": true, + "CheckboxValueType": true, + "PropType": true, + "DateModelType": true, + "UploadFile": true, + "ElFormInstance": true, + "ElTableInstance": true, + "ElTreeInstance": true, + "ElTreeSelectInstance": true, + "ElSelectInstance": true, + "ElUploadInstance": true, + "ElCardInstance": true, + "ElDialogInstance": true, + "ElInputInstance": true, + "ElInputNumberInstance": true, + "ElRadioInstance": true, + "ElRadioGroupInstance": true, + "ElRadioButtonInstance": true, + "ElCheckboxInstance": true, + "ElCheckboxGroupInstance": true, + "ElSwitchInstance": true, + "ElDatePickerInstance": true, + "ElTimePickerInstance": true, + "ElTimeSelectInstance": true, + "ElScrollbarInstance": true, + "ElCascaderInstance": true, + "ElColorPickerInstance": true, + "ElRateInstance": true, + "ElSliderInstance": true, + "useRouter": true, + "useRoute": true, + "EffectScope": true, + "ElTable": true, + "ElSelect": true, + "ElUpload": true, + "ElForm": true, + "ElTree": true, + "ElMessage": true, + "ElMessageBox": 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, + "resolveDirective": 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, + "useArrayFindLast": true, + "useArrayJoin": true, + "useArrayMap": true, + "useArrayReduce": true, + "useArraySome": true, + "useArrayUnique": true, + "useAsyncQueue": true, + "useAsyncState": true, + "useAttrs": true, + "useBase64": true, + "useBattery": true, + "useBluetooth": true, + "useBreakpoints": true, + "useBroadcastChannel": true, + "useBrowserLocation": true, + "useCached": true, + "useClipboard": true, + "useCloned": 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, + "usePointerLock": true, + "usePointerSwipe": true, + "usePreferredColorScheme": true, + "usePreferredContrast": true, + "usePreferredDark": true, + "usePreferredLanguages": true, + "usePreferredReducedMotion": true, + "usePrevious": true, + "useRafFn": true, + "useRefHistory": true, + "useResizeObserver": true, + "useScreenOrientation": true, + "useScreenSafeArea": true, + "useScriptTag": true, + "useScroll": true, + "useScrollLock": true, + "useSessionStorage": true, + "useShare": true, + "useSlots": true, + "useSorted": 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, + "useToNumber": true, + "useToString": 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, + "ImportOption": true, + "TreeType": true, + "FieldOption": true, + "PageData": true, + "storeToRefs": true, + "DictDataOption": true, + "UploadOption": true + } +} diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..6042c39 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,51 @@ +module.exports = { + env: { + browser: true, + node: true, + es6: true + }, + parser: 'vue-eslint-parser', + extends: [ + 'plugin:vue/vue3-recommended', + './.eslintrc-auto-import.json', + 'plugin:@typescript-eslint/recommended', + 'prettier', + 'plugin:prettier/recommended' + ], + parserOptions: { + ecmaVersion: '2020', + sourceType: 'module', + project: './tsconfig.*?.json', + parser: '@typescript-eslint/parser' + }, + plugins: ['vue', '@typescript-eslint', 'import', 'promise', 'node', 'prettier'], + rules: { + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-this-alias': 'off', + + // vue + 'vue/multi-word-component-names': 'off', + 'vue/valid-define-props': 'off', + 'vue/no-v-model-argument': 'off', + 'prefer-rest-params': 'off', + // prettier + 'prettier/prettier': 'error', + '@typescript-eslint/ban-types': [ + 'error', + { + // 关闭空类型检查 {} + extendDefaults: true, + types: { + '{}': false, + Function: false + } + } + ] + }, + globals: { + DialogOption: 'readonly', + OptionType: 'readonly' + } +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1fd56f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +.DS_Store +.history +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock +pnpm-lock.yaml + +# 编译生成的文件 +auto-imports.d.ts +components.d.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d251d2e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +/dist/* +.local +.output.js +/node_modules/** + +**/*.svg +**/*.sh + +/public/* \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6ca3ce5 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,20 @@ +{ + "printWidth": 150, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "quoteProps": "preserve", + "jsxSingleQuote": false, + "bracketSameLine": false, + "trailingComma": "none", + "bracketSpacing": true, + "embeddedLanguageFormatting": "auto", + "arrowParens": "always", + "requirePragma": false, + "insertPragma": false, + "proseWrap": "preserve", + "htmlWhitespaceSensitivity": "css", + "vueIndentScriptAndStyle": false, + "endOfLine": "auto" +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..32b3071 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2019 RuoYi-Vue-Plus + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..010adb9 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +## 平台简介 + +- 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。 +- 成员项目: 基于 vben(ant-design-vue) 的前端项目 [ruoyi-plus-vben](https://gitee.com/dapppp/ruoyi-plus-vben) +- 配套后端代码仓库地址 +- [RuoYi-Vue-Plus 5.X(注意版本号)](https://gitee.com/dromara/RuoYi-Vue-Plus) +- [RuoYi-Cloud-Plus 2.X(注意版本号)](https://gitee.com/dromara/RuoYi-Cloud-Plus) + +## 前端运行 + +```bash +# 克隆项目 +git clone https://gitee.com/JavaLionLi/plus-ui.git + +# 安装依赖 +npm install --registry=https://registry.npmmirror.com + +# 启动服务 +npm run dev + +# 构建生产环境 +npm run build:prod + +# 前端访问地址 http://localhost:80 +``` + +## 本框架与RuoYi的业务差异 + +| 业务 | 功能说明 | 本框架 | RuoYi | +| ------------ | ------------------------------------------------------------- | ------ | ----------------------------- | +| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 | +| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 | +| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 | +| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 | +| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 | +| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 | +| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 | +| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 | +| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 | +| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 | +| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 | +| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 | +| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 | +| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 | +| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 | +| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 | +| 代码生成 | 多数据源前后端代码的生成(java、html、xml、sql)支持CRUD下载 | 支持 | 仅支持单数据源 | +| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 | +| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 | +| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 | +| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 | +| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 | + +## 演示图例 + +| | | +| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| ![输入图片说明](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680078982294090567/b31c343d_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680079000642440444/77ca82a9_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680079020995074177/03b7d52e_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680079039367822173/76811806_1766278.png '屏幕截图') | +| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png '屏幕截图') | diff --git a/bin/build.bat b/bin/build.bat new file mode 100644 index 0000000..ecbb454 --- /dev/null +++ b/bin/build.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [Ϣ] Weḅdistļ +echo. + +%~d0 +cd %~dp0 + +cd .. +yarn build:prod + +pause \ No newline at end of file diff --git a/bin/package.bat b/bin/package.bat new file mode 100644 index 0000000..f5b24e0 --- /dev/null +++ b/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [Ϣ] װWeḅnode_modulesļ +echo. + +%~d0 +cd %~dp0 + +cd .. +yarn --registry=https://registry.npmmirror.com + +pause \ No newline at end of file diff --git a/bin/run-web.bat b/bin/run-web.bat new file mode 100644 index 0000000..d2fe397 --- /dev/null +++ b/bin/run-web.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [Ϣ] ʹ Vite Web ̡ +echo. + +%~d0 +cd %~dp0 + +cd .. +yarn dev + +pause \ No newline at end of file diff --git a/html/ie.html b/html/ie.html new file mode 100644 index 0000000..4d2773d --- /dev/null +++ b/html/ie.html @@ -0,0 +1,242 @@ + + + + + 请升级您的浏览器 + + + + + + +

请升级您的浏览器,以便我们更好的为您提供服务!

+

您正在使用 Internet Explorer 的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。

+
+

请注意:微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束

+

+ 自 2016 年 1 月 12 日起,Microsoft 不再为 IE 11 + 以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅 + 微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明 + 。 +

+
+

您可以选择更先进的浏览器

+

推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。

+ +
+ + diff --git a/index.html b/index.html new file mode 100644 index 0000000..aa1c86d --- /dev/null +++ b/index.html @@ -0,0 +1,214 @@ + + + + + + + + + RuoYi-Vue-Plus多租户管理系统 + + + + + +
+
+
+
+
+
正在加载系统资源,请耐心等待
+
+
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..5154ed0 --- /dev/null +++ b/package.json @@ -0,0 +1,101 @@ +{ + "name": "ruoyi-vue-plus", + "version": "5.2.0-BETA", + "description": "RuoYi-Vue-Plus多租户管理系统", + "author": "LionLi", + "license": "MIT", + "type": "module", + "scripts": { + "dev": "vite serve --mode development", + "build:prod": "vite build --mode production", + "build:dev": "vite build --mode development", + "preview": "vite preview", + "lint:eslint": "eslint --fix --ext .ts,.js,.vue ./src ", + "prettier": "prettier --write ." + }, + "repository": { + "type": "git", + "url": "https://gitee.com/JavaLionLi/plus-ui.git" + }, + "dependencies": { + "@element-plus/icons-vue": "2.3.1", + "@highlightjs/vue-plugin": "2.1.0", + "@lezer/common": "1.2.1", + "@vueup/vue-quill": "1.2.0", + "@vueuse/core": "10.9.0", + "animate.css": "4.1.1", + "await-to-js": "3.0.0", + "axios": "1.6.8", + "bpmn-js": "16.4.0", + "camunda-bpmn-js-behaviors": "1.2.2", + "camunda-bpmn-moddle": "7.0.1", + "crypto-js": "4.2.0", + "diagram-js": "12.3.0", + "didi": "9.0.2", + "echarts": "5.5.0", + "element-plus": "2.7.2", + "file-saver": "2.0.5", + "fuse.js": "7.0.0", + "highlight.js": "11.9.0", + "image-conversion": "^2.1.1", + "js-cookie": "3.0.5", + "jsencrypt": "3.3.2", + "moddle": "6.2.3", + "nprogress": "0.2.0", + "path-browserify": "1.0.1", + "path-to-regexp": "6.2.1", + "pinia": "2.1.7", + "preact": "10.19.7", + "screenfull": "6.0.2", + "vform3-builds": "3.0.10", + "vue": "3.4.25", + "vue-cropper": "1.1.1", + "vue-i18n": "9.10.2", + "vue-router": "4.3.2", + "vue-types": "5.1.1", + "vxe-table": "4.5.22", + "zeebe-bpmn-moddle": "1.0.0" + }, + "devDependencies": { + "@iconify/json": "2.2.201", + "@intlify/unplugin-vue-i18n": "3.0.1", + "@types/crypto-js": "4.2.2", + "@types/file-saver": "2.0.7", + "@types/js-cookie": "3.0.6", + "@types/node": "18.18.2", + "@types/nprogress": "0.2.3", + "@types/path-browserify": "1.0.2", + "@typescript-eslint/eslint-plugin": "7.3.1", + "@typescript-eslint/parser": "7.3.1", + "@unocss/preset-attributify": "0.58.6", + "@unocss/preset-icons": "0.58.6", + "@unocss/preset-uno": "0.58.6", + "@vitejs/plugin-vue": "5.0.4", + "@vue/compiler-sfc": "3.4.23", + "autoprefixer": "10.4.18", + "eslint": "8.57.0", + "eslint-config-prettier": "9.1.0", + "eslint-define-config": "2.1.0", + "eslint-plugin-prettier": "5.1.3", + "eslint-plugin-promise": "6.1.1", + "eslint-plugin-node": "11.1.0", + "eslint-plugin-import": "2.29.1", + "eslint-plugin-vue": "9.23.0", + "fast-glob": "3.3.2", + "postcss": "8.4.36", + "prettier": "3.2.5", + "sass": "1.72.0", + "typescript": "5.4.5", + "unocss": "0.58.6", + "unplugin-auto-import": "0.17.5", + "unplugin-icons": "0.18.5", + "unplugin-vue-components": "0.26.0", + "unplugin-vue-setup-extend-plus": "1.0.1", + "vite": "5.2.10", + "vite-plugin-compression": "0.5.1", + "vite-plugin-svg-icons": "2.0.1", + "vitest": "1.5.0", + "vue-eslint-parser": "9.4.2", + "vue-tsc": "2.0.13" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..3f919d8 Binary files /dev/null and b/public/favicon.ico differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..66dbed6 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/animate.ts b/src/animate.ts new file mode 100644 index 0000000..7a23df8 --- /dev/null +++ b/src/animate.ts @@ -0,0 +1,48 @@ +// 前缀 +const animatePrefix = 'animate__animated '; +// 开启随机动画 随机动画值 +const animateList: string[] = [ + animatePrefix + 'animate__pulse', + animatePrefix + 'animate__rubberBand', + animatePrefix + 'animate__bounceIn', + animatePrefix + 'animate__bounceInLeft', + animatePrefix + 'animate__fadeIn', + animatePrefix + 'animate__fadeInLeft', + animatePrefix + 'animate__fadeInDown', + animatePrefix + 'animate__fadeInUp', + animatePrefix + 'animate__flipInX', + animatePrefix + 'animate__lightSpeedInLeft', + animatePrefix + 'animate__rotateInDownLeft', + animatePrefix + 'animate__rollIn', + animatePrefix + 'animate__rotateInDownLeft', + animatePrefix + 'animate__zoomIn', + animatePrefix + 'animate__zoomInDown', + animatePrefix + 'animate__slideInLeft', + animatePrefix + 'animate__lightSpeedIn' +]; +// 关闭随机动画后的默认效果 +const defaultAnimate = animatePrefix + 'animate__fadeIn'; +// 搜索隐藏显示动画 +const searchAnimate = { + enter: '', + leave: '' +}; + +// 菜单搜索动画 +const menuSearchAnimate = { + enter: animatePrefix + 'animate__fadeIn', + leave: animatePrefix + 'animate__fadeOut' +}; +// logo动画 +const logoAnimate = { + enter: animatePrefix + 'animate__fadeIn', + leave: animatePrefix + 'animate__fadeOut' +}; + +export default { + animateList, + defaultAnimate, + searchAnimate, + menuSearchAnimate, + logoAnimate +}; diff --git a/src/api/demo/demo/index.ts b/src/api/demo/demo/index.ts new file mode 100644 index 0000000..7441720 --- /dev/null +++ b/src/api/demo/demo/index.ts @@ -0,0 +1,62 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { DemoVO, DemoForm, DemoQuery } from '@/api/demo/demo/types'; + +/** + * 查询测试单列表 + * @param query + * @returns {*} + */ +export const listDemo = (query?: DemoQuery): AxiosPromise => { + return request({ + url: '/demo/demo/list', + method: 'get', + params: query + }); +}; + +/** + * 查询测试单详细 + * @param id + */ +export const getDemo = (id: string | number): AxiosPromise => { + return request({ + url: '/demo/demo/' + id, + method: 'get' + }); +}; + +/** + * 新增测试单 + * @param data + */ +export const addDemo = (data: DemoForm) => { + return request({ + url: '/demo/demo', + method: 'post', + data: data + }); +}; + +/** + * 修改测试单 + * @param data + */ +export const updateDemo = (data: DemoForm) => { + return request({ + url: '/demo/demo', + method: 'put', + data: data + }); +}; + +/** + * 删除测试单 + * @param id + */ +export const delDemo = (id: string | number | Array) => { + return request({ + url: '/demo/demo/' + id, + method: 'delete' + }); +}; diff --git a/src/api/demo/demo/types.ts b/src/api/demo/demo/types.ts new file mode 100644 index 0000000..ea51d32 --- /dev/null +++ b/src/api/demo/demo/types.ts @@ -0,0 +1,90 @@ +export interface DemoVO { + /** + * 主键 + */ + id: string | number; + + /** + * 部门id + */ + deptId: string | number; + + /** + * 用户id + */ + userId: string | number; + + /** + * 排序号 + */ + orderNum: number; + + /** + * key键 + */ + testKey: string; + + /** + * 值 + */ + value: string; +} + +export interface DemoForm extends BaseEntity { + /** + * 主键 + */ + id?: string | number; + + /** + * 部门id + */ + deptId?: string | number; + + /** + * 用户id + */ + userId?: string | number; + + /** + * 排序号 + */ + orderNum?: number; + + /** + * key键 + */ + testKey?: string; + + /** + * 值 + */ + value?: string; +} + +export interface DemoQuery extends PageQuery { + /** + * 部门id + */ + deptId?: string | number; + + /** + * 用户id + */ + userId?: string | number; + + /** + * 排序号 + */ + orderNum?: number; + + /** + * key键 + */ + testKey?: string; + + /** + * 值 + */ + value?: string; +} diff --git a/src/api/demo/tree/index.ts b/src/api/demo/tree/index.ts new file mode 100644 index 0000000..562deb6 --- /dev/null +++ b/src/api/demo/tree/index.ts @@ -0,0 +1,62 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { TreeVO, TreeForm, TreeQuery } from '@/api/demo/tree/types'; + +/** + * 查询测试树列表 + * @param query + * @returns {*} + */ +export const listTree = (query?: TreeQuery): AxiosPromise => { + return request({ + url: '/demo/tree/list', + method: 'get', + params: query + }); +}; + +/** + * 查询测试树详细 + * @param id + */ +export const getTree = (id: string | number): AxiosPromise => { + return request({ + url: '/demo/tree/' + id, + method: 'get' + }); +}; + +/** + * 新增测试树 + * @param data + */ +export const addTree = (data: TreeForm) => { + return request({ + url: '/demo/tree', + method: 'post', + data: data + }); +}; + +/** + * 修改测试树 + * @param data + */ +export const updateTree = (data: TreeForm) => { + return request({ + url: '/demo/tree', + method: 'put', + data: data + }); +}; + +/** + * 删除测试树 + * @param id + */ +export const delTree = (id: string | number | Array) => { + return request({ + url: '/demo/tree/' + id, + method: 'delete' + }); +}; diff --git a/src/api/demo/tree/types.ts b/src/api/demo/tree/types.ts new file mode 100644 index 0000000..e164d8b --- /dev/null +++ b/src/api/demo/tree/types.ts @@ -0,0 +1,80 @@ +export interface TreeVO { + /** + * 主键 + */ + id: string | number; + + /** + * 父id + */ + parentId: string | number; + + /** + * 部门id + */ + deptId: string | number; + + /** + * 用户id + */ + userId: string | number; + + /** + * 值 + */ + treeName: string; + + /** + * 子对象 + */ + children: TreeVO[]; +} + +export interface TreeForm extends BaseEntity { + /** + * 主键 + */ + id?: string | number; + + /** + * 父id + */ + parentId?: string | number; + + /** + * 部门id + */ + deptId?: string | number; + + /** + * 用户id + */ + userId?: string | number; + + /** + * 值 + */ + treeName?: string; +} + +export interface TreeQuery { + /** + * 父id + */ + parentId?: string | number; + + /** + * 部门id + */ + deptId?: string | number; + + /** + * 用户id + */ + userId?: string | number; + + /** + * 值 + */ + treeName?: string; +} diff --git a/src/api/login.ts b/src/api/login.ts new file mode 100644 index 0000000..b6955de --- /dev/null +++ b/src/api/login.ts @@ -0,0 +1,107 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { LoginData, LoginResult, VerifyCodeResult, TenantInfo } from './types'; +import { UserInfo } from '@/api/system/user/types'; + +// pc端固定客户端授权id +const clientId = import.meta.env.VITE_APP_CLIENT_ID; + +/** + * @param data {LoginData} + * @returns + */ +export function login(data: LoginData): AxiosPromise { + const params = { + ...data, + clientId: data.clientId || clientId, + grantType: data.grantType || 'password' + }; + return request({ + url: '/auth/login', + headers: { + isToken: false, + isEncrypt: true, + repeatSubmit: false + }, + method: 'post', + data: params + }); +} + +// 注册方法 +export function register(data: any) { + const params = { + ...data, + clientId: clientId, + grantType: 'password' + }; + return request({ + url: '/auth/register', + headers: { + isToken: false, + isEncrypt: true, + repeatSubmit: false + }, + method: 'post', + data: params + }); +} + +/** + * 注销 + */ +export function logout() { + return request({ + url: '/auth/logout', + method: 'post' + }); +} + +/** + * 获取验证码 + */ +export function getCodeImg(): AxiosPromise { + return request({ + url: '/auth/code', + headers: { + isToken: false + }, + method: 'get', + timeout: 20000 + }); +} + +/** + * 第三方登录 + */ +export function callback(data: LoginData): AxiosPromise { + const LoginData = { + ...data, + clientId: clientId, + grantType: 'social' + }; + return request({ + url: '/auth/social/callback', + method: 'post', + data: LoginData + }); +} + +// 获取用户详细信息 +export function getInfo(): AxiosPromise { + return request({ + url: '/system/user/getInfo', + method: 'get' + }); +} + +// 获取租户列表 +export function getTenantList(): AxiosPromise { + return request({ + url: '/auth/tenant/list', + headers: { + isToken: false + }, + method: 'get' + }); +} diff --git a/src/api/menu.ts b/src/api/menu.ts new file mode 100644 index 0000000..a3ae80e --- /dev/null +++ b/src/api/menu.ts @@ -0,0 +1,11 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { RouteRecordRaw } from 'vue-router'; + +// 获取路由 +export function getRouters(): AxiosPromise { + return request({ + url: '/system/menu/getRouters', + method: 'get' + }); +} diff --git a/src/api/monitor/cache/index.ts b/src/api/monitor/cache/index.ts new file mode 100644 index 0000000..e45d6fb --- /dev/null +++ b/src/api/monitor/cache/index.ts @@ -0,0 +1,59 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { CacheVO } from './types'; + +// 查询缓存详细 +export function getCache(): AxiosPromise { + return request({ + url: '/monitor/cache', + method: 'get' + }); +} + +// 查询缓存名称列表 +export function listCacheName() { + return request({ + url: '/monitor/cache/getNames', + method: 'get' + }); +} + +// 查询缓存键名列表 +export function listCacheKey(cacheName: string) { + return request({ + url: '/monitor/cache/getKeys/' + cacheName, + method: 'get' + }); +} + +// 查询缓存内容 +export function getCacheValue(cacheName: string, cacheKey: string) { + return request({ + url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey, + method: 'get' + }); +} + +// 清理指定名称缓存 +export function clearCacheName(cacheName: string) { + return request({ + url: '/monitor/cache/clearCacheName/' + cacheName, + method: 'delete' + }); +} + +// 清理指定键名缓存 +export function clearCacheKey(cacheName: string, cacheKey: string) { + return request({ + url: '/monitor/cache/clearCacheKey/' + cacheName + '/' + cacheKey, + method: 'delete' + }); +} + +// 清理全部缓存 +export function clearCacheAll() { + return request({ + url: '/monitor/cache/clearCacheAll', + method: 'delete' + }); +} diff --git a/src/api/monitor/cache/types.ts b/src/api/monitor/cache/types.ts new file mode 100644 index 0000000..4017b65 --- /dev/null +++ b/src/api/monitor/cache/types.ts @@ -0,0 +1,7 @@ +export interface CacheVO { + commandStats: Array<{ name: string; value: string }>; + + dbSize: number; + + info: { [key: string]: string }; +} diff --git a/src/api/monitor/loginInfo/index.ts b/src/api/monitor/loginInfo/index.ts new file mode 100644 index 0000000..f8877c9 --- /dev/null +++ b/src/api/monitor/loginInfo/index.ts @@ -0,0 +1,36 @@ +import request from '@/utils/request'; +import { LoginInfoQuery, LoginInfoVO } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询登录日志列表 +export function list(query: LoginInfoQuery): AxiosPromise { + return request({ + url: '/monitor/logininfor/list', + method: 'get', + params: query + }); +} + +// 删除登录日志 +export function delLoginInfo(infoId: string | number | Array) { + return request({ + url: '/monitor/logininfor/' + infoId, + method: 'delete' + }); +} + +// 解锁用户登录状态 +export function unlockLoginInfo(userName: string | Array) { + return request({ + url: '/monitor/logininfor/unlock/' + userName, + method: 'get' + }); +} + +// 清空登录日志 +export function cleanLoginInfo() { + return request({ + url: '/monitor/logininfor/clean', + method: 'delete' + }); +} diff --git a/src/api/monitor/loginInfo/types.ts b/src/api/monitor/loginInfo/types.ts new file mode 100644 index 0000000..202c779 --- /dev/null +++ b/src/api/monitor/loginInfo/types.ts @@ -0,0 +1,20 @@ +export interface LoginInfoVO { + infoId: string | number; + tenantId: string | number; + userName: string; + status: string; + ipaddr: string; + loginLocation: string; + browser: string; + os: string; + msg: string; + loginTime: string; +} + +export interface LoginInfoQuery extends PageQuery { + ipaddr: string; + userName: string; + status: string; + orderByColumn: string; + isAsc: string; +} diff --git a/src/api/monitor/online/index.ts b/src/api/monitor/online/index.ts new file mode 100644 index 0000000..7484702 --- /dev/null +++ b/src/api/monitor/online/index.ts @@ -0,0 +1,36 @@ +import request from '@/utils/request'; +import { OnlineQuery, OnlineVO } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询在线用户列表 +export function list(query: OnlineQuery): AxiosPromise { + return request({ + url: '/monitor/online/list', + method: 'get', + params: query + }); +} + +// 强退用户 +export function forceLogout(tokenId: string) { + return request({ + url: '/monitor/online/' + tokenId, + method: 'delete' + }); +} + +// 获取当前用户登录在线设备 +export function getOnline() { + return request({ + url: '/monitor/online', + method: 'get' + }); +} + +// 删除当前在线设备 +export function delOnline(tokenId: string) { + return request({ + url: '/monitor/online/' + tokenId, + method: 'post' + }); +} diff --git a/src/api/monitor/online/types.ts b/src/api/monitor/online/types.ts new file mode 100644 index 0000000..8c0ec27 --- /dev/null +++ b/src/api/monitor/online/types.ts @@ -0,0 +1,15 @@ +export interface OnlineQuery extends PageQuery { + ipaddr: string; + userName: string; +} + +export interface OnlineVO extends BaseEntity { + tokenId: string; + deptName: string; + userName: string; + ipaddr: string; + loginLocation: string; + browser: string; + os: string; + loginTime: number; +} diff --git a/src/api/monitor/operlog/index.ts b/src/api/monitor/operlog/index.ts new file mode 100644 index 0000000..7ac3453 --- /dev/null +++ b/src/api/monitor/operlog/index.ts @@ -0,0 +1,28 @@ +import request from '@/utils/request'; +import { OperLogQuery, OperLogVO } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询操作日志列表 +export function list(query: OperLogQuery): AxiosPromise { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query + }); +} + +// 删除操作日志 +export function delOperlog(operId: string | number | Array) { + return request({ + url: '/monitor/operlog/' + operId, + method: 'delete' + }); +} + +// 清空操作日志 +export function cleanOperlog() { + return request({ + url: '/monitor/operlog/clean', + method: 'delete' + }); +} diff --git a/src/api/monitor/operlog/types.ts b/src/api/monitor/operlog/types.ts new file mode 100644 index 0000000..10f65c7 --- /dev/null +++ b/src/api/monitor/operlog/types.ts @@ -0,0 +1,53 @@ +export interface OperLogQuery extends PageQuery { + operIp: string; + title: string; + operName: string; + businessType: string; + status: string; + orderByColumn: string; + isAsc: string; +} + +export interface OperLogVO extends BaseEntity { + operId: string | number; + tenantId: string; + title: string; + businessType: number; + businessTypes: number[] | undefined; + method: string; + requestMethod: string; + operatorType: number; + operName: string; + deptName: string; + operUrl: string; + operIp: string; + operLocation: string; + operParam: string; + jsonResult: string; + status: number; + errorMsg: string; + operTime: string; + costTime: number; +} + +export interface OperLogForm { + operId: number | string | undefined; + tenantId: string | number | undefined; + title: string; + businessType: number; + businessTypes: number[] | undefined; + method: string; + requestMethod: string; + operatorType: number; + operName: string; + deptName: string; + operUrl: string; + operIp: string; + operLocation: string; + operParam: string; + jsonResult: string; + status: number; + errorMsg: string; + operTime: string; + costTime: number; +} diff --git a/src/api/system/client/index.ts b/src/api/system/client/index.ts new file mode 100644 index 0000000..6b302b0 --- /dev/null +++ b/src/api/system/client/index.ts @@ -0,0 +1,80 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { ClientVO, ClientForm, ClientQuery } from '@/api/system/client/types'; + +/** + * 查询客户端管理列表 + * @param query + * @returns {*} + */ + +export const listClient = (query?: ClientQuery): AxiosPromise => { + return request({ + url: '/system/client/list', + method: 'get', + params: query + }); +}; + +/** + * 查询客户端管理详细 + * @param id + */ +export const getClient = (id: string | number): AxiosPromise => { + return request({ + url: '/system/client/' + id, + method: 'get' + }); +}; + +/** + * 新增客户端管理 + * @param data + */ +export const addClient = (data: ClientForm) => { + return request({ + url: '/system/client', + method: 'post', + data: data + }); +}; + +/** + * 修改客户端管理 + * @param data + */ +export const updateClient = (data: ClientForm) => { + return request({ + url: '/system/client', + method: 'put', + data: data + }); +}; + +/** + * 删除客户端管理 + * @param id + */ +export const delClient = (id: string | number | Array) => { + return request({ + url: '/system/client/' + id, + method: 'delete' + }); +}; + +/** + * 状态修改 + * @param clientId 客户端id + * @param status 状态 + */ +export function changeStatus(clientId: string, status: string) { + const data = { + clientId, + status + }; + return request({ + url: '/system/client/changeStatus', + method: 'put', + data: data + }); +} diff --git a/src/api/system/client/types.ts b/src/api/system/client/types.ts new file mode 100644 index 0000000..142118d --- /dev/null +++ b/src/api/system/client/types.ts @@ -0,0 +1,135 @@ +export interface ClientVO { + /** + * id + */ + id: string | number; + + /** + * 客户端id + */ + clientId: string; + + /** + * 客户端key + */ + clientKey: string; + + /** + * 客户端秘钥 + */ + clientSecret: string; + + /** + * 授权类型 + */ + grantTypeList: string[]; + + /** + * 设备类型 + */ + deviceType: string; + + /** + * token活跃超时时间 + */ + activeTimeout: number; + + /** + * token固定超时 + */ + timeout: number; + + /** + * 状态(0正常 1停用) + */ + status: string; +} + +export interface ClientForm extends BaseEntity { + /** + * id + */ + id?: string | number; + + /** + * 客户端id + */ + clientId?: string | number; + + /** + * 客户端key + */ + clientKey?: string; + + /** + * 客户端秘钥 + */ + clientSecret?: string; + + /** + * 授权类型 + */ + grantTypeList?: string[]; + + /** + * 设备类型 + */ + deviceType?: string; + + /** + * token活跃超时时间 + */ + activeTimeout?: number; + + /** + * token固定超时 + */ + timeout?: number; + + /** + * 状态(0正常 1停用) + */ + status?: string; +} + +export interface ClientQuery extends PageQuery { + /** + * 客户端id + */ + clientId?: string | number; + + /** + * 客户端key + */ + clientKey?: string; + + /** + * 客户端秘钥 + */ + clientSecret?: string; + + /** + * 授权类型 + */ + grantType?: string; + + /** + * 设备类型 + */ + deviceType?: string; + + /** + * token活跃超时时间 + */ + activeTimeout?: number; + + /** + * token固定超时 + */ + timeout?: number; + + /** + * 状态(0正常 1停用) + */ + status?: string; +} diff --git a/src/api/system/config/index.ts b/src/api/system/config/index.ts new file mode 100644 index 0000000..1e4842d --- /dev/null +++ b/src/api/system/config/index.ts @@ -0,0 +1,74 @@ +import request from '@/utils/request'; +import { ConfigForm, ConfigQuery, ConfigVO } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询参数列表 +export function listConfig(query: ConfigQuery): AxiosPromise { + return request({ + url: '/system/config/list', + method: 'get', + params: query + }); +} + +// 查询参数详细 +export function getConfig(configId: string | number): AxiosPromise { + return request({ + url: '/system/config/' + configId, + method: 'get' + }); +} + +// 根据参数键名查询参数值 +export function getConfigKey(configKey: string): AxiosPromise { + return request({ + url: '/system/config/configKey/' + configKey, + method: 'get' + }); +} + +// 新增参数配置 +export function addConfig(data: ConfigForm) { + return request({ + url: '/system/config', + method: 'post', + data: data + }); +} + +// 修改参数配置 +export function updateConfig(data: ConfigForm) { + return request({ + url: '/system/config', + method: 'put', + data: data + }); +} + +// 修改参数配置 +export function updateConfigByKey(key: string, value: any) { + return request({ + url: '/system/config/updateByKey', + method: 'put', + data: { + configKey: key, + configValue: value + } + }); +} + +// 删除参数配置 +export function delConfig(configId: string | number | Array) { + return request({ + url: '/system/config/' + configId, + method: 'delete' + }); +} + +// 刷新参数缓存 +export function refreshCache() { + return request({ + url: '/system/config/refreshCache', + method: 'delete' + }); +} diff --git a/src/api/system/config/types.ts b/src/api/system/config/types.ts new file mode 100644 index 0000000..b68f073 --- /dev/null +++ b/src/api/system/config/types.ts @@ -0,0 +1,23 @@ +export interface ConfigVO extends BaseEntity { + configId: number | string; + configName: string; + configKey: string; + configValue: string; + configType: string; + remark: string; +} + +export interface ConfigForm { + configId: number | string | undefined; + configName: string; + configKey: string; + configValue: string; + configType: string; + remark: string; +} + +export interface ConfigQuery extends PageQuery { + configName: string; + configKey: string; + configType: string; +} diff --git a/src/api/system/dept/index.ts b/src/api/system/dept/index.ts new file mode 100644 index 0000000..7e097fd --- /dev/null +++ b/src/api/system/dept/index.ts @@ -0,0 +1,62 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { DeptForm, DeptQuery, DeptVO } from './types'; + +// 查询部门列表 +export const listDept = (query?: DeptQuery) => { + return request({ + url: '/system/dept/list', + method: 'get', + params: query + }); +}; + +// 查询部门列表(排除节点) +export const listDeptExcludeChild = (deptId: string | number): AxiosPromise => { + return request({ + url: '/system/dept/list/exclude/' + deptId, + method: 'get' + }); +}; + +// 查询部门详细 +export const getDept = (deptId: string | number): AxiosPromise => { + return request({ + url: '/system/dept/' + deptId, + method: 'get' + }); +}; + +// 查询部门下拉树结构 +export const treeselect = (): AxiosPromise => { + return request({ + url: '/system/dept/treeselect', + method: 'get' + }); +}; + +// 新增部门 +export const addDept = (data: DeptForm) => { + return request({ + url: '/system/dept', + method: 'post', + data: data + }); +}; + +// 修改部门 +export const updateDept = (data: DeptForm) => { + return request({ + url: '/system/dept', + method: 'put', + data: data + }); +}; + +// 删除部门 +export const delDept = (deptId: number | string) => { + return request({ + url: '/system/dept/' + deptId, + method: 'delete' + }); +}; diff --git a/src/api/system/dept/types.ts b/src/api/system/dept/types.ts new file mode 100644 index 0000000..494745c --- /dev/null +++ b/src/api/system/dept/types.ts @@ -0,0 +1,48 @@ +/** + * 部门查询参数 + */ +export interface DeptQuery extends PageQuery { + deptName?: string; + deptCategory?: string; + status?: number; +} + +/** + * 部门类型 + */ +export interface DeptVO extends BaseEntity { + id: number | string; + parentName: string; + parentId: number | string; + children: DeptVO[]; + deptId: number | string; + deptName: string; + deptCategory: string; + orderNum: number; + leader: string; + phone: string; + email: string; + status: string; + delFlag: string; + ancestors: string; + menuId: string | number; +} + +/** + * 部门表单类型 + */ +export interface DeptForm { + parentName?: string; + parentId?: number | string; + children?: DeptForm[]; + deptId?: number | string; + deptName?: string; + deptCategory?: string; + orderNum?: number; + leader?: string; + phone?: string; + email?: string; + status?: string; + delFlag?: string; + ancestors?: string; +} diff --git a/src/api/system/dict/data/index.ts b/src/api/system/dict/data/index.ts new file mode 100644 index 0000000..7692abc --- /dev/null +++ b/src/api/system/dict/data/index.ts @@ -0,0 +1,53 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { DictDataForm, DictDataQuery, DictDataVO } from './types'; +// 根据字典类型查询字典数据信息 +export function getDicts(dictType: string): AxiosPromise { + return request({ + url: '/system/dict/data/type/' + dictType, + method: 'get' + }); +} + +// 查询字典数据列表 +export function listData(query: DictDataQuery): AxiosPromise { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query + }); +} + +// 查询字典数据详细 +export function getData(dictCode: string | number): AxiosPromise { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'get' + }); +} + +// 新增字典数据 +export function addData(data: DictDataForm) { + return request({ + url: '/system/dict/data', + method: 'post', + data: data + }); +} + +// 修改字典数据 +export function updateData(data: DictDataForm) { + return request({ + url: '/system/dict/data', + method: 'put', + data: data + }); +} + +// 删除字典数据 +export function delData(dictCode: string | number | Array) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete' + }); +} diff --git a/src/api/system/dict/data/types.ts b/src/api/system/dict/data/types.ts new file mode 100644 index 0000000..e4abb9b --- /dev/null +++ b/src/api/system/dict/data/types.ts @@ -0,0 +1,26 @@ +export interface DictDataQuery extends PageQuery { + dictName: string; + dictType: string; + dictLabel: string; +} + +export interface DictDataVO extends BaseEntity { + dictCode: string; + dictLabel: string; + dictValue: string; + cssClass: string; + listClass: ElTagType; + dictSort: number; + remark: string; +} + +export interface DictDataForm { + dictType?: string; + dictCode: string | undefined; + dictLabel: string; + dictValue: string; + cssClass: string; + listClass: ElTagType; + dictSort: number; + remark: string; +} diff --git a/src/api/system/dict/type/index.ts b/src/api/system/dict/type/index.ts new file mode 100644 index 0000000..7dc3d66 --- /dev/null +++ b/src/api/system/dict/type/index.ts @@ -0,0 +1,62 @@ +import request from '@/utils/request'; +import { DictTypeForm, DictTypeVO, DictTypeQuery } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询字典类型列表 +export function listType(query: DictTypeQuery): AxiosPromise { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query + }); +} + +// 查询字典类型详细 +export function getType(dictId: number | string): AxiosPromise { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get' + }); +} + +// 新增字典类型 +export function addType(data: DictTypeForm) { + return request({ + url: '/system/dict/type', + method: 'post', + data: data + }); +} + +// 修改字典类型 +export function updateType(data: DictTypeForm) { + return request({ + url: '/system/dict/type', + method: 'put', + data: data + }); +} + +// 删除字典类型 +export function delType(dictId: string | number | Array) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete' + }); +} + +// 刷新字典缓存 +export function refreshCache() { + return request({ + url: '/system/dict/type/refreshCache', + method: 'delete' + }); +} + +// 获取字典选择框列表 +export function optionselect(): AxiosPromise { + return request({ + url: '/system/dict/type/optionselect', + method: 'get' + }); +} diff --git a/src/api/system/dict/type/types.ts b/src/api/system/dict/type/types.ts new file mode 100644 index 0000000..4987dbf --- /dev/null +++ b/src/api/system/dict/type/types.ts @@ -0,0 +1,18 @@ +export interface DictTypeVO extends BaseEntity { + dictId: number | string; + dictName: string; + dictType: string; + remark: string; +} + +export interface DictTypeForm { + dictId: number | string | undefined; + dictName: string; + dictType: string; + remark: string; +} + +export interface DictTypeQuery extends PageQuery { + dictName: string; + dictType: string; +} diff --git a/src/api/system/menu/index.ts b/src/api/system/menu/index.ts new file mode 100644 index 0000000..7a0cf74 --- /dev/null +++ b/src/api/system/menu/index.ts @@ -0,0 +1,70 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { MenuQuery, MenuVO, MenuForm, MenuTreeOption, RoleMenuTree } from './types'; + +// 查询菜单列表 +export const listMenu = (query?: MenuQuery): AxiosPromise => { + return request({ + url: '/system/menu/list', + method: 'get', + params: query + }); +}; + +// 查询菜单详细 +export const getMenu = (menuId: string | number): AxiosPromise => { + return request({ + url: '/system/menu/' + menuId, + method: 'get' + }); +}; + +// 查询菜单下拉树结构 +export const treeselect = (): AxiosPromise => { + return request({ + url: '/system/menu/treeselect', + method: 'get' + }); +}; + +// 根据角色ID查询菜单下拉树结构 +export const roleMenuTreeselect = (roleId: string | number): AxiosPromise => { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get' + }); +}; + +// 根据角色ID查询菜单下拉树结构 +export const tenantPackageMenuTreeselect = (packageId: string | number): AxiosPromise => { + return request({ + url: '/system/menu/tenantPackageMenuTreeselect/' + packageId, + method: 'get' + }); +}; + +// 新增菜单 +export const addMenu = (data: MenuForm) => { + return request({ + url: '/system/menu', + method: 'post', + data: data + }); +}; + +// 修改菜单 +export const updateMenu = (data: MenuForm) => { + return request({ + url: '/system/menu', + method: 'put', + data: data + }); +}; + +// 删除菜单 +export const delMenu = (menuId: string | number) => { + return request({ + url: '/system/menu/' + menuId, + method: 'delete' + }); +}; diff --git a/src/api/system/menu/types.ts b/src/api/system/menu/types.ts new file mode 100644 index 0000000..ca19840 --- /dev/null +++ b/src/api/system/menu/types.ts @@ -0,0 +1,69 @@ +import { MenuTypeEnum } from '@/enums/MenuTypeEnum'; + +/** + * 菜单树形结构类型 + */ +export interface MenuTreeOption { + id: string | number; + label: string; + parentId: string | number; + weight: number; + children?: MenuTreeOption[]; +} + +export interface RoleMenuTree { + menus: MenuTreeOption[]; + checkedKeys: string[]; +} + +/** + * 菜单查询参数类型 + */ +export interface MenuQuery { + keywords?: string; + menuName?: string; + status?: string; +} + +/** + * 菜单视图对象类型 + */ +export interface MenuVO extends BaseEntity { + parentName: string; + parentId: string | number; + children: MenuVO[]; + menuId: string | number; + menuName: string; + orderNum: number; + path: string; + component: string; + queryParam: string; + isFrame: string; + isCache: string; + menuType: MenuTypeEnum; + visible: string; + status: string; + icon: string; + remark: string; +} + +export interface MenuForm { + parentName?: string; + parentId?: string | number; + children?: MenuForm[]; + menuId?: string | number; + menuName: string; + orderNum: number; + path: string; + component?: string; + queryParam?: string; + isFrame?: string; + isCache?: string; + menuType?: MenuTypeEnum; + visible?: string; + status?: string; + icon?: string; + remark?: string; + query?: string; + perms?: string; +} diff --git a/src/api/system/notice/index.ts b/src/api/system/notice/index.ts new file mode 100644 index 0000000..285d1f4 --- /dev/null +++ b/src/api/system/notice/index.ts @@ -0,0 +1,45 @@ +import request from '@/utils/request'; +import { NoticeForm, NoticeQuery, NoticeVO } from './types'; +import { AxiosPromise } from 'axios'; +// 查询公告列表 +export function listNotice(query: NoticeQuery): AxiosPromise { + return request({ + url: '/system/notice/list', + method: 'get', + params: query + }); +} + +// 查询公告详细 +export function getNotice(noticeId: string | number): AxiosPromise { + return request({ + url: '/system/notice/' + noticeId, + method: 'get' + }); +} + +// 新增公告 +export function addNotice(data: NoticeForm) { + return request({ + url: '/system/notice', + method: 'post', + data: data + }); +} + +// 修改公告 +export function updateNotice(data: NoticeForm) { + return request({ + url: '/system/notice', + method: 'put', + data: data + }); +} + +// 删除公告 +export function delNotice(noticeId: string | number | Array) { + return request({ + url: '/system/notice/' + noticeId, + method: 'delete' + }); +} diff --git a/src/api/system/notice/types.ts b/src/api/system/notice/types.ts new file mode 100644 index 0000000..abfd5b2 --- /dev/null +++ b/src/api/system/notice/types.ts @@ -0,0 +1,26 @@ +export interface NoticeVO extends BaseEntity { + noticeId: number; + noticeTitle: string; + noticeType: string; + noticeContent: string; + status: string; + remark: string; + createByName: string; +} + +export interface NoticeQuery extends PageQuery { + noticeTitle: string; + createByName: string; + status: string; + noticeType: string; +} + +export interface NoticeForm { + noticeId: number | string | undefined; + noticeTitle: string; + noticeType: string; + noticeContent: string; + status: string; + remark: string; + createByName: string; +} diff --git a/src/api/system/oss/index.ts b/src/api/system/oss/index.ts new file mode 100644 index 0000000..4472112 --- /dev/null +++ b/src/api/system/oss/index.ts @@ -0,0 +1,28 @@ +import request from '@/utils/request'; +import { OssQuery, OssVO } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询OSS对象存储列表 +export function listOss(query: OssQuery): AxiosPromise { + return request({ + url: '/resource/oss/list', + method: 'get', + params: query + }); +} + +// 查询OSS对象基于id串 +export function listByIds(ossId: string | number): AxiosPromise { + return request({ + url: '/resource/oss/listByIds/' + ossId, + method: 'get' + }); +} + +// 删除OSS对象存储 +export function delOss(ossId: string | number | Array) { + return request({ + url: '/resource/oss/' + ossId, + method: 'delete' + }); +} diff --git a/src/api/system/oss/types.ts b/src/api/system/oss/types.ts new file mode 100644 index 0000000..bc0bc1f --- /dev/null +++ b/src/api/system/oss/types.ts @@ -0,0 +1,22 @@ +export interface OssVO extends BaseEntity { + ossId: string | number; + fileName: string; + originalName: string; + fileSuffix: string; + url: string; + createByName: string; + service: string; +} + +export interface OssQuery extends PageQuery { + fileName: string; + originalName: string; + fileSuffix: string; + createTime: string; + service: string; + orderByColumn: string; + isAsc: string; +} +export interface OssForm { + file: undefined | string; +} diff --git a/src/api/system/ossConfig/index.ts b/src/api/system/ossConfig/index.ts new file mode 100644 index 0000000..d0faefe --- /dev/null +++ b/src/api/system/ossConfig/index.ts @@ -0,0 +1,60 @@ +import request from '@/utils/request'; +import { OssConfigForm, OssConfigQuery, OssConfigVO } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询对象存储配置列表 +export function listOssConfig(query: OssConfigQuery): AxiosPromise { + return request({ + url: '/resource/oss/config/list', + method: 'get', + params: query + }); +} + +// 查询对象存储配置详细 +export function getOssConfig(ossConfigId: string | number): AxiosPromise { + return request({ + url: '/resource/oss/config/' + ossConfigId, + method: 'get' + }); +} + +// 新增对象存储配置 +export function addOssConfig(data: OssConfigForm) { + return request({ + url: '/resource/oss/config', + method: 'post', + data: data + }); +} + +// 修改对象存储配置 +export function updateOssConfig(data: OssConfigForm) { + return request({ + url: '/resource/oss/config', + method: 'put', + data: data + }); +} + +// 删除对象存储配置 +export function delOssConfig(ossConfigId: string | number | Array) { + return request({ + url: '/resource/oss/config/' + ossConfigId, + method: 'delete' + }); +} + +// 对象存储状态修改 +export function changeOssConfigStatus(ossConfigId: string | number, status: string, configKey: string) { + const data = { + ossConfigId, + status, + configKey + }; + return request({ + url: '/resource/oss/config/changeStatus', + method: 'put', + data: data + }); +} diff --git a/src/api/system/ossConfig/types.ts b/src/api/system/ossConfig/types.ts new file mode 100644 index 0000000..d227bd0 --- /dev/null +++ b/src/api/system/ossConfig/types.ts @@ -0,0 +1,38 @@ +export interface OssConfigVO extends BaseEntity { + ossConfigId: number | string; + configKey: string; + accessKey: string; + secretKey: string; + bucketName: string; + prefix: string; + endpoint: string; + domain: string; + isHttps: string; + region: string; + status: string; + ext1: string; + remark: string; + accessPolicy: string; +} + +export interface OssConfigQuery extends PageQuery { + configKey: string; + bucketName: string; + status: string; +} + +export interface OssConfigForm { + ossConfigId: string | number | undefined; + configKey: string; + accessKey: string; + secretKey: string; + bucketName: string; + prefix: string; + endpoint: string; + domain: string; + isHttps: string; + accessPolicy: string; + region: string; + status: string; + remark: string; +} diff --git a/src/api/system/post/index.ts b/src/api/system/post/index.ts new file mode 100644 index 0000000..5f6ab18 --- /dev/null +++ b/src/api/system/post/index.ts @@ -0,0 +1,58 @@ +import request from '@/utils/request'; +import { PostForm, PostQuery, PostVO } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询岗位列表 +export function listPost(query: PostQuery): AxiosPromise { + return request({ + url: '/system/post/list', + method: 'get', + params: query + }); +} + +// 查询岗位详细 +export function getPost(postId: string | number): AxiosPromise { + return request({ + url: '/system/post/' + postId, + method: 'get' + }); +} + +// 获取岗位选择框列表 +export function optionselect(deptId?: number | string, postIds?: (number | string)[]): AxiosPromise { + return request({ + url: '/system/post/optionselect', + method: 'get', + params: { + postIds: postIds, + deptId: deptId + } + }); +} + +// 新增岗位 +export function addPost(data: PostForm) { + return request({ + url: '/system/post', + method: 'post', + data: data + }); +} + +// 修改岗位 +export function updatePost(data: PostForm) { + return request({ + url: '/system/post', + method: 'put', + data: data + }); +} + +// 删除岗位 +export function delPost(postId: string | number | (string | number)[]) { + return request({ + url: '/system/post/' + postId, + method: 'delete' + }); +} diff --git a/src/api/system/post/types.ts b/src/api/system/post/types.ts new file mode 100644 index 0000000..45a0540 --- /dev/null +++ b/src/api/system/post/types.ts @@ -0,0 +1,31 @@ +export interface PostVO extends BaseEntity { + postId: number | string; + deptId: number | string; + postCode: string; + postName: string; + postCategory: string; + deptName: string; + postSort: number; + status: string; + remark: string; +} + +export interface PostForm { + postId: number | string | undefined; + deptId: number | string | undefined; + postCode: string; + postName: string; + postCategory: string; + postSort: number; + status: string; + remark: string; +} + +export interface PostQuery extends PageQuery { + deptId: number | string; + belongDeptId: number | string; + postCode: string; + postName: string; + postCategory: string; + status: string; +} diff --git a/src/api/system/role/index.ts b/src/api/system/role/index.ts new file mode 100644 index 0000000..fb0fcab --- /dev/null +++ b/src/api/system/role/index.ts @@ -0,0 +1,160 @@ +import { UserVO } from '@/api/system/user/types'; +import { UserQuery } from '@/api/system/user/types'; +import { AxiosPromise } from 'axios'; +import { RoleQuery, RoleVO, RoleDeptTree } from './types'; +import request from '@/utils/request'; + +export const listRole = (query: RoleQuery): AxiosPromise => { + return request({ + url: '/system/role/list', + method: 'get', + params: query + }); +}; + +/** + * 通过roleIds查询角色 + * @param roleIds + */ +export const optionSelect = (roleIds: (number | string)[]): AxiosPromise => { + return request({ + url: '/system/role/optionselect?roleIds=' + roleIds, + method: 'get' + }); +}; + +/** + * 查询角色详细 + */ +export const getRole = (roleId: string | number): AxiosPromise => { + return request({ + url: '/system/role/' + roleId, + method: 'get' + }); +}; + +/** + * 新增角色 + */ +export const addRole = (data: any) => { + return request({ + url: '/system/role', + method: 'post', + data: data + }); +}; + +/** + * 修改角色 + * @param data + */ +export const updateRole = (data: any) => { + return request({ + url: '/system/role', + method: 'put', + data: data + }); +}; + +/** + * 角色数据权限 + */ +export const dataScope = (data: any) => { + return request({ + url: '/system/role/dataScope', + method: 'put', + data: data + }); +}; + +/** + * 角色状态修改 + */ +export const changeRoleStatus = (roleId: string | number, status: string) => { + const data = { + roleId, + status + }; + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data + }); +}; + +/** + * 删除角色 + */ +export const delRole = (roleId: Array | string | number) => { + return request({ + url: '/system/role/' + roleId, + method: 'delete' + }); +}; + +/** + * 查询角色已授权用户列表 + */ +export const allocatedUserList = (query: UserQuery): AxiosPromise => { + return request({ + url: '/system/role/authUser/allocatedList', + method: 'get', + params: query + }); +}; + +/** + * 查询角色未授权用户列表 + */ +export const unallocatedUserList = (query: UserQuery): AxiosPromise => { + return request({ + url: '/system/role/authUser/unallocatedList', + method: 'get', + params: query + }); +}; + +/** + * 取消用户授权角色 + */ +export const authUserCancel = (data: any) => { + return request({ + url: '/system/role/authUser/cancel', + method: 'put', + data: data + }); +}; + +/** + * 批量取消用户授权角色 + */ +export const authUserCancelAll = (data: any) => { + return request({ + url: '/system/role/authUser/cancelAll', + method: 'put', + params: data + }); +}; + +/** + * 授权用户选择 + */ +export const authUserSelectAll = (data: any) => { + return request({ + url: '/system/role/authUser/selectAll', + method: 'put', + params: data + }); +}; +// 根据角色ID查询部门树结构 +export const deptTreeSelect = (roleId: string | number): AxiosPromise => { + return request({ + url: '/system/role/deptTree/' + roleId, + method: 'get' + }); +}; + +export default { + optionSelect, + listRole +}; diff --git a/src/api/system/role/types.ts b/src/api/system/role/types.ts new file mode 100644 index 0000000..7dbb6ff --- /dev/null +++ b/src/api/system/role/types.ts @@ -0,0 +1,52 @@ +/** + * 菜单树形结构类型 + */ +export interface DeptTreeOption { + id: string; + label: string; + parentId: string; + weight: number; + children?: DeptTreeOption[]; +} + +export interface RoleDeptTree { + checkedKeys: string[]; + depts: DeptTreeOption[]; +} + +export interface RoleVO extends BaseEntity { + roleId: string | number; + roleName: string; + roleKey: string; + roleSort: number; + dataScope: string; + menuCheckStrictly: boolean; + deptCheckStrictly: boolean; + status: string; + delFlag: string; + remark?: any; + flag: boolean; + menuIds?: Array; + deptIds?: Array; + admin: boolean; +} + +export interface RoleQuery extends PageQuery { + roleName: string; + roleKey: string; + status: string; +} + +export interface RoleForm { + roleName: string; + roleKey: string; + roleSort: number; + status: string; + menuCheckStrictly: boolean; + deptCheckStrictly: boolean; + remark: string; + dataScope?: string; + roleId: string | undefined; + menuIds: Array; + deptIds: Array; +} diff --git a/src/api/system/social/auth.ts b/src/api/system/social/auth.ts new file mode 100644 index 0000000..17a46d3 --- /dev/null +++ b/src/api/system/social/auth.ts @@ -0,0 +1,24 @@ +import request from '@/utils/request'; + +// 绑定账号 +export function authBinding(source: string) { + return request({ + url: '/auth/binding/' + source, + method: 'get' + }); +} + +// 解绑账号 +export function authUnlock(authId: string) { + return request({ + url: '/auth/unlock/' + authId, + method: 'delete' + }); +} +//获取授权列表 +export function getAuthList() { + return request({ + url: '/system/social/list', + method: 'get' + }); +} diff --git a/src/api/system/tenant/index.ts b/src/api/system/tenant/index.ts new file mode 100644 index 0000000..4380dbe --- /dev/null +++ b/src/api/system/tenant/index.ts @@ -0,0 +1,93 @@ +import request from '@/utils/request'; +import { TenantForm, TenantQuery, TenantVO } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询租户列表 +export function listTenant(query: TenantQuery): AxiosPromise { + return request({ + url: '/system/tenant/list', + method: 'get', + params: query + }); +} + +// 查询租户详细 +export function getTenant(id: string | number): AxiosPromise { + return request({ + url: '/system/tenant/' + id, + method: 'get' + }); +} + +// 新增租户 +export function addTenant(data: TenantForm) { + return request({ + url: '/system/tenant', + method: 'post', + headers: { + isEncrypt: true, + repeatSubmit: false + }, + data: data + }); +} + +// 修改租户 +export function updateTenant(data: TenantForm) { + return request({ + url: '/system/tenant', + method: 'put', + data: data + }); +} + +// 租户状态修改 +export function changeTenantStatus(id: string | number, tenantId: string | number, status: string) { + const data = { + id, + tenantId, + status + }; + return request({ + url: '/system/tenant/changeStatus', + method: 'put', + data: data + }); +} + +// 删除租户 +export function delTenant(id: string | number | Array) { + return request({ + url: '/system/tenant/' + id, + method: 'delete' + }); +} + +// 动态切换租户 +export function dynamicTenant(tenantId: string | number) { + return request({ + url: '/system/tenant/dynamic/' + tenantId, + method: 'get' + }); +} + +// 清除动态租户 +export function dynamicClear() { + return request({ + url: '/system/tenant/dynamic/clear', + method: 'get' + }); +} + +// 同步租户套餐 +export function syncTenantPackage(tenantId: string | number, packageId: string | number) { + const data = { + tenantId, + packageId + }; + return request({ + url: '/system/tenant/syncTenantPackage', + method: 'get', + params: data + }); +} diff --git a/src/api/system/tenant/types.ts b/src/api/system/tenant/types.ts new file mode 100644 index 0000000..25395fa --- /dev/null +++ b/src/api/system/tenant/types.ts @@ -0,0 +1,46 @@ +export interface TenantVO extends BaseEntity { + id: number | string; + tenantId: number | string; + username: string; + contactUserName: string; + contactPhone: string; + companyName: string; + licenseNumber: string; + address: string; + domain: string; + intro: string; + remark: string; + packageId: string | number; + expireTime: string; + accountCount: number; + status: string; +} + +export interface TenantQuery extends PageQuery { + tenantId: string | number; + + contactUserName: string; + + contactPhone: string; + + companyName: string; +} + +export interface TenantForm { + id: number | string | undefined; + tenantId: number | string | undefined; + username: string; + password: string; + contactUserName: string; + contactPhone: string; + companyName: string; + licenseNumber: string; + domain: string; + address: string; + intro: string; + remark: string; + packageId: string | number; + expireTime: string; + accountCount: number; + status: string; +} diff --git a/src/api/system/tenantPackage/index.ts b/src/api/system/tenantPackage/index.ts new file mode 100644 index 0000000..70d0dab --- /dev/null +++ b/src/api/system/tenantPackage/index.ts @@ -0,0 +1,67 @@ +import request from '@/utils/request'; +import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询租户套餐列表 +export function listTenantPackage(query?: TenantPkgQuery): AxiosPromise { + return request({ + url: '/system/tenant/package/list', + method: 'get', + params: query + }); +} + +// 查询租户套餐下拉选列表 +export function selectTenantPackage(): AxiosPromise { + return request({ + url: '/system/tenant/package/selectList', + method: 'get' + }); +} + +// 查询租户套餐详细 +export function getTenantPackage(packageId: string | number): AxiosPromise { + return request({ + url: '/system/tenant/package/' + packageId, + method: 'get' + }); +} + +// 新增租户套餐 +export function addTenantPackage(data: TenantPkgForm) { + return request({ + url: '/system/tenant/package', + method: 'post', + data: data + }); +} + +// 修改租户套餐 +export function updateTenantPackage(data: TenantPkgForm) { + return request({ + url: '/system/tenant/package', + method: 'put', + data: data + }); +} + +// 租户套餐状态修改 +export function changePackageStatus(packageId: number | string, status: string) { + const data = { + packageId, + status + }; + return request({ + url: '/system/tenant/package/changeStatus', + method: 'put', + data: data + }); +} + +// 删除租户套餐 +export function delTenantPackage(packageId: string | number | Array) { + return request({ + url: '/system/tenant/package/' + packageId, + method: 'delete' + }); +} diff --git a/src/api/system/tenantPackage/types.ts b/src/api/system/tenantPackage/types.ts new file mode 100644 index 0000000..c24b8fc --- /dev/null +++ b/src/api/system/tenantPackage/types.ts @@ -0,0 +1,20 @@ +export interface TenantPkgVO extends BaseEntity { + packageId: string | number; + packageName: string; + menuIds: string; + remark: string; + menuCheckStrictly: boolean; + status: string; +} + +export interface TenantPkgQuery extends PageQuery { + packageName: string; +} + +export interface TenantPkgForm { + packageId: string | number | undefined; + packageName: string; + menuIds: string; + remark: string; + menuCheckStrictly: boolean; +} diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts new file mode 100644 index 0000000..25c7884 --- /dev/null +++ b/src/api/system/user/index.ts @@ -0,0 +1,229 @@ +import { DeptVO } from './../dept/types'; +import { RoleVO } from '@/api/system/role/types'; +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { UserForm, UserQuery, UserVO, UserInfoVO } from './types'; +import { parseStrEmpty } from '@/utils/ruoyi'; + +/** + * 查询用户列表 + * @param query + */ +export const listUser = (query: UserQuery): AxiosPromise => { + return request({ + url: '/system/user/list', + method: 'get', + params: query + }); +}; + +/** + * 通过用户ids查询用户 + * @param userIds + */ +export const optionSelect = (userIds: (number | string)[]): AxiosPromise => { + return request({ + url: '/system/user/optionselect?userIds=' + userIds, + method: 'get' + }); +}; + +/** + * 获取用户详情 + * @param userId + */ +export const getUser = (userId?: string | number): AxiosPromise => { + return request({ + url: '/system/user/' + parseStrEmpty(userId), + method: 'get' + }); +}; + +/** + * 新增用户 + */ +export const addUser = (data: UserForm) => { + return request({ + url: '/system/user', + method: 'post', + data: data + }); +}; + +/** + * 修改用户 + */ +export const updateUser = (data: UserForm) => { + return request({ + url: '/system/user', + method: 'put', + data: data + }); +}; + +/** + * 删除用户 + * @param userId 用户ID + */ +export const delUser = (userId: Array | string | number) => { + return request({ + url: '/system/user/' + userId, + method: 'delete' + }); +}; + +/** + * 用户密码重置 + * @param userId 用户ID + * @param password 密码 + */ +export const resetUserPwd = (userId: string | number, password: string) => { + const data = { + userId, + password + }; + return request({ + url: '/system/user/resetPwd', + method: 'put', + headers: { + isEncrypt: true, + repeatSubmit: false + }, + data: data + }); +}; + +/** + * 用户状态修改 + * @param userId 用户ID + * @param status 用户状态 + */ +export const changeUserStatus = (userId: number | string, status: string) => { + const data = { + userId, + status + }; + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data + }); +}; + +/** + * 查询用户个人信息 + */ +export const getUserProfile = (): AxiosPromise => { + return request({ + url: '/system/user/profile', + method: 'get' + }); +}; + +/** + * 修改用户个人信息 + * @param data 用户信息 + */ +export const updateUserProfile = (data: UserForm) => { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }); +}; + +/** + * 用户密码重置 + * @param oldPassword 旧密码 + * @param newPassword 新密码 + */ +export const updateUserPwd = (oldPassword: string, newPassword: string) => { + const data = { + oldPassword, + newPassword + }; + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + headers: { + isEncrypt: true, + repeatSubmit: false + }, + data: data + }); +}; + +/** + * 用户头像上传 + * @param data 头像文件 + */ +export const uploadAvatar = (data: FormData) => { + return request({ + url: '/system/user/profile/avatar', + method: 'post', + data: data + }); +}; + +/** + * 查询授权角色 + * @param userId 用户ID + */ +export const getAuthRole = (userId: string | number): AxiosPromise<{ user: UserVO; roles: RoleVO[] }> => { + return request({ + url: '/system/user/authRole/' + userId, + method: 'get' + }); +}; + +/** + * 保存授权角色 + * @param data 用户ID + */ +export const updateAuthRole = (data: { userId: string; roleIds: string }) => { + return request({ + url: '/system/user/authRole', + method: 'put', + params: data + }); +}; + +/** + * 查询当前部门的所有用户信息 + * @param deptId + */ +export const listUserByDeptId = (deptId: string | number): AxiosPromise => { + return request({ + url: '/system/user/list/dept/' + deptId, + method: 'get' + }); +}; + +/** + * 查询部门下拉树结构 + */ +export const deptTreeSelect = (): AxiosPromise => { + return request({ + url: '/system/user/deptTree', + method: 'get' + }); +}; + +export default { + listUser, + getUser, + optionSelect, + addUser, + updateUser, + delUser, + resetUserPwd, + changeUserStatus, + getUserProfile, + updateUserProfile, + updateUserPwd, + uploadAvatar, + getAuthRole, + updateAuthRole, + deptTreeSelect, + listUserByDeptId +}; diff --git a/src/api/system/user/types.ts b/src/api/system/user/types.ts new file mode 100644 index 0000000..3488e9f --- /dev/null +++ b/src/api/system/user/types.ts @@ -0,0 +1,83 @@ +import { RoleVO } from '@/api/system/role/types'; +import { PostVO } from '@/api/system/post/types'; + +/** + * 用户信息 + */ +export interface UserInfo { + user: UserVO; + roles: string[]; + permissions: string[]; +} + +/** + * 用户查询对象类型 + */ +export interface UserQuery extends PageQuery { + userName?: string; + phonenumber?: string; + status?: string; + deptId?: string | number; + roleId?: string | number; +} + +/** + * 用户返回对象 + */ +export interface UserVO extends BaseEntity { + userId: string | number; + deptId: number; + userName: string; + nickName: string; + userType: string; + email: string; + phonenumber: string; + sex: string; + avatar: string; + status: string; + delFlag: string; + loginIp: string; + loginDate: string; + remark: string; + deptName: string; + roles: RoleVO[]; + roleIds: any; + postIds: any; + roleId: any; + admin: boolean; +} + +/** + * 用户表单类型 + */ +export interface UserForm { + id?: string; + userId?: string; + deptId?: number; + userName: string; + nickName?: string; + password: string; + phonenumber?: string; + email?: string; + sex?: string; + status: string; + remark?: string; + postIds: string[]; + roleIds: string[]; +} + +export interface UserInfoVO { + user: UserVO; + roles: RoleVO[]; + roleIds: string[]; + posts: PostVO[]; + postIds: string[]; + roleGroup: string; + postGroup: string; +} + +export interface ResetPwdForm { + oldPassword: string; + newPassword: string; + confirmPassword: string; +} diff --git a/src/api/tool/gen/index.ts b/src/api/tool/gen/index.ts new file mode 100644 index 0000000..efe079c --- /dev/null +++ b/src/api/tool/gen/index.ts @@ -0,0 +1,86 @@ +import request from '@/utils/request'; +import { DbTableQuery, DbTableVO, TableQuery, TableVO, GenTableVO, DbTableForm } from './types'; +import { AxiosPromise } from 'axios'; + +// 查询生成表数据 +export const listTable = (query: TableQuery): AxiosPromise => { + return request({ + url: '/tool/gen/list', + method: 'get', + params: query + }); +}; +// 查询db数据库列表 +export const listDbTable = (query: DbTableQuery): AxiosPromise => { + return request({ + url: '/tool/gen/db/list', + method: 'get', + params: query + }); +}; + +// 查询表详细信息 +export const getGenTable = (tableId: string | number): AxiosPromise => { + return request({ + url: '/tool/gen/' + tableId, + method: 'get' + }); +}; + +// 修改代码生成信息 +export const updateGenTable = (data: DbTableForm): AxiosPromise => { + return request({ + url: '/tool/gen', + method: 'put', + data: data + }); +}; + +// 导入表 +export const importTable = (data: { tables: string; dataName: string }): AxiosPromise => { + return request({ + url: '/tool/gen/importTable', + method: 'post', + params: data + }); +}; + +// 预览生成代码 +export const previewTable = (tableId: string | number) => { + return request({ + url: '/tool/gen/preview/' + tableId, + method: 'get' + }); +}; + +// 删除表数据 +export const delTable = (tableId: string | number | Array) => { + return request({ + url: '/tool/gen/' + tableId, + method: 'delete' + }); +}; + +// 生成代码(自定义路径) +export const genCode = (tableId: string | number) => { + return request({ + url: '/tool/gen/genCode/' + tableId, + method: 'get' + }); +}; + +// 同步数据库 +export const synchDb = (tableId: string | number) => { + return request({ + url: '/tool/gen/synchDb/' + tableId, + method: 'get' + }); +}; + +// 获取数据源名称 +export const getDataNames = () => { + return request({ + url: '/tool/gen/getDataNames', + method: 'get' + }); +}; diff --git a/src/api/tool/gen/types.ts b/src/api/tool/gen/types.ts new file mode 100644 index 0000000..7f7b62f --- /dev/null +++ b/src/api/tool/gen/types.ts @@ -0,0 +1,180 @@ +export interface TableVO extends BaseEntity { + createDept: number | string; + tableId: string | number; + dataName: string; + tableName: string; + tableComment: string; + subTableName?: any; + subTableFkName?: any; + className: string; + tplCategory: string; + packageName: string; + moduleName: string; + businessName: string; + functionName: string; + functionAuthor: string; + genType: string; + genPath: string; + pkColumn?: any; + columns?: any; + options?: any; + remark?: any; + treeCode?: any; + treeParentCode?: any; + treeName?: any; + menuIds?: any; + parentMenuId?: any; + parentMenuName?: any; + tree: boolean; + crud: boolean; +} + +export interface TableQuery extends PageQuery { + tableName: string; + tableComment: string; + dataName: string; +} + +export interface DbColumnVO extends BaseEntity { + createDept?: any; + columnId?: any; + tableId?: any; + columnName?: any; + columnComment?: any; + columnType?: any; + javaType?: any; + javaField?: any; + isPk?: any; + isIncrement?: any; + isRequired?: any; + isInsert?: any; + isEdit?: any; + isList?: any; + isQuery?: any; + queryType?: any; + htmlType?: any; + dictType?: any; + sort?: any; + increment: boolean; + capJavaField?: any; + usableColumn: boolean; + superColumn: boolean; + list: boolean; + pk: boolean; + insert: boolean; + edit: boolean; + query: boolean; + required: boolean; +} + +export interface DbTableVO { + createDept?: any; + tableId?: any; + tableName: string; + tableComment: string; + subTableName?: any; + subTableFkName?: any; + className?: any; + tplCategory?: any; + packageName?: any; + moduleName?: any; + businessName?: any; + functionName?: any; + functionAuthor?: any; + genType?: any; + genPath?: any; + pkColumn?: any; + columns: DbColumnVO[]; + options?: any; + remark?: any; + treeCode?: any; + treeParentCode?: any; + treeName?: any; + menuIds?: any; + parentMenuId?: any; + parentMenuName?: any; + tree: boolean; + crud: boolean; +} + +export interface DbTableQuery extends PageQuery { + dataName: string; + tableName: string; + tableComment: string; +} + +export interface GenTableVO { + info: DbTableVO; + rows: DbColumnVO[]; + tables: DbTableVO[]; +} + +export interface DbColumnForm extends BaseEntity { + createDept: number; + columnId: string; + tableId: string; + columnName: string; + columnComment: string; + columnType: string; + javaType: string; + javaField: string; + isPk: string; + isIncrement: string; + isRequired: string; + isInsert?: any; + isEdit: string; + isList: string; + isQuery?: any; + queryType: string; + htmlType: string; + dictType: string; + sort: number; + increment: boolean; + capJavaField: string; + usableColumn: boolean; + superColumn: boolean; + list: boolean; + pk: boolean; + insert: boolean; + edit: boolean; + query: boolean; + required: boolean; +} + +export interface DbParamForm { + treeCode?: any; + treeName?: any; + treeParentCode?: any; + parentMenuId: string; +} + +export interface DbTableForm extends BaseEntity { + createDept?: any; + tableId: string | string; + tableName: string; + tableComment: string; + subTableName?: any; + subTableFkName?: any; + className: string; + tplCategory: string; + packageName: string; + moduleName: string; + businessName: string; + functionName: string; + functionAuthor: string; + genType: string; + genPath: string; + pkColumn?: any; + columns: DbColumnForm[]; + options: string; + remark?: any; + treeCode?: any; + treeParentCode?: any; + treeName?: any; + menuIds?: any; + parentMenuId: string; + parentMenuName?: any; + tree: boolean; + crud: boolean; + params: DbParamForm; +} diff --git a/src/api/types.ts b/src/api/types.ts new file mode 100644 index 0000000..617286c --- /dev/null +++ b/src/api/types.ts @@ -0,0 +1,59 @@ +/** + * 注册 + */ +export type RegisterForm = { + tenantId: string; + username: string; + password: string; + confirmPassword?: string; + code?: string; + uuid?: string; + userType?: string; +}; + +/** + * 登录请求 + */ +export interface LoginData { + tenantId?: string; + username?: string; + password?: string; + rememberMe?: boolean; + socialCode?: string; + socialState?: string; + source?: string; + code?: string; + uuid?: string; + clientId: string; + grantType: string; +} + +/** + * 登录响应 + */ +export interface LoginResult { + access_token: string; +} + +/** + * 验证码返回 + */ +export interface VerifyCodeResult { + captchaEnabled: boolean; + uuid?: string; + img?: string; +} + +/** + * 租户 + */ +export interface TenantVO { + companyName: string; + domain: any; + tenantId: string; +} + +export interface TenantInfo { + tenantEnabled: boolean; + voList: TenantVO[]; +} diff --git a/src/api/workflow/category/index.ts b/src/api/workflow/category/index.ts new file mode 100644 index 0000000..e9723b0 --- /dev/null +++ b/src/api/workflow/category/index.ts @@ -0,0 +1,63 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { CategoryVO, CategoryForm, CategoryQuery } from '@/api/workflow/category/types'; + +/** + * 查询流程分类列表 + * @param query + * @returns {*} + */ + +export const listCategory = (query?: CategoryQuery): AxiosPromise => { + return request({ + url: '/workflow/category/list', + method: 'get', + params: query + }); +}; + +/** + * 查询流程分类详细 + * @param id + */ +export const getCategory = (id: string | number): AxiosPromise => { + return request({ + url: '/workflow/category/' + id, + method: 'get' + }); +}; + +/** + * 新增流程分类 + * @param data + */ +export const addCategory = (data: CategoryForm) => { + return request({ + url: '/workflow/category', + method: 'post', + data: data + }); +}; + +/** + * 修改流程分类 + * @param data + */ +export const updateCategory = (data: CategoryForm) => { + return request({ + url: '/workflow/category', + method: 'put', + data: data + }); +}; + +/** + * 删除流程分类 + * @param id + */ +export const delCategory = (id: string | number | Array) => { + return request({ + url: '/workflow/category/' + id, + method: 'delete' + }); +}; diff --git a/src/api/workflow/category/types.ts b/src/api/workflow/category/types.ts new file mode 100644 index 0000000..414fa55 --- /dev/null +++ b/src/api/workflow/category/types.ts @@ -0,0 +1,67 @@ +export interface CategoryVO { + /** + * 主键 + */ + id: string; + + /** + * 分类名称 + */ + categoryName: string; + + /** + * 分类编码 + */ + categoryCode: string; + + /** + * 父级id + */ + parentId: string | number; + + /** + * 排序 + */ + sortNum: number; + + children?: CategoryVO[]; +} + +export interface CategoryForm extends BaseEntity { + /** + * 主键 + */ + id?: string | number; + + /** + * 分类名称 + */ + categoryName?: string; + + /** + * 分类编码 + */ + categoryCode?: string; + + /** + * 父级id + */ + parentId?: string | number; + + /** + * 排序 + */ + sortNum?: number; +} + +export interface CategoryQuery extends PageQuery { + /** + * 分类名称 + */ + categoryName?: string; + + /** + * 分类编码 + */ + categoryCode?: string; +} diff --git a/src/api/workflow/definitionConfig/index.ts b/src/api/workflow/definitionConfig/index.ts new file mode 100644 index 0000000..d34bf05 --- /dev/null +++ b/src/api/workflow/definitionConfig/index.ts @@ -0,0 +1,49 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { DefinitionConfigVO, DefinitionConfigForm } from '@/api/workflow/definitionConfig/types'; + +/** + * 查询表单配置详细 + * @param definitionId + */ +export const getByDefId = (definitionId: string | number): AxiosPromise => { + return request({ + url: '/workflow/definitionConfig/getByDefId/' + definitionId, + method: 'get' + }); +}; + +/** + * 新增表单配置 + * @param data + */ +export const saveOrUpdate = (data: DefinitionConfigForm) => { + return request({ + url: '/workflow/definitionConfig/saveOrUpdate', + method: 'post', + data: data + }); +}; + +/** + * 删除表单配置 + * @param id + */ +export const deldefinitionConfig = (id: string | number | Array) => { + return request({ + url: '/workflow/definitionConfig/' + id, + method: 'delete' + }); +}; + +/** + * 查询流程定义配置排除当前查询的流程定义 + * @param tableName + * @param definitionId + */ +export const getByTableNameNotDefId = (tableName: string, definitionId: string | number) => { + return request({ + url: `/workflow/definitionConfig/getByTableNameNotDefId/${tableName}/${definitionId}`, + method: 'get' + }); +}; diff --git a/src/api/workflow/definitionConfig/types.ts b/src/api/workflow/definitionConfig/types.ts new file mode 100644 index 0000000..7627403 --- /dev/null +++ b/src/api/workflow/definitionConfig/types.ts @@ -0,0 +1,102 @@ +import { FormManageVO } from '@/api/workflow/formManage/types'; + +export interface DefinitionConfigVO { + /** + * 主键 + */ + id: string | number; + + /** + * 表名 + */ + tableName?: string; + + /** + * 流程定义ID + */ + definitionId: string | number; + + /** + * 流程KEY + */ + processKey: string; + + /** + * 流程版本 + */ + version?: string | number; + + /** + * 备注 + */ + remark: string; + + /** + * 表单管理 + */ + wfFormManageVo: FormManageVO; +} + +export interface DefinitionConfigForm extends BaseEntity { + /** + * 主键 + */ + id?: string | number; + + /** + * 表名 + */ + tableName?: string; + + /** + * 流程定义ID + */ + definitionId?: string | number; + + /** + * 流程KEY + */ + processKey?: string; + + /** + * 流程版本 + */ + version?: string | number; + + /** + * 备注 + */ + remark?: string; + + /** + * 表单管理 + */ + wfFormManageVo?: FormManageVO; +} + +export interface DefinitionConfigQuery extends PageQuery { + /** + * 表名 + */ + tableName?: string; + + /** + * 流程定义ID + */ + definitionId?: string | number; + + /** + * 流程KEY + */ + processKey?: string; + + /** + * 流程版本 + */ + version?: string | number; + + /** + * 表单管理 + */ + wfFormManageVo: FormManageVO; +} diff --git a/src/api/workflow/formManage/index.ts b/src/api/workflow/formManage/index.ts new file mode 100644 index 0000000..c2930cf --- /dev/null +++ b/src/api/workflow/formManage/index.ts @@ -0,0 +1,76 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { FormManageVO, FormManageForm, FormManageQuery } from '@/api/workflow/formManage/types'; + +/** + * 查询表单管理列表 + * @param query + * @returns {*} + */ + +export const listFormManage = (query?: FormManageQuery): AxiosPromise => { + return request({ + url: '/workflow/formManage/list', + method: 'get', + params: query + }); +}; + +/** + * 查询表单管理列表 + * @param query + * @returns {*} + */ + +export const selectListFormManage = (): AxiosPromise => { + return request({ + url: '/workflow/formManage/list/selectList', + method: 'get', + }); +}; + +/** + * 查询表单管理详细 + * @param id + */ +export const getFormManage = (id: string | number): AxiosPromise => { + return request({ + url: '/workflow/formManage/' + id, + method: 'get' + }); +}; + +/** + * 新增表单管理 + * @param data + */ +export const addFormManage = (data: FormManageForm) => { + return request({ + url: '/workflow/formManage', + method: 'post', + data: data + }); +}; + +/** + * 修改表单管理 + * @param data + */ +export const updateFormManage = (data: FormManageForm) => { + return request({ + url: '/workflow/formManage', + method: 'put', + data: data + }); +}; + +/** + * 删除表单管理 + * @param id + */ +export const delFormManage = (id: string | number | Array) => { + return request({ + url: '/workflow/formManage/' + id, + method: 'delete' + }); +}; diff --git a/src/api/workflow/formManage/types.ts b/src/api/workflow/formManage/types.ts new file mode 100644 index 0000000..6554f55 --- /dev/null +++ b/src/api/workflow/formManage/types.ts @@ -0,0 +1,69 @@ +export interface FormManageVO { + /** + * 主键 + */ + id: string | number; + + /** + * 表单名称 + */ + formName: string; + + /** + * 表单类型 + */ + formType: string; + /** + * 表单类型名称 + */ + formTypeName: string; + + /** + * 路由地址/表单ID + */ + router: string; + + /** + * 备注 + */ + remork: string; +} + +export interface FormManageForm extends BaseEntity { + /** + * 主键 + */ + id?: string | number; + + /** + * 表单名称 + */ + formName?: string; + + /** + * 表单类型 + */ + formType?: string; + + /** + * 路由地址/表单ID + */ + router?: string; + + /** + * 备注 + */ + remork?: string; +} + +export interface FormManageQuery extends PageQuery { + /** + * 表单名称 + */ + formName?: string; + + /** + * 表单类型 + */ + formType?: string; +} diff --git a/src/api/workflow/leave/index.ts b/src/api/workflow/leave/index.ts new file mode 100644 index 0000000..36c6fdf --- /dev/null +++ b/src/api/workflow/leave/index.ts @@ -0,0 +1,63 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { LeaveVO, LeaveQuery, LeaveForm } from '@/api/workflow/leave/types'; + +/** + * 查询请假列表 + * @param query + * @returns {*} + */ + +export const listLeave = (query?: LeaveQuery): AxiosPromise => { + return request({ + url: '/demo/leave/list', + method: 'get', + params: query + }); +}; + +/** + * 查询请假详细 + * @param id + */ +export const getLeave = (id: string | number): AxiosPromise => { + return request({ + url: '/demo/leave/' + id, + method: 'get' + }); +}; + +/** + * 新增请假 + * @param data + */ +export const addLeave = (data: LeaveForm): AxiosPromise => { + return request({ + url: '/demo/leave', + method: 'post', + data: data + }); +}; + +/** + * 修改请假 + * @param data + */ +export const updateLeave = (data: LeaveForm): AxiosPromise => { + return request({ + url: '/demo/leave', + method: 'put', + data: data + }); +}; + +/** + * 删除请假 + * @param id + */ +export const delLeave = (id: string | number | Array) => { + return request({ + url: '/demo/leave/' + id, + method: 'delete' + }); +}; diff --git a/src/api/workflow/leave/types.ts b/src/api/workflow/leave/types.ts new file mode 100644 index 0000000..b093546 --- /dev/null +++ b/src/api/workflow/leave/types.ts @@ -0,0 +1,24 @@ +export interface LeaveVO { + id: string | number; + leaveType: string; + startDate: string; + endDate: string; + leaveDays: number; + remark: string; + processInstanceVo: any; +} + +export interface LeaveForm extends BaseEntity { + id?: string | number; + leaveType?: string; + startDate?: string; + endDate?: string; + leaveDays?: number; + remark?: string; + processInstanceVo?: any; +} + +export interface LeaveQuery extends PageQuery { + startLeaveDays?: number; + endLeaveDays?: number; +} diff --git a/src/api/workflow/model/index.ts b/src/api/workflow/model/index.ts new file mode 100644 index 0000000..56f7fed --- /dev/null +++ b/src/api/workflow/model/index.ts @@ -0,0 +1,104 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { ModelForm, ModelQuery, ModelVO } from '@/api/workflow/model/types'; + +/** + * 查询模型列表 + * @param query + * @returns {*} + */ +export const listModel = (query: ModelQuery): AxiosPromise => { + return request({ + url: '/workflow/model/list', + method: 'get', + params: query + }); +}; + +/** + * 查询模型信息 + * @param query + * @returns {*} + */ +export const getInfo = (id: string): AxiosPromise => { + return request({ + url: '/workflow/model/getInfo/'+id, + method: 'get' + }); +}; + +/** + * 新增模型 + * @param data + * @returns {*} + */ +export const addModel = (data: ModelForm): AxiosPromise => { + return request({ + url: '/workflow/model/save', + method: 'post', + data: data + }); +}; + +/** + * 修改模型信息 + * @param data + * @returns {*} + */ +export function update(data: ModelForm): AxiosPromise { + return request({ + url: '/workflow/model/update', + method: 'put', + data: data + }); +} + +/** + * 修改模型信息 + * @param data + * @returns {*} + */ +export function editModelXml(data: ModelForm): AxiosPromise { + return request({ + url: '/workflow/model/editModelXml', + method: 'put', + data: data + }); +} + +/** + * 按id删除模型 + * @returns {*} + * @param id 模型id + */ +export function delModel(id: string | string[]): AxiosPromise { + return request({ + url: '/workflow/model/' + id, + method: 'delete' + }); +} + +/** + * 模型部署 + * @returns {*} + * @param id 模型id + */ +export const modelDeploy = (id: string): AxiosPromise => { + return request({ + url: `/workflow/model/modelDeploy/${id}`, + method: 'post' + }); +}; + +/** + * 复制模型 + * @param data + * @returns {*} + */ +export const copyModel = (data: ModelForm): AxiosPromise => { + return request({ + url: '/workflow/model/copyModel', + method: 'post', + data: data + }); +}; \ No newline at end of file diff --git a/src/api/workflow/model/types.ts b/src/api/workflow/model/types.ts new file mode 100644 index 0000000..40a0faa --- /dev/null +++ b/src/api/workflow/model/types.ts @@ -0,0 +1,66 @@ +export interface ModelForm { + id: string, + name: string; + key: string; + categoryCode: string; + xml:string, + svg:string, + description: string; +} + +export interface ModelQuery extends PageQuery { + name?: string; + key?: string; + categoryCode?: string; +} + +export interface OriginalPersistentState { + metaInfo: string; + editorSourceValueId: string; + createTime: string; + deploymentId?: string; + name: string; + tenantId: string; + category?: string; + version: number; + editorSourceExtraValueId?: string; + key: string; + lastUpdateTime: string; +} + +export interface PersistentState { + metaInfo: string; + editorSourceValueId: string; + createTime: string; + deploymentId?: string; + name: string; + tenantId: string; + category?: string; + version: number; + editorSourceExtraValueId?: string; + key: string; + lastUpdateTime: string; +} + +export interface ModelVO { + id: string; + revision: number; + originalPersistentState: OriginalPersistentState; + name: string; + key: string; + category?: string; + createTime: string; + lastUpdateTime: string; + version: number; + metaInfo: string; + deploymentId?: string; + editorSourceValueId: string; + editorSourceExtraValueId?: string; + tenantId: string; + persistentState: PersistentState; + revisionNext: number; + idPrefix: string; + inserted: boolean; + updated: boolean; + deleted: boolean; +} diff --git a/src/api/workflow/nodeConfig/index.ts b/src/api/workflow/nodeConfig/index.ts new file mode 100644 index 0000000..3270c17 --- /dev/null +++ b/src/api/workflow/nodeConfig/index.ts @@ -0,0 +1,63 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { NodeConfigVO, NodeConfigForm, NodeConfigQuery } from '@/api/workflow/nodeConfig/types'; + +/** + * 查询节点配置列表 + * @param query + * @returns {*} + */ + +export const listNodeConfig = (query?: NodeConfigQuery): AxiosPromise => { + return request({ + url: '/workflow/nodeConfig/list', + method: 'get', + params: query + }); +}; + +/** + * 查询节点配置详细 + * @param id + */ +export const getNodeConfig = (id: string | number): AxiosPromise => { + return request({ + url: '/workflow/nodeConfig/' + id, + method: 'get' + }); +}; + +/** + * 新增节点配置 + * @param data + */ +export const addNodeConfig = (data: NodeConfigForm) => { + return request({ + url: '/workflow/nodeConfig', + method: 'post', + data: data + }); +}; + +/** + * 修改节点配置 + * @param data + */ +export const updateNodeConfig = (data: NodeConfigForm) => { + return request({ + url: '/workflow/nodeConfig', + method: 'put', + data: data + }); +}; + +/** + * 删除节点配置 + * @param id + */ +export const delNodeConfig = (id: string | number | Array) => { + return request({ + url: '/workflow/nodeConfig/' + id, + method: 'delete' + }); +}; diff --git a/src/api/workflow/nodeConfig/types.ts b/src/api/workflow/nodeConfig/types.ts new file mode 100644 index 0000000..4e3a60b --- /dev/null +++ b/src/api/workflow/nodeConfig/types.ts @@ -0,0 +1,43 @@ +import { FormManageVO } from '@/api/workflow/formManage/types'; + +export interface NodeConfigVO { + /** + * 主键 + */ + id: string | number; + + /** + * 表单id + */ + formId: string | number; + + /** + * 表单类型 + */ + formType: string; + + /** + * 节点名称 + */ + nodeName: string; + + /** + * 节点id + */ + nodeId: string | number; + + /** + * 流程定义id + */ + definitionId: string | number; + + /** + * 表单管理 + */ + wfFormManageVo: FormManageVO; + +} + + + + diff --git a/src/api/workflow/processDefinition/index.ts b/src/api/workflow/processDefinition/index.ts new file mode 100644 index 0000000..c063120 --- /dev/null +++ b/src/api/workflow/processDefinition/index.ts @@ -0,0 +1,114 @@ +import request from '@/utils/request'; +import { ProcessDefinitionQuery, ProcessDefinitionVO, definitionXmlVO } from '@/api/workflow/processDefinition/types'; +import { AxiosPromise } from 'axios'; + +/** + * 获取流程定义列表 + * @param query 流程实例id + * @returns + */ +export const listProcessDefinition = (query: ProcessDefinitionQuery): AxiosPromise => { + return request({ + url: `/workflow/processDefinition/list`, + method: 'get', + params: query + }); +}; +/** + * 按照流程定义key获取流程定义 + * @param processInstanceId 流程实例id + * @returns + */ +export const getListByKey = (key: string) => { + return request({ + url: `/workflow/processDefinition/getListByKey/${key}`, + method: 'get' + }); +}; + +/** + * 通过流程定义id获取流程图 + */ +export const definitionImage = (processDefinitionId: string): AxiosPromise => { + return request({ + url: `/workflow/processDefinition/definitionImage/${processDefinitionId}` + '?t' + Math.random(), + method: 'get' + }); +}; + +/** + * 通过流程定义id获取xml + * @param processDefinitionId 流程定义id + * @returns + */ +export const definitionXml = (processDefinitionId: string): AxiosPromise => { + return request({ + url: `/workflow/processDefinition/definitionXml/${processDefinitionId}`, + method: 'get' + }); +}; + +/** + * 删除流程定义 + * @param deploymentId 部署id + * @param processDefinitionId 流程定义id + * @returns + */ +export const deleteProcessDefinition = (deploymentId: string | string[], processDefinitionId: string | string[]) => { + return request({ + url: `/workflow/processDefinition/${deploymentId}/${processDefinitionId}`, + method: 'delete' + }); +}; + +/** + * 挂起/激活 + * @param processDefinitionId 流程定义id + * @returns + */ +export const updateDefinitionState = (processDefinitionId: string) => { + return request({ + url: `/workflow/processDefinition/updateDefinitionState/${processDefinitionId}`, + method: 'put' + }); +}; + +/** + * 流程定义转换为模型 + * @param processDefinitionId 流程定义id + * @returns + */ +export const convertToModel = (processDefinitionId: string) => { + return request({ + url: `/workflow/processDefinition/convertToModel/${processDefinitionId}`, + method: 'put' + }); +}; + +/** + * 通过zip或xml部署流程定义 + * @returns + */ +export function deployProcessFile(data: any) { + return request({ + url: '/workflow/processDefinition/deployByFile', + method: 'post', + data: data, + headers: { + repeatSubmit: false + } + }); +} + +/** + * 迁移流程 + * @param currentProcessDefinitionId + * @param fromProcessDefinitionId + * @returns + */ +export const migrationDefinition = (currentProcessDefinitionId: string, fromProcessDefinitionId: string) => { + return request({ + url: `/workflow/processDefinition/migrationDefinition/${currentProcessDefinitionId}/${fromProcessDefinitionId}`, + method: 'put' + }); +}; diff --git a/src/api/workflow/processDefinition/types.ts b/src/api/workflow/processDefinition/types.ts new file mode 100644 index 0000000..979ec25 --- /dev/null +++ b/src/api/workflow/processDefinition/types.ts @@ -0,0 +1,24 @@ +import { DefinitionConfigVO } from '@/api/workflow/definitionConfig/types'; +export interface ProcessDefinitionQuery extends PageQuery { + key?: string; + name?: string; + categoryCode?: string; +} + +export interface ProcessDefinitionVO extends BaseEntity { + id: string; + name: string; + key: string; + version: number; + suspensionState: number; + resourceName: string; + diagramResourceName: string; + deploymentId: string; + deploymentTime: string; + wfDefinitionConfigVo: DefinitionConfigVO; +} + +export interface definitionXmlVO { + xml: string[]; + xmlStr: string; +} diff --git a/src/api/workflow/processInstance/index.ts b/src/api/workflow/processInstance/index.ts new file mode 100644 index 0000000..80f122f --- /dev/null +++ b/src/api/workflow/processInstance/index.ts @@ -0,0 +1,136 @@ +import request from '@/utils/request'; +import { ProcessInstanceQuery, ProcessInstanceVO } from '@/api/workflow/processInstance/types'; +import { AxiosPromise } from 'axios'; + +/** + * 查询运行中实例列表 + * @param query + * @returns {*} + */ +export const getPageByRunning = (query: ProcessInstanceQuery): AxiosPromise => { + return request({ + url: '/workflow/processInstance/getPageByRunning', + method: 'get', + params: query + }); +}; + +/** + * 查询已完成实例列表 + * @param query + * @returns {*} + */ +export const getPageByFinish = (query: ProcessInstanceQuery): AxiosPromise => { + return request({ + url: '/workflow/processInstance/getPageByFinish', + method: 'get', + params: query + }); +}; + +/** + * 通过流程实例id获取历史流程图 + */ +export const getHistoryImage = (processInstanceId: string) => { + return request({ + url: `/workflow/processInstance/getHistoryImage/${processInstanceId}` + '?t' + Math.random(), + method: 'get' + }); +}; + +/** + * 通过流程实例id获取历史流程图运行中,历史等节点 + */ +export const getHistoryList = (instanceId: string): AxiosPromise> => { + return request({ + url: `/workflow/processInstance/getHistoryList/${instanceId}` + '?t' + Math.random(), + method: 'get' + }); +}; + +/** + * 获取审批记录 + * @param processInstanceId 流程实例id + * @returns + */ +export const getHistoryRecord = (processInstanceId: string) => { + return request({ + url: `/workflow/processInstance/getHistoryRecord/${processInstanceId}`, + method: 'get' + }); +}; + +/** + * 作废 + * @param data 参数 + * @returns + */ +export const deleteRunInstance = (data: object) => { + return request({ + url: `/workflow/processInstance/deleteRunInstance`, + method: 'post', + data: data + }); +}; + +/** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * @param processInstanceId 流程实例id + * @returns + */ +export const deleteRunAndHisInstance = (processInstanceId: string | string[]) => { + return request({ + url: `/workflow/processInstance/deleteRunAndHisInstance/${processInstanceId}`, + method: 'delete' + }); +}; + +/** + * 已完成的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * @param processInstanceId 流程实例id + * @returns + */ +export const deleteFinishAndHisInstance = (processInstanceId: string | string[]) => { + return request({ + url: `/workflow/processInstance/deleteFinishAndHisInstance/${processInstanceId}`, + method: 'delete' + }); +}; + +/** + * 分页查询当前登录人单据 + * @param query + * @returns {*} + */ +export const getPageByCurrent = (query: ProcessInstanceQuery): AxiosPromise => { + return request({ + url: '/workflow/processInstance/getPageByCurrent', + method: 'get', + params: query + }); +}; + +/** + * 撤销流程 + * @param processInstanceId 流程实例id + * @returns + */ +export const cancelProcessApply = (processInstanceId: string) => { + return request({ + url: `/workflow/processInstance/cancelProcessApply/${processInstanceId}`, + method: 'post' + }); +}; + +export default { + getPageByRunning, + getPageByFinish, + getHistoryImage, + getHistoryList, + getHistoryRecord, + deleteRunInstance, + deleteRunAndHisInstance, + deleteFinishAndHisInstance, + getPageByCurrent, + cancelProcessApply +}; diff --git a/src/api/workflow/processInstance/types.ts b/src/api/workflow/processInstance/types.ts new file mode 100644 index 0000000..99d0511 --- /dev/null +++ b/src/api/workflow/processInstance/types.ts @@ -0,0 +1,27 @@ +import { TaskVO } from '@/api/workflow/task/types'; + +export interface ProcessInstanceQuery extends PageQuery { + categoryCode?: string; + name?: string; + key?: string; + startUserId?: string; + businessKey?: string; +} + +export interface ProcessInstanceVO extends BaseEntity { + id: string; + processDefinitionId: string; + processDefinitionName: string; + processDefinitionKey: string; + processDefinitionVersion: string; + deploymentId: string; + businessKey: string; + isSuspended?: any; + tenantId: string; + startTime: string; + endTime?: string; + startUserId: string; + businessStatus: string; + businessStatusName: string; + taskVoList: TaskVO[]; +} diff --git a/src/api/workflow/task/index.ts b/src/api/workflow/task/index.ts new file mode 100644 index 0000000..d29de30 --- /dev/null +++ b/src/api/workflow/task/index.ts @@ -0,0 +1,264 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { TaskQuery, TaskVO } from '@/api/workflow/task/types'; + +/** + * 查询待办列表 + * @param query + * @returns {*} + */ +export const getPageByTaskWait = (query: TaskQuery): AxiosPromise => { + return request({ + url: '/workflow/task/getPageByTaskWait', + method: 'get', + params: query + }); +}; + +/** + * 查询已办列表 + * @param query + * @returns {*} + */ +export const getPageByTaskFinish = (query: TaskQuery): AxiosPromise => { + return request({ + url: '/workflow/task/getPageByTaskFinish', + method: 'get', + params: query + }); +}; + +/** + * 查询当前用户的抄送列表 + * @param query + * @returns {*} + */ +export const getPageByTaskCopy = (query: TaskQuery): AxiosPromise => { + return request({ + url: '/workflow/task/getPageByTaskCopy', + method: 'get', + params: query + }); +}; + +/** + * 当前租户所有待办任务 + * @param query + * @returns {*} + */ +export const getPageByAllTaskWait = (query: TaskQuery): AxiosPromise => { + return request({ + url: '/workflow/task/getPageByAllTaskWait', + method: 'get', + params: query + }); +}; + +/** + * 当前租户所有已办任务 + * @param query + * @returns {*} + */ +export const getPageByAllTaskFinish = (query: TaskQuery): AxiosPromise => { + return request({ + url: '/workflow/task/getPageByAllTaskFinish', + method: 'get', + params: query + }); +}; + +/** + * 启动流程 + * @param data + * @returns {*} + */ +export const startWorkFlow = (data: object): any => { + return request({ + url: '/workflow/task/startWorkFlow', + method: 'post', + data: data + }); +}; + +/** + * 办理流程 + * @param data + * @returns {*} + */ +export const completeTask = (data: object) => { + return request({ + url: '/workflow/task/completeTask', + method: 'post', + data: data + }); +}; + +/** + * 认领任务 + * @param taskId + * @returns {*} + */ +export const claim = (taskId: string): any => { + return request({ + url: '/workflow/task/claim/' + taskId, + method: 'post' + }); +}; + +/** + * 归还任务 + * @param taskId + * @returns {*} + */ +export const returnTask = (taskId: string): any => { + return request({ + url: '/workflow/task/returnTask/' + taskId, + method: 'post' + }); +}; + +/** + * 任务驳回 + * @param data + * @returns {*} + */ +export const backProcess = (data: any): any => { + return request({ + url: '/workflow/task/backProcess', + method: 'post', + data: data + }); +}; + +/** + * 获取当前任务 + * @param taskId + * @returns + */ +export const getTaskById = (taskId: string) => { + return request({ + url: '/workflow/task/getTaskById/' + taskId, + method: 'get' + }); +}; + +/** + * 加签 + * @param data + * @returns + */ +export const addMultiInstanceExecution = (data: any) => { + return request({ + url: '/workflow/task/addMultiInstanceExecution', + method: 'post', + data: data + }); +}; + +/** + * 减签 + * @param data + * @returns + */ +export const deleteMultiInstanceExecution = (data: any) => { + return request({ + url: '/workflow/task/deleteMultiInstanceExecution', + method: 'post', + data: data + }); +}; + +/** + * 修改任务办理人 + * @param taskIds + * @param userId + * @returns + */ +export const updateAssignee = (taskIds: Array, userId: string) => { + return request({ + url: `/workflow/task/updateAssignee/${taskIds}/${userId}`, + method: 'put' + }); +}; + +/** + * 转办任务 + * @returns + */ +export const transferTask = (data: any) => { + return request({ + url: `/workflow/task/transferTask`, + method: 'post', + data: data + }); +}; + +/** + * 终止任务 + * @returns + */ +export const terminationTask = (data: any) => { + return request({ + url: `/workflow/task/terminationTask`, + method: 'post', + data: data + }); +}; + +/** + * 查询流程变量 + * @returns + */ +export const getInstanceVariable = (taskId: string) => { + return request({ + url: `/workflow/task/getInstanceVariable/${taskId}`, + method: 'get' + }); +}; + +/** + * 获取可驳回得任务节点 + * @returns + */ +export const getTaskNodeList = (processInstanceId: string) => { + return request({ + url: `/workflow/task/getTaskNodeList/${processInstanceId}`, + method: 'get' + }); +}; + +/** + * 委托任务 + * @returns + */ +export const delegateTask = (data: any) => { + return request({ + url: `/workflow/task/delegateTask`, + method: 'post', + data: data + }); +}; + +/** + * 查询工作流任务用户选择加签人员 + * @param taskId + * @returns {*} + */ +export const getTaskUserIdsByAddMultiInstance = (taskId: string) => { + return request({ + url: '/workflow/task/getTaskUserIdsByAddMultiInstance/' + taskId, + method: 'get' + }); +}; + +/** + * 查询工作流选择减签人员 + * @param taskId + * @returns {*} + */ +export const getListByDeleteMultiInstance = (taskId: string) => { + return request({ + url: '/workflow/task/getListByDeleteMultiInstance/' + taskId, + method: 'get' + }); +}; diff --git a/src/api/workflow/task/types.ts b/src/api/workflow/task/types.ts new file mode 100644 index 0000000..0425a1a --- /dev/null +++ b/src/api/workflow/task/types.ts @@ -0,0 +1,49 @@ +import { NodeConfigVO } from '@/api/workflow/nodeConfig/types'; +import { DefinitionConfigVO } from '@/api/workflow/definitionConfig/types'; +export interface TaskQuery extends PageQuery { + name?: string; + processDefinitionKey?: string; + processDefinitionName?: string; +} + +export interface ParticipantVo { + groupIds?: string[] | number[]; + candidate: string[] | number[]; + candidateName: string[]; + claim: boolean; +} + +export interface TaskVO extends BaseEntity { + id: string; + name: string; + description?: string; + priority: number; + owner?: string; + assignee?: string | number; + assigneeName?: string; + processInstanceId: string; + executionId: string; + taskDefinitionId?: any; + processDefinitionId: string; + endTime?: string; + taskDefinitionKey: string; + dueDate?: string; + category?: any; + parentTaskId?: any; + tenantId: string; + claimTime?: string; + businessStatus?: string; + businessStatusName?: string; + processDefinitionName?: string; + processDefinitionKey?: string; + participantVo?: ParticipantVo; + multiInstance?: boolean; + businessKey?: string; + wfNodeConfigVo?: NodeConfigVO; + wfDefinitionConfigVo?: DefinitionConfigVO; +} + +export interface VariableVo { + key: string; + value: string; +} diff --git a/src/api/workflow/workflowCommon/index.ts b/src/api/workflow/workflowCommon/index.ts new file mode 100644 index 0000000..a90216a --- /dev/null +++ b/src/api/workflow/workflowCommon/index.ts @@ -0,0 +1,29 @@ +import { RouterJumpVo } from '@/api/workflow/workflowCommon/types'; + +export default { + routerJump(routerJumpVo: RouterJumpVo,proxy){ + if (routerJumpVo.wfNodeConfigVo && routerJumpVo.wfNodeConfigVo.formType === 'static' && routerJumpVo.wfNodeConfigVo.wfFormManageVo) { + proxy.$tab.closePage(proxy.$route); + proxy.$router.push({ + path: `${routerJumpVo.wfNodeConfigVo.wfFormManageVo.router}`, + query: { + id: routerJumpVo.businessKey, + type: routerJumpVo.type, + taskId: routerJumpVo.taskId + } + }); + } else if (routerJumpVo.wfNodeConfigVo && routerJumpVo.wfNodeConfigVo.formType === 'dynamic' && routerJumpVo.wfNodeConfigVo.wfFormManageVo) { + proxy.$tab.closePage(proxy.$route); + proxy.$router.push({ + path: `${routerJumpVo.wfNodeConfigVo.wfFormManageVo.router}`, + query: { + id: routerJumpVo.businessKey, + type: routerJumpVo.type, + taskId: routerJumpVo.taskId + } + }); + }else { + proxy?.$modal.msgError('请到模型配置菜单!'); + } + } +} \ No newline at end of file diff --git a/src/api/workflow/workflowCommon/types.ts b/src/api/workflow/workflowCommon/types.ts new file mode 100644 index 0000000..0f1ef1f --- /dev/null +++ b/src/api/workflow/workflowCommon/types.ts @@ -0,0 +1,16 @@ +import { NodeConfigVO } from '@/api/workflow/nodeConfig/types'; +import { DefinitionConfigVO } from '@/api/workflow/definitionConfig/types'; + +export interface RouterJumpVo { + wfNodeConfigVo: NodeConfigVO; + wfDefinitionConfigVo: DefinitionConfigVO; + businessKey: string; + taskId: string; + type: string; +} + +export interface StartProcessBo { + businessKey: string | number; + tableName: string; + variables: any; +} diff --git a/src/assets/401_images/401.gif b/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/src/assets/401_images/401.gif differ diff --git a/src/assets/404_images/404.png b/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/src/assets/404_images/404.png differ diff --git a/src/assets/404_images/404_cloud.png b/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/src/assets/404_images/404_cloud.png differ diff --git a/src/assets/icons/svg/404.svg b/src/assets/icons/svg/404.svg new file mode 100644 index 0000000..6df5019 --- /dev/null +++ b/src/assets/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/bug.svg b/src/assets/icons/svg/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/src/assets/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/build.svg b/src/assets/icons/svg/build.svg new file mode 100644 index 0000000..97c4688 --- /dev/null +++ b/src/assets/icons/svg/build.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/button.svg b/src/assets/icons/svg/button.svg new file mode 100644 index 0000000..904fddc --- /dev/null +++ b/src/assets/icons/svg/button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/caret-back.svg b/src/assets/icons/svg/caret-back.svg new file mode 100644 index 0000000..9bae722 --- /dev/null +++ b/src/assets/icons/svg/caret-back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/caret-forward.svg b/src/assets/icons/svg/caret-forward.svg new file mode 100644 index 0000000..1ec3f7d --- /dev/null +++ b/src/assets/icons/svg/caret-forward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/cascader.svg b/src/assets/icons/svg/cascader.svg new file mode 100644 index 0000000..e256024 --- /dev/null +++ b/src/assets/icons/svg/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/category.svg b/src/assets/icons/svg/category.svg new file mode 100644 index 0000000..df92526 --- /dev/null +++ b/src/assets/icons/svg/category.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/chart.svg b/src/assets/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/checkbox.svg b/src/assets/icons/svg/checkbox.svg new file mode 100644 index 0000000..013fd3a --- /dev/null +++ b/src/assets/icons/svg/checkbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/clipboard.svg b/src/assets/icons/svg/clipboard.svg new file mode 100644 index 0000000..90923ff --- /dev/null +++ b/src/assets/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/code.svg b/src/assets/icons/svg/code.svg new file mode 100644 index 0000000..5f9c5ab --- /dev/null +++ b/src/assets/icons/svg/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/color.svg b/src/assets/icons/svg/color.svg new file mode 100644 index 0000000..44a81aa --- /dev/null +++ b/src/assets/icons/svg/color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/company.svg b/src/assets/icons/svg/company.svg new file mode 100644 index 0000000..fcf1394 --- /dev/null +++ b/src/assets/icons/svg/company.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/component.svg b/src/assets/icons/svg/component.svg new file mode 100644 index 0000000..29c3458 --- /dev/null +++ b/src/assets/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/dashboard.svg b/src/assets/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/date-range.svg b/src/assets/icons/svg/date-range.svg new file mode 100644 index 0000000..fda571e --- /dev/null +++ b/src/assets/icons/svg/date-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/date.svg b/src/assets/icons/svg/date.svg new file mode 100644 index 0000000..52dc73e --- /dev/null +++ b/src/assets/icons/svg/date.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/dict.svg b/src/assets/icons/svg/dict.svg new file mode 100644 index 0000000..4849377 --- /dev/null +++ b/src/assets/icons/svg/dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/documentation.svg b/src/assets/icons/svg/documentation.svg new file mode 100644 index 0000000..7043122 --- /dev/null +++ b/src/assets/icons/svg/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/download.svg b/src/assets/icons/svg/download.svg new file mode 100644 index 0000000..c896951 --- /dev/null +++ b/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/drag.svg b/src/assets/icons/svg/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/src/assets/icons/svg/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/druid.svg b/src/assets/icons/svg/druid.svg new file mode 100644 index 0000000..a2b4b4e --- /dev/null +++ b/src/assets/icons/svg/druid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/edit.svg b/src/assets/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/education.svg b/src/assets/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/email.svg b/src/assets/icons/svg/email.svg new file mode 100644 index 0000000..74d25e2 --- /dev/null +++ b/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/example.svg b/src/assets/icons/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/src/assets/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/excel.svg b/src/assets/icons/svg/excel.svg new file mode 100644 index 0000000..74d97b8 --- /dev/null +++ b/src/assets/icons/svg/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/exit-fullscreen.svg b/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/eye-open.svg b/src/assets/icons/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/src/assets/icons/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/eye.svg b/src/assets/icons/svg/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/src/assets/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/finish.svg b/src/assets/icons/svg/finish.svg new file mode 100644 index 0000000..4685c23 --- /dev/null +++ b/src/assets/icons/svg/finish.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/form.svg b/src/assets/icons/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/src/assets/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/fullscreen.svg b/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/gitee.svg b/src/assets/icons/svg/gitee.svg new file mode 100644 index 0000000..6324608 --- /dev/null +++ b/src/assets/icons/svg/gitee.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/svg/github.svg b/src/assets/icons/svg/github.svg new file mode 100644 index 0000000..db0a0d4 --- /dev/null +++ b/src/assets/icons/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/guide.svg b/src/assets/icons/svg/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/src/assets/icons/svg/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/icon.svg b/src/assets/icons/svg/icon.svg new file mode 100644 index 0000000..82be8ee --- /dev/null +++ b/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/input.svg b/src/assets/icons/svg/input.svg new file mode 100644 index 0000000..ab91381 --- /dev/null +++ b/src/assets/icons/svg/input.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/international.svg b/src/assets/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/job.svg b/src/assets/icons/svg/job.svg new file mode 100644 index 0000000..2a93a25 --- /dev/null +++ b/src/assets/icons/svg/job.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/language.svg b/src/assets/icons/svg/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/src/assets/icons/svg/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/link.svg b/src/assets/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/list.svg b/src/assets/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/lock.svg b/src/assets/icons/svg/lock.svg new file mode 100644 index 0000000..74fee54 --- /dev/null +++ b/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/log.svg b/src/assets/icons/svg/log.svg new file mode 100644 index 0000000..d879d33 --- /dev/null +++ b/src/assets/icons/svg/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/logininfor.svg b/src/assets/icons/svg/logininfor.svg new file mode 100644 index 0000000..267f844 --- /dev/null +++ b/src/assets/icons/svg/logininfor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/maxkey.svg b/src/assets/icons/svg/maxkey.svg new file mode 100644 index 0000000..f8f8a7d --- /dev/null +++ b/src/assets/icons/svg/maxkey.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/svg/message.svg b/src/assets/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/model.svg b/src/assets/icons/svg/model.svg new file mode 100644 index 0000000..0c57d70 --- /dev/null +++ b/src/assets/icons/svg/model.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/money.svg b/src/assets/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/monitor.svg b/src/assets/icons/svg/monitor.svg new file mode 100644 index 0000000..bc308cb --- /dev/null +++ b/src/assets/icons/svg/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/my-copy.svg b/src/assets/icons/svg/my-copy.svg new file mode 100644 index 0000000..49f69fa --- /dev/null +++ b/src/assets/icons/svg/my-copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/my-task.svg b/src/assets/icons/svg/my-task.svg new file mode 100644 index 0000000..1f1ea44 --- /dev/null +++ b/src/assets/icons/svg/my-task.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/nested.svg b/src/assets/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/number.svg b/src/assets/icons/svg/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/src/assets/icons/svg/number.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/online.svg b/src/assets/icons/svg/online.svg new file mode 100644 index 0000000..330a202 --- /dev/null +++ b/src/assets/icons/svg/online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/password.svg b/src/assets/icons/svg/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/src/assets/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/pdf.svg b/src/assets/icons/svg/pdf.svg new file mode 100644 index 0000000..957aa0c --- /dev/null +++ b/src/assets/icons/svg/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/people.svg b/src/assets/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/peoples.svg b/src/assets/icons/svg/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/phone.svg b/src/assets/icons/svg/phone.svg new file mode 100644 index 0000000..ab8e8c4 --- /dev/null +++ b/src/assets/icons/svg/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/post.svg b/src/assets/icons/svg/post.svg new file mode 100644 index 0000000..2922c61 --- /dev/null +++ b/src/assets/icons/svg/post.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/process-definition.svg b/src/assets/icons/svg/process-definition.svg new file mode 100644 index 0000000..202d200 --- /dev/null +++ b/src/assets/icons/svg/process-definition.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/qq.svg b/src/assets/icons/svg/qq.svg new file mode 100644 index 0000000..ee13d4e --- /dev/null +++ b/src/assets/icons/svg/qq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/question.svg b/src/assets/icons/svg/question.svg new file mode 100644 index 0000000..cf75bd4 --- /dev/null +++ b/src/assets/icons/svg/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/radio.svg b/src/assets/icons/svg/radio.svg new file mode 100644 index 0000000..0cde345 --- /dev/null +++ b/src/assets/icons/svg/radio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/rate.svg b/src/assets/icons/svg/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/src/assets/icons/svg/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/redis-list.svg b/src/assets/icons/svg/redis-list.svg new file mode 100644 index 0000000..98a15b2 --- /dev/null +++ b/src/assets/icons/svg/redis-list.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/redis.svg b/src/assets/icons/svg/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/src/assets/icons/svg/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/row.svg b/src/assets/icons/svg/row.svg new file mode 100644 index 0000000..0780992 --- /dev/null +++ b/src/assets/icons/svg/row.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/search.svg b/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/select.svg b/src/assets/icons/svg/select.svg new file mode 100644 index 0000000..d628382 --- /dev/null +++ b/src/assets/icons/svg/select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/server.svg b/src/assets/icons/svg/server.svg new file mode 100644 index 0000000..eb287e3 --- /dev/null +++ b/src/assets/icons/svg/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/shopping.svg b/src/assets/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/size.svg b/src/assets/icons/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/skill.svg b/src/assets/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/slider.svg b/src/assets/icons/svg/slider.svg new file mode 100644 index 0000000..fbe4f39 --- /dev/null +++ b/src/assets/icons/svg/slider.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/star.svg b/src/assets/icons/svg/star.svg new file mode 100644 index 0000000..6cf86e6 --- /dev/null +++ b/src/assets/icons/svg/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/swagger.svg b/src/assets/icons/svg/swagger.svg new file mode 100644 index 0000000..05d4e7b --- /dev/null +++ b/src/assets/icons/svg/swagger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/switch.svg b/src/assets/icons/svg/switch.svg new file mode 100644 index 0000000..0ba61e3 --- /dev/null +++ b/src/assets/icons/svg/switch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/system.svg b/src/assets/icons/svg/system.svg new file mode 100644 index 0000000..5992593 --- /dev/null +++ b/src/assets/icons/svg/system.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/tab.svg b/src/assets/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/table.svg b/src/assets/icons/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/src/assets/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/textarea.svg b/src/assets/icons/svg/textarea.svg new file mode 100644 index 0000000..2709f29 --- /dev/null +++ b/src/assets/icons/svg/textarea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/theme.svg b/src/assets/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/time-range.svg b/src/assets/icons/svg/time-range.svg new file mode 100644 index 0000000..13c1202 --- /dev/null +++ b/src/assets/icons/svg/time-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/time.svg b/src/assets/icons/svg/time.svg new file mode 100644 index 0000000..b376e32 --- /dev/null +++ b/src/assets/icons/svg/time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/tool.svg b/src/assets/icons/svg/tool.svg new file mode 100644 index 0000000..48e0e35 --- /dev/null +++ b/src/assets/icons/svg/tool.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/topiam.svg b/src/assets/icons/svg/topiam.svg new file mode 100644 index 0000000..e7ea057 --- /dev/null +++ b/src/assets/icons/svg/topiam.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/icons/svg/tree-table.svg b/src/assets/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/tree.svg b/src/assets/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/upload.svg b/src/assets/icons/svg/upload.svg new file mode 100644 index 0000000..bae49c0 --- /dev/null +++ b/src/assets/icons/svg/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/user.svg b/src/assets/icons/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/validCode.svg b/src/assets/icons/svg/validCode.svg new file mode 100644 index 0000000..cfb1021 --- /dev/null +++ b/src/assets/icons/svg/validCode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/waiting.svg b/src/assets/icons/svg/waiting.svg new file mode 100644 index 0000000..2c2042d --- /dev/null +++ b/src/assets/icons/svg/waiting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/wechat.svg b/src/assets/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/workflow.svg b/src/assets/icons/svg/workflow.svg new file mode 100644 index 0000000..2f7423a --- /dev/null +++ b/src/assets/icons/svg/workflow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/zip.svg b/src/assets/icons/svg/zip.svg new file mode 100644 index 0000000..f806fc4 --- /dev/null +++ b/src/assets/icons/svg/zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/dark.svg b/src/assets/images/dark.svg new file mode 100644 index 0000000..f646bd7 --- /dev/null +++ b/src/assets/images/dark.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/light.svg b/src/assets/images/light.svg new file mode 100644 index 0000000..ab7cc08 --- /dev/null +++ b/src/assets/images/light.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/login-background.jpg b/src/assets/images/login-background.jpg new file mode 100644 index 0000000..fa6408b Binary files /dev/null and b/src/assets/images/login-background.jpg differ diff --git a/src/assets/images/profile.jpg b/src/assets/images/profile.jpg new file mode 100644 index 0000000..f4fde57 Binary files /dev/null and b/src/assets/images/profile.jpg differ diff --git a/src/assets/logo/logo.png b/src/assets/logo/logo.png new file mode 100644 index 0000000..3f919d8 Binary files /dev/null and b/src/assets/logo/logo.png differ diff --git a/src/assets/styles/btn.scss b/src/assets/styles/btn.scss new file mode 100644 index 0000000..a1ccd01 --- /dev/null +++ b/src/assets/styles/btn.scss @@ -0,0 +1,99 @@ +@import './variables.module.scss'; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue); +} + +.light-blue-btn { + @include colorBtn($light-blue); +} + +.red-btn { + @include colorBtn($red); +} + +.pink-btn { + @include colorBtn($pink); +} + +.green-btn { + @include colorBtn($green); +} + +.tiffany-btn { + @include colorBtn($tiffany); +} + +.yellow-btn { + @include colorBtn($yellow); +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss new file mode 100644 index 0000000..220e4ac --- /dev/null +++ b/src/assets/styles/element-ui.scss @@ -0,0 +1,149 @@ + +.el-collapse { + .collapse__title { + font-weight: 600; + padding: 0 8px; + font-size: 1.2em; + line-height: 1.1em; + } + .el-collapse-item__content { + padding: 0 8px; + } +} + +.el-divider--horizontal { + margin-bottom: 10px; + margin-top: 10px; +} + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type='file'] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0px; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +/*-------------Dialog-------------**/ +.el-overlay { + overflow: hidden; + + .el-overlay-dialog { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + + .el-dialog { + margin: 0 auto !important; + + .el-dialog__body { + padding: 15px !important; + } + .el-dialog__header { + padding: 16px 16px 8px 16px; + box-sizing: border-box; + border-bottom: 1px solid var(--brder-color); + margin-right: 0; + } + } + } +} + +.el-dialog__body { + max-height: calc(90vh - 111px) !important; + overflow-y: auto; + overflow-x: hidden; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block; + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} + +// to fix el-date-picker css style +.el-range-separator { + box-sizing: content-box; +} + +.el-menu--collapse > div > .el-submenu > .el-submenu__title .el-submenu__icon-arrow { + display: none; +} + +.el-dropdown .el-dropdown-link { + color: var(--el-color-primary) !important; +} + +/* 当 el-form 的 inline 属性为 true 时 */ +/* 设置 label 的宽度默认为 68px */ +.el-form--inline .el-form-item__label { + width: 68px; +} + +/* 设置 el-select 的宽度默认为 240px */ +.el-form--inline .el-select { + width: 240px; +} + +/* 设置 el-input 的宽度默认为 240px */ +.el-form--inline .el-input { + width: 240px; +} diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss new file mode 100644 index 0000000..bdf9b03 --- /dev/null +++ b/src/assets/styles/index.scss @@ -0,0 +1,215 @@ +@import './variables.module.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; +@import './btn.scss'; +@import './ruoyi.scss'; +@import 'animate.css'; +@import 'element-plus/dist/index.css'; + +body { + height: 100%; + margin: 0; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: + Helvetica Neue, + Helvetica, + PingFang SC, + Hiragino Sans GB, + Microsoft YaHei, + Arial, + sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +html.dark .svg-icon, +html.dark svg { + fill: var(--el-text-color-regular); +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0px !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: ' '; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container全局样式 +.app-container { + padding: 20px; +} + +// search面板样式 +.panel, +.search { + margin-bottom: 0.75rem; + border-radius: 0.25rem; + border: 1px solid var(--el-border-color-light); + background-color: var(--el-bg-color-overlay); + padding: 0.75rem; + transition: all ease 0.3s; + + &:hover { + box-shadow: 0 2px 12px #0000001a; + transition: all ease 0.3s; + } +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.pagination-container { + margin-top: 30px; +} + +.text-center { + text-align: center; +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +.filter-container { + padding-bottom: 10px; + + .filter-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + } +} diff --git a/src/assets/styles/mixin.scss b/src/assets/styles/mixin.scss new file mode 100644 index 0000000..5250e71 --- /dev/null +++ b/src/assets/styles/mixin.scss @@ -0,0 +1,60 @@ +@mixin clearfix { + &:after { + content: ''; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/src/assets/styles/ruoyi.scss b/src/assets/styles/ruoyi.scss new file mode 100644 index 0000000..d2ecb24 --- /dev/null +++ b/src/assets/styles/ruoyi.scss @@ -0,0 +1,290 @@ +/** + * 通用css样式布局处理 + * Copyright (c) 2019 ruoyi + */ + +/** 基础通用 **/ +.pt5 { + padding-top: 5px; +} +.pr5 { + padding-right: 5px; +} +.pb5 { + padding-bottom: 5px; +} +.mt5 { + margin-top: 5px; +} +.mr5 { + margin-right: 5px; +} +.mb5 { + margin-bottom: 5px; +} +.mb8 { + margin-bottom: 8px; +} +.ml5 { + margin-left: 5px; +} +.mt10 { + margin-top: 10px; +} +.mr10 { + margin-right: 10px; +} +.mb10 { + margin-bottom: 10px; +} +.ml10 { + margin-left: 10px; +} +.mt20 { + margin-top: 20px; +} +.mr20 { + margin-right: 20px; +} +.mb20 { + margin-bottom: 20px; +} +.ml20 { + margin-left: 20px; +} + +.h1, +.h2, +.h3, +.h4, +.h5, +.h6, +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.el-form .el-form-item__label { + font-weight: 700; +} +.el-dialog:not(.is-fullscreen) { + margin-top: 6vh !important; +} + +.el-dialog.scrollbar .el-dialog__body { + overflow: auto; + overflow-x: hidden; + max-height: 70vh; + padding: 10px 20px 0; +} + +.el-table { + .el-table__header-wrapper, + .el-table__fixed-header-wrapper { + th { + word-break: break-word; + background-color: $table-header-bg !important; + color: $table-header-text-color; + height: 40px !important; + font-size: 13px; + } + } + .el-table__body-wrapper { + .el-button [class*='el-icon-'] + span { + margin-left: 1px; + } + } +} + +/** 表单布局 **/ +.form-header { + font-size: 15px; + color: #6379bb; + border-bottom: 1px solid #ddd; + margin: 8px 10px 25px 10px; + padding-bottom: 5px; +} + +/** 表格布局 **/ +.pagination-container { + // position: relative; + height: 25px; + margin-bottom: 10px; + margin-top: 15px; + padding: 10px 20px !important; +} + +/* tree border */ +.tree-border { + margin-top: 5px; + border: 1px solid #e5e6e7; + background: #ffffff none; + border-radius: 4px; + width: 100%; +} + +.pagination-container .el-pagination { + //right: 0; + //position: absolute; +} + +@media (max-width: 768px) { + .pagination-container .el-pagination > .el-pagination__jump { + display: none !important; + } + .pagination-container .el-pagination > .el-pagination__sizes { + display: none !important; + } +} + +.el-table .fixed-width .el-button--small { + padding-left: 0; + padding-right: 0; + width: inherit; +} + +/** 表格更多操作下拉样式 */ +.el-table .el-dropdown-link { + cursor: pointer; + color: #409eff; + margin-left: 10px; +} + +.el-table .el-dropdown, +.el-icon-arrow-down { + font-size: 12px; +} + +.el-tree-node__content > .el-checkbox { + margin-right: 8px; +} + +.list-group-striped > .list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; +} + +.list-group { + padding-left: 0px; + list-style: none; +} + +.list-group-item { + border-bottom: 1px solid #e7eaec; + border-top: 1px solid #e7eaec; + margin-bottom: -1px; + padding: 11px 0px; + font-size: 13px; +} + +.pull-right { + float: right !important; +} + +.el-card__header { + padding: 14px 15px 7px !important; + min-height: 40px; +} + +.el-card__body { + padding: 15px 20px 20px 20px !important; +} + +.card-box { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 10px; +} + +/* button color */ +.el-button--cyan.is-active, +.el-button--cyan:active { + background: #20b2aa; + border-color: #20b2aa; + color: #ffffff; +} + +.el-button--cyan:focus, +.el-button--cyan:hover { + background: #48d1cc; + border-color: #48d1cc; + color: #ffffff; +} + +.el-button--cyan { + background-color: #20b2aa; + border-color: #20b2aa; + color: #ffffff; +} + +/* text color */ +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} + +/* image */ +.img-circle { + border-radius: 50%; +} + +.img-lg { + width: 120px; + height: 120px; +} + +.avatar-upload-preview { + position: absolute; + top: 50%; + transform: translate(50%, -50%); + width: 200px; + height: 200px; + border-radius: 50%; + box-shadow: 0 0 4px #ccc; + overflow: hidden; +} + +/* 拖拽列样式 */ +.sortable-ghost { + opacity: 0.8; + color: #fff !important; + background: #42b983 !important; +} + +/* 表格右侧工具栏样式 */ +.top-right-btn { + margin-left: auto; +} diff --git a/src/assets/styles/sidebar.scss b/src/assets/styles/sidebar.scss new file mode 100644 index 0000000..d85da55 --- /dev/null +++ b/src/assets/styles/sidebar.scss @@ -0,0 +1,232 @@ +#app { + .main-container { + height: 100%; + transition: margin-left 0.28s; + margin-left: $base-sidebar-width; + position: relative; + } + + .sidebarHide { + margin-left: 0 !important; + } + + .sidebar-container { + -webkit-transition: width 0.28s; + transition: width 0.28s; + width: $base-sidebar-width !important; + background-color: $base-menu-background; + height: 100%; + position: fixed; + font-size: 0; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + -webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35); + box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35); + + // reset element-ui css + .horizontal-collapse-transition { + transition: + 0s width ease-in-out, + 0s padding-left ease-in-out, + 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + .el-menu-item, + .menu-title { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + } + + .el-menu-item .el-menu-tooltip__trigger { + display: inline-block !important; + } + + // menu hover + .theme-dark .sub-menu-title-noDropdown, + .theme-dark .el-sub-menu__title { + &:hover { + background-color: $base-sub-menu-title-hover !important; + } + } + .sub-menu-title-noDropdown, + .el-sub-menu__title { + &:hover { + background-color: rgba(0, 0, 0, 0.05) !important; + } + } + + & .theme-dark .is-active > .el-sub-menu__title { + color: $base-menu-color-active !important; + } + + & .nest-menu .el-sub-menu > .el-sub-menu__title, + & .el-sub-menu .el-menu-item { + min-width: $base-sidebar-width !important; + &:hover { + background-color: rgba(0, 0, 0, 0.1) !important; + } + } + + & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title, + & .theme-dark .el-sub-menu .el-menu-item { + background-color: $base-sub-menu-background !important; + + &:hover { + background-color: $base-sub-menu-hover !important; + } + } + + & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title, + & .theme-dark .el-menu-item { + &:hover { + // you can use $sub-menuHover + background-color: $base-menu-hover !important; + } + } + & .nest-menu .el-sub-menu > .el-sub-menu__title, + & .el-menu-item { + &:hover { + // you can use $sub-menuHover + background-color: rgba(0, 0, 0, 0.04) !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .sub-menu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-sub-menu { + overflow: hidden; + + & > .el-sub-menu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-menu--collapse { + .el-sub-menu { + & > .el-sub-menu__title { + & > span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + & > i { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-sub-menu { + min-width: $base-sidebar-width !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform 0.28s; + width: $base-sidebar-width !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$base-sidebar-width, 0, 0); + } + } + } + + .withoutAnimation { + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + & > .el-menu { + .svg-icon { + margin-right: 16px; + } + } +} diff --git a/src/assets/styles/transition.scss b/src/assets/styles/transition.scss new file mode 100644 index 0000000..468ad3c --- /dev/null +++ b/src/assets/styles/transition.scss @@ -0,0 +1,49 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform--move, +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all 0.5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all 0.5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all 0.5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/src/assets/styles/variables.module.scss b/src/assets/styles/variables.module.scss new file mode 100644 index 0000000..92fc34d --- /dev/null +++ b/src/assets/styles/variables.module.scss @@ -0,0 +1,121 @@ +// 全局SCSS变量 +:root { + --menuBg: #304156; + --menuColor: #bfcbd9; + --menuActiveText: #f4f4f5; + --menuHover: #263445; + + --subMenuBg: #1f2d3d; + --subMenuActiveText: #f4f4f5; + --subMenuHover: #001528; + --subMenuTitleHover: #293444; + + --fixedHeaderBg: #ffffff; + --tableHeaderBg: #f8f8f9; + --tableHeaderTextColor: #515a6e; + + // 工作流 + --bpmn-panel-border: #eeeeee; + --bpmn-panel-box-shadow: #cccccc; + --bpmn-panel-bar-background-color: #f5f7fa; + + // ele + --brder-color: #e8e8e8 +} +html.dark { + --menuBg: #1d1e1f; + --menuColor: #bfcbd9; + --menuActiveText: #f4f4f5; + --menuHover: #171819; + + --subMenuBg: #1d1e1f; + --subMenuActiveText: #1d1e1f; + --subMenuHover: #171819; + --subMenuTitleHover: #171819; + + --fixedHeaderBg: #171819; + --tableHeaderBg: var(--el-bg-color); + --tableHeaderTextColor: var(--el-text-color); + + // 覆盖ele 高亮当前行的标准暗色 + .el-tree-node__content { + --el-color-primary-light-9: #262727; + } + // vxe-table 主题 + --vxe-font-color: #98989E; + --vxe-primary-color: #2C7ECF; + --vxe-icon-background-color: #98989E; + --vxe-table-font-color: #98989E; + --vxe-table-resizable-color: #95969a; + --vxe-table-header-background-color: #28282A; + --vxe-table-body-background-color: #151518; + --vxe-table-background-color: #4a5663; + --vxe-table-border-width: 1px; + --vxe-table-border-color: #37373A; + --vxe-toolbar-background-color: #37373A; + + // 工作流 + --bpmn-panel-border: #37373A; + --bpmn-panel-box-shadow: #37373A; + --bpmn-panel-bar-background-color: #37373A; + + // ele + --brder-color: #37373A +} + +// base color +$blue: #324157; +$light-blue: #3a71a8; +$red: #c03639; +$pink: #e65d6e; +$green: #30b08f; +$tiffany: #4ab7bd; +$yellow: #fec171; +$panGreen: #30b08f; + +// 默认菜单主题风格 +$base-menu-color: var(--menuColor); +$base-menu-hover: var(--menuHover); +$base-menu-color-active: var(--menuActiveText); +$base-menu-background: var(--menuBg); +$base-logo-title-color: #ffffff; + +$base-menu-light-color: rgba(0, 0, 0, 0.7); +$base-menu-light-background: #ffffff; +$base-logo-light-title-color: #001529; + +$base-sub-menu-background: var(--subMenuBg); +$base-sub-menu-hover: var(--subMenuHover); +$base-sub-menu-title-hover: var(--subMenuTitleHover); +// 表单头背景色和标题颜色 +$fixed-header-bg: var(--fixedHeaderBg); +$table-header-bg: var(--tableHeaderBg); +$table-header-text-color: var(--tableHeaderTextColor); + +$--color-primary: #409eff; +$--color-success: #67c23a; +$--color-warning: #e6a23c; +$--color-danger: #f56c6c; +$--color-info: #909399; + +$base-sidebar-width: 200px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuColor: $base-menu-color; + menuLightColor: $base-menu-light-color; + menuColorActive: $base-menu-color-active; + menuBackground: $base-menu-background; + menuLightBackground: $base-menu-light-background; + subMenuBackground: $base-sub-menu-background; + subMenuHover: $base-sub-menu-hover; + sideBarWidth: $base-sidebar-width; + logoTitleColor: $base-logo-title-color; + logoLightTitleColor: $base-logo-light-title-color; + primaryColor: $--color-primary; + successColor: $--color-success; + dangerColor: $--color-danger; + infoColor: $--color-info; + warningColor: $--color-warning; +} diff --git a/src/bpmn/assets/defaultXML.ts b/src/bpmn/assets/defaultXML.ts new file mode 100644 index 0000000..dff0349 --- /dev/null +++ b/src/bpmn/assets/defaultXML.ts @@ -0,0 +1,23 @@ +function generateRandomValue() { + // 生成一个随机数 + const randomValue = Math.random().toString(36).slice(2, 12); + return `Process_${randomValue}`; +} + +const cartage: string = 'default'; +export default ` + + + + + + + + + + + + + + +`; diff --git a/src/bpmn/assets/lang/zh.ts b/src/bpmn/assets/lang/zh.ts new file mode 100644 index 0000000..ee0c5de --- /dev/null +++ b/src/bpmn/assets/lang/zh.ts @@ -0,0 +1,126 @@ +export const NodeName = { + 'bpmn:Process': '流程', + 'bpmn:StartEvent': '开始事件', + 'bpmn:IntermediateThrowEvent': '中间事件', + 'bpmn:Task': '任务', + 'bpmn:SendTask': '发送任务', + 'bpmn:ReceiveTask': '接收任务', + 'bpmn:UserTask': '用户任务', + 'bpmn:ManualTask': '手工任务', + 'bpmn:BusinessRuleTask': '业务规则任务', + 'bpmn:ServiceTask': '服务任务', + 'bpmn:ScriptTask': '脚本任务', + 'bpmn:EndEvent': '结束事件', + 'bpmn:SequenceFlow': '流程线', + 'bpmn:ExclusiveGateway': '互斥网关', + 'bpmn:ParallelGateway': '并行网关', + 'bpmn:InclusiveGateway': '相容网关', + 'bpmn:ComplexGateway': '复杂网关', + 'bpmn:EventBasedGateway': '事件网关', + 'bpmn:Participant': '池/参与者', + 'bpmn:SubProcess': '子流程', + 'bpmn:DataObjectReference': '数据对象引用', + 'bpmn:DataStoreReference': '数据存储引用', + 'bpmn:Group': '组' +}; + +export default { + 'Activate hand tool': '启动手动工具', + 'Activate lasso tool': '启动 Lasso 工具', + 'Activate create/remove space tool': '启动创建/删除空间工具', + 'Activate global connect tool': '启动全局连接工具', + 'Ad-hoc': 'Ad-hoc', + 'Add lane above': '在上方添加泳道', + 'Add lane below': '在下方添加泳道', + 'Business rule task': '规则任务', + 'Call activity': '引用流程', + 'Compensation end event': '结束补偿事件', + 'Compensation intermediate throw event': '中间补偿抛出事件', + 'Complex gateway': '复杂网关', + 'Conditional intermediate catch event': '中间条件捕获事件', + 'Conditional start event (non-interrupting)': '条件启动事件 (非中断)', + 'Conditional start event': '条件启动事件', + 'Connect using association': '文本关联', + 'Connect using sequence/message flow or association': '消息关联', + 'Change element': '更改元素', + 'Change type': '更改类型', + 'Create data object reference': '创建数据对象引用', + 'Create data store reference': '创建数据存储引用', + 'Create expanded sub-process': '创建可折叠子流程', + 'Create pool/participant': '创建池/参与者', + 'Collection': '集合', + 'Connect using data input association': '数据输入关联', + 'Data store reference': '数据存储引用', + 'Data object reference': '数据对象引用', + 'Divide into two lanes': '分成两个泳道', + 'Divide into three lanes': '分成三个泳道', + 'End event': '结束事件', + 'Error end event': '结束错误事件', + 'Escalation end event': '结束升级事件', + 'Escalation intermediate throw event': '中间升级抛出事件', + 'Event sub-process': '事件子流程', + 'Event-based gateway': '事件网关', + 'Exclusive gateway': '互斥网关', + 'Empty pool/participant (removes content)': '清空池/参与者 (删除内容)', + 'Empty pool/participant': '清空池/参与者', + 'Expanded pool/participant': '展开池/参与者', + 'Inclusive gateway': '相容网关', + 'Intermediate throw event': '中间抛出事件', + 'Loop': '循环', + 'Link intermediate catch event': '中间链接捕获事件', + 'Link intermediate throw event': '中间链接抛出事件', + 'Manual task': '手动任务', + 'Message end event': '结束消息事件', + 'Message intermediate catch event': '中间消息捕获事件', + 'Message intermediate throw event': '中间消息抛出事件', + 'Message start event': '消息启动事件', + 'Parallel gateway': '并行网关', + 'Parallel multi-instance': '并行多实例', + 'Participant multiplicity': '参与者多重性', + 'Receive task': '接受任务', + 'Remove': '移除', + 'Script task': '脚本任务', + 'Send task': '发送任务', + 'Sequential multi-instance': '串行多实例', + 'Service task': '服务任务', + 'Signal end event': '结束信号事件', + 'Signal intermediate catch event': '中间信号捕获事件', + 'Signal intermediate throw event': '中间信号抛出事件', + 'Signal start event (non-interrupting)': '信号启动事件 (非中断)', + 'Signal start event': '信号启动事件', + 'Start event': '开始事件', + 'Sub-process (collapsed)': '可折叠子流程', + 'Sub-process (expanded)': '可展开子流程', + 'Sub rocess': '子流程', + 'Task': '任务', + 'Transaction': '事务', + 'Terminate end event': '终止边界事件', + 'Timer intermediate catch event': '中间定时捕获事件', + 'Timer start event (non-interrupting)': '定时启动事件 (非中断)', + 'Timer start event': '定时启动事件', + 'User task': '用户任务', + 'Create start event': '创建开始事件', + 'Create gateway': '创建网关', + 'Create intermediate/boundary event': '创建中间/边界事件', + 'Create end event': '创建结束事件', + 'Create group': '创建组', + 'Create startEvent': '开始节点', + 'Create endEvent': '结束节点', + 'Create exclusiveGateway': '互斥网关', + 'Create parallelGateway': '并行网关', + 'Create task': '任务节点', + 'Create userTask': '用户任务节点', + 'Condition type': '条件类型', + 'Append end event': '追加结束事件节点', + 'Append gateway': '追加网关节点', + 'Append task': '追加任务', + 'Append user task': '追加用户任务节点', + 'Append text annotation': '追加文本注释', + 'Append intermediate/boundary event': '追加中间或边界事件', + 'Append receive task': '追加接收任务节点', + 'Append message intermediate catch event': '追加中间消息捕获事件', + 'Append timer intermediate catch event': '追加中间定时捕获事件', + 'Append conditional intermediate catch event': '追加中间条件捕获事件', + 'Append signal intermediate catch event': '追加中间信号捕获事件', + 'flow elements must be children of pools/participants': '流程元素必须是池/参与者的子元素' +}; diff --git a/src/bpmn/assets/moddle/flowable.ts b/src/bpmn/assets/moddle/flowable.ts new file mode 100644 index 0000000..de959a6 --- /dev/null +++ b/src/bpmn/assets/moddle/flowable.ts @@ -0,0 +1,1250 @@ +export default { + 'name': 'Flowable', + 'uri': 'http://flowable.org/bpmn', + 'prefix': 'flowable', + 'xml': { + 'tagAlias': 'lowerCase' + }, + 'associations': [], + 'types': [ + { + 'name': 'flowable:extCandidateUsers', + 'isAbstract': true, + 'extends': [], + 'superClass': ['Element'], + 'meta': { + 'allowedIn': ['*'] + }, + 'properties': [ + { + 'name': 'body', + 'type': 'String', + 'isBody': true + } + ] + }, + { + 'name': 'flowable:extAssignee', + 'isAbstract': true, + 'extends': [], + 'superClass': ['Element'], + 'meta': { + 'allowedIn': ['*'] + }, + 'properties': [ + { + 'name': 'body', + 'type': 'String', + 'isBody': true + } + ] + }, + { + 'name': 'flowable:property', + 'superClass': ['Element'], + 'properties': [ + { + 'name': 'id', + 'isAttr': true, + 'type': 'String' + }, + { + 'name': 'name', + 'isAttr': true, + 'type': 'String' + }, + { + 'name': 'value', + 'isAttr': true, + 'type': 'String' + } + ] + }, + { + 'name': 'flowable:properties', + 'isAbstract': true, + 'extends': [], + 'superClass': ['Element'], + 'meta': { + 'allowedIn': ['*'] + }, + 'properties': [ + { + 'name': 'values', + 'type': 'flowable:property', + 'isMany': true + } + ] + }, + { + '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': 'flowable:in', + 'superClass': ['Element'], + 'properties': [ + { + 'name': 'source', + 'type': 'string', + 'isAttr': true + }, + { + 'name': 'target', + 'type': 'string', + 'isAttr': true + } + ] + }, + { + 'name': 'flowable:out', + 'superClass': ['Element'], + 'properties': [ + { + 'name': 'source', + 'type': 'string', + 'isAttr': true + }, + { + 'name': 'target', + 'type': 'string', + 'isAttr': true + } + ] + }, + { + 'name': 'BoundaryEvent', + 'superClass': ['CatchEvent'], + 'properties': [ + { + 'name': 'cancelActivity', + 'default': true, + 'isAttr': true, + 'type': 'Boolean' + }, + { + 'name': 'attachedToRef', + 'type': 'Activity', + 'isAttr': true, + 'isReference': 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': 'UserTask', + 'isAbstract': true, + 'extends': ['bpmn:UserTask'], + 'properties': [ + { + 'name': 'timerEventDefinition', + 'type': 'Expression' + }, + { + 'name': 'multiInstanceLoopCharacteristics', + 'type': 'MultiInstanceLoopCharacteristics' + } + ] + }, + { + 'name': 'StartEvent', + 'isAbstract': true, + 'extends': ['bpmn:StartEvent'], + 'properties': [ + { + 'name': 'timerEventDefinition', + 'type': 'Expression' + } + ] + }, + { + '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': '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': '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': 'ExclusiveGateway', + 'isAbstract': true, + 'extends': ['bpmn:ExclusiveGateway'], + 'properties': [ + { + 'name': 'serviceClass', + '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': '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', + 'isAttr': true, + 'type': 'expression' + }, + { + 'name': 'string', + 'type': 'string' + }, + { + 'name': 'stringValue', + 'isAttr': true, + 'type': 'String' + } + ] + }, + { + 'name': 'string', + 'superClass': ['Element'], + 'meta': { + 'allowedIn': ['flowable:Field'] + }, + 'properties': [ + { + 'name': 'body', + 'isBody': true, + 'type': 'String' + } + ] + }, + { + 'name': 'expression', + 'superClass': ['Element'], + 'meta': { + 'allowedIn': ['flowable:Field'] + }, + 'properties': [ + { + 'name': 'body', + 'isBody': true, + 'type': 'String' + } + ] + }, + { + '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': 'SequenceFlow', + 'superClass': ['FlowElement'], + 'properties': [ + { + 'name': 'isImmediate', + 'isAttr': true, + 'type': 'Boolean' + }, + { + 'name': 'conditionExpression', + 'type': 'Expression' + }, + { + 'name': 'sourceRef', + 'type': 'FlowNode', + 'isAttr': true, + 'isReference': true + }, + { + 'name': 'targetRef', + 'type': 'FlowNode', + 'isAttr': true, + 'isReference': true + } + ] + }, + { + 'name': 'MultiInstanceLoopCharacteristics', + 'superClass': ['LoopCharacteristics'], + 'properties': [ + { + 'name': 'isSequential', + 'default': false, + 'isAttr': true, + 'type': 'Boolean' + }, + { + 'name': 'behavior', + 'type': 'MultiInstanceBehavior', + 'default': 'All', + 'isAttr': true + }, + { + 'name': 'loopCardinality', + 'type': 'Expression', + 'xml': { + 'serialize': 'xsi:type' + } + }, + { + 'name': 'loopDataInputRef', + 'type': 'ItemAwareElement', + 'isReference': true + }, + { + 'name': 'loopDataOutputRef', + 'type': 'ItemAwareElement', + 'isReference': true + }, + { + 'name': 'inputDataItem', + 'type': 'DataInput', + 'xml': { + 'serialize': 'property' + } + }, + { + 'name': 'outputDataItem', + 'type': 'DataOutput', + 'xml': { + 'serialize': 'property' + } + }, + { + 'name': 'complexBehaviorDefinition', + 'type': 'ComplexBehaviorDefinition', + 'isMany': true + }, + { + 'name': 'completionCondition', + 'type': 'Expression', + 'xml': { + 'serialize': 'xsi:type' + } + }, + { + 'name': 'oneBehaviorEventRef', + 'type': 'EventDefinition', + 'isAttr': true, + 'isReference': true + }, + { + 'name': 'noneBehaviorEventRef', + 'type': 'EventDefinition', + 'isAttr': true, + 'isReference': true + } + ] + }, + { + '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': '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' + } + ] + } + ], + 'emumerations': [] +}; diff --git a/src/bpmn/assets/module/ContextPad/CustomContextPadProvider.ts b/src/bpmn/assets/module/ContextPad/CustomContextPadProvider.ts new file mode 100644 index 0000000..9f12ff2 --- /dev/null +++ b/src/bpmn/assets/module/ContextPad/CustomContextPadProvider.ts @@ -0,0 +1,138 @@ +import ContextPadProvider from 'bpmn-js/lib/features/context-pad/ContextPadProvider'; +import { Injector } from 'didi'; +import EventBus from 'diagram-js/lib/core/EventBus'; +import ContextPad from 'diagram-js/lib/features/context-pad/ContextPad'; +import Modeling from 'bpmn-js/lib/features/modeling/Modeling.js'; +import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory'; +import Connect from 'diagram-js/lib/features/connect/Connect'; +import Create from 'diagram-js/lib/features/create/Create'; +import PopupMenu from 'diagram-js/lib/features/popup-menu/PopupMenu'; +import Canvas from 'diagram-js/lib/core/Canvas'; +import Rules from 'diagram-js/lib/features/rules/Rules'; +import { Element, Shape } from 'diagram-js/lib/model/Types'; +import BpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory'; +import modeler from '@/store/modules/modeler'; + +// @Description: 增强元素连线事件 + +class CustomContextPadProvider extends ContextPadProvider { + private _contextPad: ContextPad; + private _modeling: Modeling; + private _elementFactory: ElementFactory; + private _autoPlace: any; + private _connect: Connect; + private _create: Create; + private _popupMenu: PopupMenu; + private _canvas: Canvas; + private _rules: Rules; + + constructor( + config: any, + injector: Injector, + eventBus: EventBus, + contextPad: ContextPad, + modeling: Modeling, + elementFactory: ElementFactory, + connect: Connect, + create: Create, + popupMenu: PopupMenu, + canvas: Canvas, + rules: Rules, + translate + ) { + // @ts-ignore + super(config, injector, eventBus, contextPad, modeling, elementFactory, connect, create, popupMenu, canvas, rules, translate); + + 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._autoPlace = injector.get('autoPlace', false); + } + + getContextPadEntries(element: Element) { + const actions: Record = {}; + + const appendUserTask = (event: Event, element: Shape) => { + const shape = this._elementFactory.createShape({ type: 'bpmn:UserTask' }); + this._create.start(event, shape, { + source: element + }); + }; + + const appendMultiInstanceUserTask = (event: Event, element: Shape) => { + const store = modeler(); + const bpmnFactory = store.getModeler().get('bpmnFactory') as BpmnFactory; + const businessObject = bpmnFactory.create('bpmn:UserTask', { + // name: '多实例用户任务', + isForCompensation: false + }); + businessObject.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics'); + // 创建 Shape + const shape = this._elementFactory.createShape({ + type: 'bpmn:UserTask', + businessObject: businessObject + }); + this._create.start(event, shape, { source: element }); + }; + + const appendTask = this._autoPlace + ? (event, element) => { + const bpmnFactory: BpmnFactory | undefined = modeler().getModeler().get('bpmnFactory'); + const businessObject = bpmnFactory.create('bpmn:UserTask', { + // name: '多实例用户任务',// 右键创建显示 + isForCompensation: false + }); + + // 创建多实例属性并分配给用户任务的 loopCharacteristics + businessObject.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics'); + + // 创建 Shape + const shape = this._elementFactory.createShape({ + type: 'bpmn:UserTask', + businessObject: businessObject + }); + + this._autoPlace.append(element, shape); + } + : appendMultiInstanceUserTask; + + const append = this._autoPlace + ? (event: Event, element: Shape) => { + const shape = this._elementFactory.createShape({ type: 'bpmn:UserTask' }); + this._autoPlace.append(element, shape); + } + : appendUserTask; + + // // 添加创建用户任务按钮 + actions['append.append-user-task'] = { + group: 'model', + className: 'bpmn-icon-user-task', + title: '用户任务', + action: { + dragstart: appendUserTask, + click: append + } + }; + + // 添加创建多实例用户任务按钮 + actions['append.append-multi-instance-user-task'] = { + group: 'model', + className: 'bpmn-icon-user', // 你可以使用多实例用户任务的图标 bpmn-icon-user bpmn-icon-user-task + title: '多实例用户任务', + action: { + dragstart: appendMultiInstanceUserTask, + click: appendTask + } + }; + + return actions; + } +} + +export default CustomContextPadProvider; diff --git a/src/bpmn/assets/module/Palette/CustomPaletteProvider.ts b/src/bpmn/assets/module/Palette/CustomPaletteProvider.ts new file mode 100644 index 0000000..8556d9b --- /dev/null +++ b/src/bpmn/assets/module/Palette/CustomPaletteProvider.ts @@ -0,0 +1,109 @@ +import { assign } from 'min-dash'; +import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider'; +import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory'; +import Create from 'diagram-js/lib/features/create/Create'; +import SpaceTool from 'diagram-js/lib/features/space-tool/SpaceTool'; +import LassoTool from 'diagram-js/lib/features/lasso-tool/LassoTool'; +import HandTool from 'diagram-js/lib/features/hand-tool/HandTool'; +import GlobalConnect from 'diagram-js/lib/features/global-connect/GlobalConnect'; +import Palette from 'diagram-js/lib/features/palette/Palette'; +import modeler from '@/store/modules/modeler'; +import BpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory'; + +// @Description: 增强左侧面板 +class CustomPaletteProvider extends PaletteProvider { + private readonly _palette: Palette; + private readonly _create: Create; + private readonly _elementFactory: ElementFactory; + private readonly _spaceTool: SpaceTool; + private readonly _lassoTool: LassoTool; + private readonly _handTool: HandTool; + private readonly _globalConnect: GlobalConnect; + private readonly _translate: any; + + constructor(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate) { + super(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; + } + + getPaletteEntries() { + const actions = {}, + create = this._create, + elementFactory = this._elementFactory, + translate = this._translate; + + function createAction(type: string, group: string, className: string, title: string, options?: object) { + function createListener(event) { + const shape = elementFactory.createShape(assign({ type: type }, options)); + if (options) { + !shape.businessObject.di && (shape.businessObject.di = {}); + shape.businessObject.di.isExpanded = (options as { [key: string]: any }).isExpanded; + } + create.start(event, shape, null); + } + const shortType = type.replace(/^bpmn:/, ''); + return { + group: group, + className: className, + title: title || translate('Create {type}', { type: shortType }), + action: { + dragstart: createListener, + click: createListener + } + }; + } + + function createMultiInstanceUserTask(event) { + const bpmnFactory: BpmnFactory | undefined = modeler().getBpmnFactory(); + // 创建一个 bpmn:UserTask + const userTask = bpmnFactory.create('bpmn:UserTask', { + // name: '多实例用户任务', // 在画板中显示字段 + isForCompensation: false + }); + // 将多实例属性分配给 bpmn:UserTask 的 loopCharacteristics + userTask.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics'); + const customUserTask = elementFactory.createShape({ + type: 'bpmn:UserTask', + businessObject: userTask // 分配创建的 userTask 到 businessObject + }); + create.start(event, customUserTask, {}); + } + + assign(actions, { + 'create.parallel-gateway': createAction('bpmn:ParallelGateway', 'gateway', 'bpmn-icon-gateway-parallel', '并行网关'), + 'create.event-base-gateway': createAction('bpmn:EventBasedGateway', 'gateway', 'bpmn-icon-gateway-eventbased', '事件网关'), + // 分组线 + 'gateway-separator': { + group: 'gateway', + separator: true + }, + 'create.user-task': createAction('bpmn:UserTask', 'activity', 'bpmn-icon-user-task', '创建用户任务'), + 'create.multi-instance-user-task': { + group: 'activity', + type: 'bpmn:UserTask', + className: 'bpmn-icon-user task-multi-instance', + title: '创建多实例用户任务', + action: { + click: createMultiInstanceUserTask, + dragstart: createMultiInstanceUserTask + } + }, + 'task-separator': { + group: 'activity', + separator: true + } + }); + return actions; + } +} + +CustomPaletteProvider['$inject'] = ['palette', 'create', 'elementFactory', 'spaceTool', 'lassoTool', 'handTool', 'globalConnect', 'translate']; + +export default CustomPaletteProvider; diff --git a/src/bpmn/assets/module/Renderer/CustomRenderer.ts b/src/bpmn/assets/module/Renderer/CustomRenderer.ts new file mode 100644 index 0000000..6a4eb1a --- /dev/null +++ b/src/bpmn/assets/module/Renderer/CustomRenderer.ts @@ -0,0 +1,56 @@ +import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer'; +import { + append as svgAppend, + attr as svgAttr, + create as svgCreate, + select as svgSelect, + selectAll as svgSelectAll, + clone as svgClone, + clear as svgClear, + remove as svgRemove +} from 'tiny-svg'; + +const HIGH_PRIORITY = 1500; +export default class CustomRenderer extends BaseRenderer { + bpmnRenderer: BaseRenderer; + modeling: any; + constructor(eventBus, bpmnRenderer, modeling) { + super(eventBus, HIGH_PRIORITY); + this.bpmnRenderer = bpmnRenderer; + this.modeling = modeling; + } + canRender(element) { + // ignore labels + return !element.labelTarget; + } + + /** + * 自定义节点图形 + * @param {*} parentNode 当前元素的svgNode + * @param {*} element + * @returns + */ + drawShape(parentNode, element) { + const shape = this.bpmnRenderer.drawShape(parentNode, element); + const { type, width, height } = element; + // 开始 填充绿色 + if (type === 'bpmn:StartEvent') { + svgAttr(shape, { fill: '#77DF6D' }); + return shape; + } + if (type === 'bpmn:EndEvent') { + svgAttr(shape, { fill: '#EE7B77' }); + return shape; + } + if (type === 'bpmn:UserTask') { + svgAttr(shape, { fill: '#A9C4F8' }); + return shape; + } + return shape; + } + + getShapePath(shape) { + return this.bpmnRenderer.getShapePath(shape); + } +} +CustomRenderer['$inject'] = ['eventBus', 'bpmnRenderer']; diff --git a/src/bpmn/assets/module/Translate/index.ts b/src/bpmn/assets/module/Translate/index.ts new file mode 100644 index 0000000..6b52dae --- /dev/null +++ b/src/bpmn/assets/module/Translate/index.ts @@ -0,0 +1,15 @@ +import zh from '../../lang/zh'; + +const customTranslate = (template: any, replacements: any) => { + replacements = replacements || {}; + template = zh[template] || template; + return template.replace(/{([^}]+)}/g, function (_: any, key: any) { + return replacements[key] || '{' + key + '}'; + }); +}; + +export const translateModule = { + translate: ['value', customTranslate] +}; + +export default translateModule; diff --git a/src/bpmn/assets/module/index.ts b/src/bpmn/assets/module/index.ts new file mode 100644 index 0000000..55f6b9f --- /dev/null +++ b/src/bpmn/assets/module/index.ts @@ -0,0 +1,17 @@ +// 翻译模块 +import TranslationModule from './Translate'; +import { ModuleDeclaration } from 'didi'; +import CustomPaletteProvider from './Palette/CustomPaletteProvider'; +import CustomRenderer from './Renderer/CustomRenderer'; +import CustomContextPadProvider from './ContextPad/CustomContextPadProvider'; + +const Module: ModuleDeclaration[] = [ + { + __init__: ['customPaletteProvider', 'customContextPadProvider', 'customRenderer'], + customPaletteProvider: ['type', CustomPaletteProvider], + customRenderer: ['type', CustomRenderer], + customContextPadProvider: ['type', CustomContextPadProvider] + }, + TranslationModule +]; +export default Module; diff --git a/src/bpmn/assets/showConfig.ts b/src/bpmn/assets/showConfig.ts new file mode 100644 index 0000000..853eb35 --- /dev/null +++ b/src/bpmn/assets/showConfig.ts @@ -0,0 +1,50 @@ +export default { + 'bpmn:EndEvent': {}, + 'bpmn:StartEvent': { + initiator: true, + formKey: true + }, + 'bpmn:UserTask': { + allocationType: true, + specifyDesc: true, + multipleUserAuditType: true, + async: true, + priority: true, + skipExpression: true, + dueDate: true, + taskListener: true, + executionListener: true + }, + 'bpmn:ServiceTask': { + async: true, + skipExpression: true, + isForCompensation: true, + triggerable: true, + class: true + }, + 'bpmn:ScriptTask': { + async: true, + isForCompensation: true, + autoStoreVariables: true + }, + 'bpmn:ManualTask': { + async: true, + isForCompensation: true + }, + 'bpmn:ReceiveTask': { + async: true, + isForCompensation: true + }, + 'bpmn:SendTask': { + async: true, + isForCompensation: true + }, + 'bpmn:BusinessRuleTask': { + async: true, + isForCompensation: true, + ruleVariablesInput: true, + rules: true, + resultVariable: true, + exclude: true + } +}; diff --git a/src/bpmn/assets/style/index.scss b/src/bpmn/assets/style/index.scss new file mode 100644 index 0000000..3c92792 --- /dev/null +++ b/src/bpmn/assets/style/index.scss @@ -0,0 +1,284 @@ +.djs-palette { + width: 300px; + + .bpmn-icon-hand-tool:hover { + &:after { + content: '启动手动工具'; + position: absolute; + left: 45px; + width: 120px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-lasso-tool:hover { + &:after { + content: '启动套索工具'; + position: absolute; + left: 100px; + width: 120px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-space-tool:hover { + &:after { + content: '启动创建/删除空间工具'; + position: absolute; + left: 45px; + width: 170px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-connection-multi:hover { + &:after { + content: '启动全局连接工具'; + position: absolute; + left: 100px; + width: 140px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-start-event-none:hover { + &:after { + content: '创建开始事件'; + position: absolute; + left: 45px; + width: 120px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-intermediate-event-none:hover { + &:after { + content: '创建中间/边界事件'; + position: absolute; + left: 100px; + width: 140px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-end-event-none:hover { + &:after { + content: '创建结束事件'; + position: absolute; + left: 45px; + width: 120px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-gateway-none:hover { + &:after { + content: '创建网关'; + position: absolute; + left: 100px; + width: 90px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-gateway-parallel:hover { + &:after { + content: '创建并行网关'; + position: absolute; + left: 45px; + width: 120px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-gateway-eventbased:hover { + &:after { + content: '创建事件网关'; + position: absolute; + left: 100px; + width: 120px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-task:hover { + &:after { + content: '创建任务'; + position: absolute; + left: 45px; + width: 80px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-subprocess-expanded:hover { + &:after { + content: '创建可折叠子流程'; + position: absolute; + left: 100px; + width: 140px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-user-task:hover { + &:after { + content: '创建用户任务'; + position: absolute; + left: 45px; + width: 120px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + + .task-multi-instance:hover { + &:after { + content: '创建多实例用户任务'; + position: absolute; + left: 100px; + width: 160px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-participant:hover { + &:after { + content: '创建泳池/泳道'; + position: absolute; + left: 45px; + width: 120px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + .bpmn-icon-data-object { + display: none; + &:hover { + &:after { + content: '创建数据对象'; + position: absolute; + left: 45px; + width: 120px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + } + .bpmn-icon-data-store { + display: none; + &:hover { + &:after { + content: '创建数据存储'; + position: absolute; + left: 100px; + width: 120px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + } + .bpmn-icon-group { + display: none; + &:hover { + &:after { + content: '创建分组'; + position: absolute; + left: 100px; + width: 100px; + font-size: 15px; + font-weight: bold; + color: #3a84de; + border-radius: 2px; + border: 1px solid #cccccc; + background-color: #fafafa; + opacity: 0.8; + } + } + } +} diff --git a/src/bpmn/hooks/usePanel.ts b/src/bpmn/hooks/usePanel.ts new file mode 100644 index 0000000..d92b7d7 --- /dev/null +++ b/src/bpmn/hooks/usePanel.ts @@ -0,0 +1,145 @@ +import showConfig from '../assets/showConfig'; +import { ModdleElement } from 'bpmn'; +import useModelerStore from '@/store/modules/modeler'; +import { MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums'; +interface Options { + element: ModdleElement; +} + +export default (ops: Options) => { + const { element } = ops; + const { getModeling, getModdle } = useModelerStore(); + const modeling = getModeling(); + const moddle = getModdle(); + + /** + * 当前节点类型 + */ + const elementType = computed(() => { + const bizObj = element.businessObject; + return bizObj.eventDefinitions ? bizObj.eventDefinitions[0].$type : bizObj.$type; + }); + + /** + * 用于控制面板字段显示与隐藏的配置 + */ + const config = computed(() => showConfig[elementType.value] || {}); + + /** + * 创建一个节点 + * @param elementType 节点类型 + * @param properties 属性 + * @param parent 父节点 + */ + const createModdleElement = (elementType: string, properties: any, parent: ModdleElement) => { + const element = moddle.create(elementType, properties); + parent && (element.$parent = parent); + return element; + }; + + /** + * 获取扩展属性,如果不存在会自动创建 + */ + const getExtensionElements = (create = true) => { + let extensionElements = element.businessObject.get('extensionElements'); + if (!extensionElements && create) { + extensionElements = createModdleElement('bpmn:ExtensionElements', { values: [] }, element.businessObject); + modeling.updateModdleProperties(element, element.businessObject, { extensionElements }); + } + return extensionElements; + }; + + /** + * 获取extensionElements下的properties + * @param extensionElements 可选参数,默认获取当前Element下的extensionElements下的Properties + */ + const getPropertiesElements = (extensionElements?: ModdleElement) => { + if (!extensionElements) { + extensionElements = getExtensionElements(); + } + let propertiesElements = extensionElements.values.find((item) => item.$type === 'flowable:properties'); + if (!propertiesElements) { + propertiesElements = createModdleElement('flowable:properties', { values: [] }, extensionElements); + modeling.updateModdleProperties(element, extensionElements, { + values: [...extensionElements.get<[]>('values'), propertiesElements] + }); + } + return propertiesElements; + }; + + /** + * 更新节点属性 + * @param properties 属性值 + */ + const updateProperties = (properties: any) => { + modeling.updateProperties(element, properties); + }; + + /** + * 更新节点信息 + * @param updateElement 需要更新的节点 + * @param properties 属性 + */ + const updateModdleProperties = (updateElement, properties: any) => { + modeling.updateModdleProperties(element, updateElement, properties); + }; + + /** + * 更新Property属性 + * @param name key值 + * @param value 值 + */ + const updateProperty = (name: string, value: string) => { + const propertiesElements = getPropertiesElements(); + + let propertyElements = propertiesElements.values.find((item) => item.name === name); + if (!propertyElements) { + propertyElements = createModdleElement('flowable:property', { name: name, value: value }, propertiesElements); + modeling.updateModdleProperties(element, propertiesElements, { + values: [...propertiesElements.get('values'), propertyElements] + }); + } else { + propertyElements.name = name; + propertyElements.value = value; + } + return propertyElements; + }; + + const idChange = (newVal: string) => { + if (newVal) { + updateProperties({ id: newVal }); + } + }; + const nameChange = (newVal: string) => { + if (newVal) { + updateProperties({ name: newVal }); + } + }; + const formKeyChange = (newVal: string) => { + updateProperties({ formKey: newVal }); + }; + const constant = { + MultiInstanceType: [ + { id: '373d4b81-a0d1-4eb8-8685-0d2fb1b468e2', label: '无', value: MultiInstanceTypeEnum.NONE }, + { id: 'b5acea7c-b7e5-46b0-8778-390db091bdab', label: '串行', value: MultiInstanceTypeEnum.SERIAL }, + { id: 'b4f0c683-1ccc-43c4-8380-e1b998986caf', label: '并行', value: MultiInstanceTypeEnum.PARALLEL } + ] + }; + + return { + elementType, + constant, + showConfig: config, + + updateProperties, + updateProperty, + updateModdleProperties, + + createModdleElement, + idChange, + nameChange, + formKeyChange, + getExtensionElements, + getPropertiesElements + }; +}; diff --git a/src/bpmn/hooks/useParseElement.ts b/src/bpmn/hooks/useParseElement.ts new file mode 100644 index 0000000..a5a255d --- /dev/null +++ b/src/bpmn/hooks/useParseElement.ts @@ -0,0 +1,34 @@ +import { ModdleElement } from 'bpmn'; + +interface Options { + element: ModdleElement; +} + +interface Data { + id: string; +} + +export default (ops: Options) => { + const { element } = ops; + + const parseData = (): T => { + const result = { + ...element.businessObject, + ...element.businessObject.$attrs + }; + + // 移除flowable前缀,格式化数组 + for (const key in result) { + if (key.indexOf('flowable:') === 0) { + const newKey = key.replace('flowable:', ''); + result[newKey] = result[key]; + delete result[key]; + } + } + return { ...result } as T; + }; + + return { + parseData + }; +}; diff --git a/src/bpmn/index.vue b/src/bpmn/index.vue new file mode 100644 index 0000000..924a838 --- /dev/null +++ b/src/bpmn/index.vue @@ -0,0 +1,496 @@ + + + + + diff --git a/src/bpmn/panel/GatewayPanel.vue b/src/bpmn/panel/GatewayPanel.vue new file mode 100644 index 0000000..5931539 --- /dev/null +++ b/src/bpmn/panel/GatewayPanel.vue @@ -0,0 +1,68 @@ + + + + diff --git a/src/bpmn/panel/ParticipantPanel.vue b/src/bpmn/panel/ParticipantPanel.vue new file mode 100644 index 0000000..cca28bd --- /dev/null +++ b/src/bpmn/panel/ParticipantPanel.vue @@ -0,0 +1,68 @@ + + + + diff --git a/src/bpmn/panel/ProcessPanel.vue b/src/bpmn/panel/ProcessPanel.vue new file mode 100644 index 0000000..7d9a359 --- /dev/null +++ b/src/bpmn/panel/ProcessPanel.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/src/bpmn/panel/SequenceFlowPanel.vue b/src/bpmn/panel/SequenceFlowPanel.vue new file mode 100644 index 0000000..fd8ad3c --- /dev/null +++ b/src/bpmn/panel/SequenceFlowPanel.vue @@ -0,0 +1,95 @@ + + + + diff --git a/src/bpmn/panel/StartEndPanel.vue b/src/bpmn/panel/StartEndPanel.vue new file mode 100644 index 0000000..d43ed80 --- /dev/null +++ b/src/bpmn/panel/StartEndPanel.vue @@ -0,0 +1,67 @@ + + + + diff --git a/src/bpmn/panel/SubProcessPanel.vue b/src/bpmn/panel/SubProcessPanel.vue new file mode 100644 index 0000000..e0cedcb --- /dev/null +++ b/src/bpmn/panel/SubProcessPanel.vue @@ -0,0 +1,193 @@ + + + + diff --git a/src/bpmn/panel/TaskPanel.vue b/src/bpmn/panel/TaskPanel.vue new file mode 100644 index 0000000..bc037ee --- /dev/null +++ b/src/bpmn/panel/TaskPanel.vue @@ -0,0 +1,492 @@ + + + + diff --git a/src/bpmn/panel/index.vue b/src/bpmn/panel/index.vue new file mode 100644 index 0000000..eecb0cb --- /dev/null +++ b/src/bpmn/panel/index.vue @@ -0,0 +1,110 @@ + + + + diff --git a/src/bpmn/panel/property/DueDate.vue b/src/bpmn/panel/property/DueDate.vue new file mode 100644 index 0000000..882766b --- /dev/null +++ b/src/bpmn/panel/property/DueDate.vue @@ -0,0 +1,252 @@ + + + diff --git a/src/bpmn/panel/property/ExecutionListener.vue b/src/bpmn/panel/property/ExecutionListener.vue new file mode 100644 index 0000000..7620cf3 --- /dev/null +++ b/src/bpmn/panel/property/ExecutionListener.vue @@ -0,0 +1,308 @@ + + + + diff --git a/src/bpmn/panel/property/ListenerParam.vue b/src/bpmn/panel/property/ListenerParam.vue new file mode 100644 index 0000000..334249b --- /dev/null +++ b/src/bpmn/panel/property/ListenerParam.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/src/bpmn/panel/property/TaskListener.vue b/src/bpmn/panel/property/TaskListener.vue new file mode 100644 index 0000000..db774e4 --- /dev/null +++ b/src/bpmn/panel/property/TaskListener.vue @@ -0,0 +1,310 @@ + + + + diff --git a/src/components/BpmnDesign/index.vue b/src/components/BpmnDesign/index.vue new file mode 100644 index 0000000..1f84516 --- /dev/null +++ b/src/components/BpmnDesign/index.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/src/components/BpmnView/index.vue b/src/components/BpmnView/index.vue new file mode 100644 index 0000000..a79f084 --- /dev/null +++ b/src/components/BpmnView/index.vue @@ -0,0 +1,410 @@ + + + + + diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..9502e60 --- /dev/null +++ b/src/components/Breadcrumb/index.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/components/BuildCode/index.vue b/src/components/BuildCode/index.vue new file mode 100644 index 0000000..0b8e3f4 --- /dev/null +++ b/src/components/BuildCode/index.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/src/components/BuildCode/render.vue b/src/components/BuildCode/render.vue new file mode 100644 index 0000000..aeb9312 --- /dev/null +++ b/src/components/BuildCode/render.vue @@ -0,0 +1,57 @@ + + + + diff --git a/src/components/DictTag/index.vue b/src/components/DictTag/index.vue new file mode 100644 index 0000000..ad326c2 --- /dev/null +++ b/src/components/DictTag/index.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/src/components/Editor/index.vue b/src/components/Editor/index.vue new file mode 100644 index 0000000..fc30c85 --- /dev/null +++ b/src/components/Editor/index.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/src/components/FileUpload/index.vue b/src/components/FileUpload/index.vue new file mode 100644 index 0000000..aaaa45c --- /dev/null +++ b/src/components/FileUpload/index.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue new file mode 100644 index 0000000..f9f5e62 --- /dev/null +++ b/src/components/Hamburger/index.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/src/components/HeaderSearch/index.vue b/src/components/HeaderSearch/index.vue new file mode 100644 index 0000000..a785958 --- /dev/null +++ b/src/components/HeaderSearch/index.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/src/components/IconSelect/index.vue b/src/components/IconSelect/index.vue new file mode 100644 index 0000000..3bfde4d --- /dev/null +++ b/src/components/IconSelect/index.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/src/components/IconSelect/requireIcons.ts b/src/components/IconSelect/requireIcons.ts new file mode 100644 index 0000000..9f06d69 --- /dev/null +++ b/src/components/IconSelect/requireIcons.ts @@ -0,0 +1,7 @@ +const icons: string[] = []; +const modules = import.meta.glob('./../../assets/icons/svg/*.svg'); +for (const path in modules) { + const p = path.split('assets/icons/svg/')[1].split('.svg')[0]; + icons.push(p); +} +export default icons; diff --git a/src/components/ImagePreview/index.vue b/src/components/ImagePreview/index.vue new file mode 100644 index 0000000..349f729 --- /dev/null +++ b/src/components/ImagePreview/index.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/src/components/ImageUpload/index.vue b/src/components/ImageUpload/index.vue new file mode 100644 index 0000000..1c6dcd1 --- /dev/null +++ b/src/components/ImageUpload/index.vue @@ -0,0 +1,234 @@ + + + + + diff --git a/src/components/LangSelect/index.vue b/src/components/LangSelect/index.vue new file mode 100644 index 0000000..b5fafd3 --- /dev/null +++ b/src/components/LangSelect/index.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue new file mode 100644 index 0000000..ac02193 --- /dev/null +++ b/src/components/Pagination/index.vue @@ -0,0 +1,88 @@ + + + + + + + diff --git a/src/components/ParentView/index.vue b/src/components/ParentView/index.vue new file mode 100644 index 0000000..98240ae --- /dev/null +++ b/src/components/ParentView/index.vue @@ -0,0 +1,3 @@ + diff --git a/src/components/Process/approvalRecord.vue b/src/components/Process/approvalRecord.vue new file mode 100644 index 0000000..cbce87a --- /dev/null +++ b/src/components/Process/approvalRecord.vue @@ -0,0 +1,116 @@ + + + diff --git a/src/components/Process/multiInstanceUser.vue b/src/components/Process/multiInstanceUser.vue new file mode 100644 index 0000000..b2039b9 --- /dev/null +++ b/src/components/Process/multiInstanceUser.vue @@ -0,0 +1,368 @@ + + + diff --git a/src/components/Process/submitVerify.vue b/src/components/Process/submitVerify.vue new file mode 100644 index 0000000..d71b855 --- /dev/null +++ b/src/components/Process/submitVerify.vue @@ -0,0 +1,353 @@ + + + diff --git a/src/components/RightToolbar/index.vue b/src/components/RightToolbar/index.vue new file mode 100644 index 0000000..e8c9d65 --- /dev/null +++ b/src/components/RightToolbar/index.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/src/components/RoleSelect/index.vue b/src/components/RoleSelect/index.vue new file mode 100644 index 0000000..6ba2076 --- /dev/null +++ b/src/components/RoleSelect/index.vue @@ -0,0 +1,250 @@ + + + diff --git a/src/components/RuoYiDoc/index.vue b/src/components/RuoYiDoc/index.vue new file mode 100644 index 0000000..1e27d89 --- /dev/null +++ b/src/components/RuoYiDoc/index.vue @@ -0,0 +1,13 @@ + + + diff --git a/src/components/RuoYiGit/index.vue b/src/components/RuoYiGit/index.vue new file mode 100644 index 0000000..ad27808 --- /dev/null +++ b/src/components/RuoYiGit/index.vue @@ -0,0 +1,13 @@ + + + diff --git a/src/components/Screenfull/index.vue b/src/components/Screenfull/index.vue new file mode 100644 index 0000000..ce0b373 --- /dev/null +++ b/src/components/Screenfull/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/components/SizeSelect/index.vue b/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..abf72cc --- /dev/null +++ b/src/components/SizeSelect/index.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..05dfe87 --- /dev/null +++ b/src/components/SvgIcon/index.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/src/components/TopNav/index.vue b/src/components/TopNav/index.vue new file mode 100644 index 0000000..e93e005 --- /dev/null +++ b/src/components/TopNav/index.vue @@ -0,0 +1,200 @@ + + + + + diff --git a/src/components/TreeSelect/index.vue b/src/components/TreeSelect/index.vue new file mode 100644 index 0000000..7accd6b --- /dev/null +++ b/src/components/TreeSelect/index.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/src/components/UserSelect/index.vue b/src/components/UserSelect/index.vue new file mode 100644 index 0000000..f6e552e --- /dev/null +++ b/src/components/UserSelect/index.vue @@ -0,0 +1,314 @@ + + + + + diff --git a/src/components/iFrame/index.vue b/src/components/iFrame/index.vue new file mode 100644 index 0000000..98f2224 --- /dev/null +++ b/src/components/iFrame/index.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue new file mode 100644 index 0000000..fd4eb8c --- /dev/null +++ b/src/layout/components/Navbar.vue @@ -0,0 +1,298 @@ + + + + + diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..3082beb --- /dev/null +++ b/src/layout/components/Settings/index.vue @@ -0,0 +1,238 @@ + + + + + diff --git a/src/layout/components/Sidebar/Link.vue b/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..fd75f35 --- /dev/null +++ b/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..631c261 --- /dev/null +++ b/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/src/layout/components/Sidebar/SidebarItem.vue b/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..3720062 --- /dev/null +++ b/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,103 @@ + + + diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..a4f20d0 --- /dev/null +++ b/src/layout/components/Sidebar/index.vue @@ -0,0 +1,55 @@ + + + diff --git a/src/layout/components/SocialCallback/index.vue b/src/layout/components/SocialCallback/index.vue new file mode 100644 index 0000000..eac66bc --- /dev/null +++ b/src/layout/components/SocialCallback/index.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..3b30043 --- /dev/null +++ b/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..1e12ca4 --- /dev/null +++ b/src/layout/components/TagsView/index.vue @@ -0,0 +1,341 @@ + + + + + + + diff --git a/src/layout/components/TopBar/search.vue b/src/layout/components/TopBar/search.vue new file mode 100644 index 0000000..cf4b024 --- /dev/null +++ b/src/layout/components/TopBar/search.vue @@ -0,0 +1,158 @@ + + + + + diff --git a/src/layout/components/index.ts b/src/layout/components/index.ts new file mode 100644 index 0000000..47c83e1 --- /dev/null +++ b/src/layout/components/index.ts @@ -0,0 +1,4 @@ +export { default as AppMain } from './AppMain.vue'; +export { default as Navbar } from './Navbar.vue'; +export { default as Settings } from './Settings/index.vue'; +export { default as TagsView } from './TagsView/index.vue'; diff --git a/src/layout/components/notice/index.vue b/src/layout/components/notice/index.vue new file mode 100644 index 0000000..e43b2b8 --- /dev/null +++ b/src/layout/components/notice/index.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/src/layout/index.vue b/src/layout/index.vue new file mode 100644 index 0000000..29fb5ff --- /dev/null +++ b/src/layout/index.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..592de58 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,57 @@ +import { createApp } from 'vue'; +// global css +import 'virtual:uno.css'; +import '@/assets/styles/index.scss'; +import 'element-plus/theme-chalk/dark/css-vars.css'; + +// App、router、store +import App from './App.vue'; +import store from './store'; +import router from './router'; + +// 自定义指令 +import directive from './directive'; + +// 注册插件 +import plugins from './plugins/index'; // plugins + +// 高亮组件 +// import 'highlight.js/styles/a11y-light.css'; +import 'highlight.js/styles/atom-one-dark.css'; +import 'highlight.js/lib/common'; +import HighLight from '@highlightjs/vue-plugin'; + +// svg图标 +import 'virtual:svg-icons-register'; +import ElementIcons from '@/plugins/svgicon'; + +// permission control +import './permission'; + +// 国际化 +import i18n from '@/lang/index'; + +// vxeTable +import VXETable from 'vxe-table'; +import 'vxe-table/lib/style.css'; +VXETable.config({ + zIndex: 999999 +}); + +// 修改 el-dialog 默认点击遮照为不关闭 +import { ElDialog } from 'element-plus'; +ElDialog.props.closeOnClickModal.default = false; + +const app = createApp(App); + +app.use(HighLight); +app.use(ElementIcons); +app.use(router); +app.use(store); +app.use(i18n); +app.use(VXETable); +app.use(plugins); +// 自定义指令 +directive(app); + +app.mount('#app'); diff --git a/src/permission.ts b/src/permission.ts new file mode 100644 index 0000000..1123738 --- /dev/null +++ b/src/permission.ts @@ -0,0 +1,64 @@ +import { to as tos } from 'await-to-js'; +import router from './router'; +import NProgress from 'nprogress'; +import 'nprogress/nprogress.css'; +import { getToken } from '@/utils/auth'; +import { isHttp } from '@/utils/validate'; +import { isRelogin } from '@/utils/request'; +import useUserStore from '@/store/modules/user'; +import useSettingsStore from '@/store/modules/settings'; +import usePermissionStore from '@/store/modules/permission'; + +NProgress.configure({ showSpinner: false }); +const whiteList = ['/login', '/register', '/social-callback']; + +router.beforeEach(async (to, from, next) => { + NProgress.start(); + if (getToken()) { + to.meta.title && useSettingsStore().setTitle(to.meta.title); + /* has token*/ + if (to.path === '/login') { + next({ path: '/' }); + NProgress.done(); + } else if (whiteList.indexOf(to.path as string) !== -1) { + next(); + } else { + if (useUserStore().roles.length === 0) { + isRelogin.show = true; + // 判断当前用户是否已拉取完user_info信息 + const [err] = await tos(useUserStore().getInfo()); + if (err) { + await useUserStore().logout(); + ElMessage.error(err); + next({ path: '/' }); + } else { + isRelogin.show = false; + const accessRoutes = await usePermissionStore().generateRoutes(); + // 根据roles权限生成可访问的路由表 + accessRoutes.forEach((route) => { + if (!isHttp(route.path)) { + router.addRoute(route); // 动态添加可访问路由表 + } + }); + next({ path: to.path, replace: true, params: to.params, query: to.query, hash: to.hash, name: to.name as string }); // hack方法 确保addRoutes已完成 + } + } else { + next(); + } + } + } else { + // 没有token + if (whiteList.indexOf(to.path as string) !== -1) { + // 在免登录白名单,直接进入 + next(); + } else { + const redirect = encodeURIComponent(to.fullPath || '/'); + next(`/login?redirect=${redirect}`) // 否则全部重定向到登录页 + NProgress.done(); + } + } +}); + +router.afterEach(() => { + NProgress.done(); +}); diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts new file mode 100644 index 0000000..74c91ee --- /dev/null +++ b/src/plugins/auth.ts @@ -0,0 +1,60 @@ +import useUserStore from '@/store/modules/user'; + +const authPermission = (permission: string): boolean => { + const all_permission = '*:*:*'; + const permissions: string[] = useUserStore().permissions; + if (permission && permission.length > 0) { + return permissions.some((v) => { + return all_permission === v || v === permission; + }); + } else { + return false; + } +}; + +const authRole = (role: string): boolean => { + const super_admin = 'admin'; + const roles = useUserStore().roles; + if (role && role.length > 0) { + return roles.some((v) => { + return super_admin === v || v === role; + }); + } else { + return false; + } +}; + +export default { + // 验证用户是否具备某权限 + hasPermi(permission: string): boolean { + return authPermission(permission); + }, + // 验证用户是否含有指定权限,只需包含其中一个 + hasPermiOr(permissions: string[]): boolean { + return permissions.some((item) => { + return authPermission(item); + }); + }, + // 验证用户是否含有指定权限,必须全部拥有 + hasPermiAnd(permissions: string[]): boolean { + return permissions.every((item) => { + return authPermission(item); + }); + }, + // 验证用户是否具备某角色 + hasRole(role: string): boolean { + return authRole(role); + }, + // 验证用户是否含有指定角色,只需包含其中一个 + hasRoleOr(roles: string[]): boolean { + return roles.some((item) => { + return authRole(item); + }); + }, + // 验证用户是否含有指定角色,必须全部拥有 + hasRoleAnd(roles: string[]): boolean { + return roles.every((item) => { + return authRole(item); + }); + } +}; diff --git a/src/plugins/cache.ts b/src/plugins/cache.ts new file mode 100644 index 0000000..cf43680 --- /dev/null +++ b/src/plugins/cache.ts @@ -0,0 +1,77 @@ +const sessionCache = { + set(key: string, value: any) { + if (!sessionStorage) { + return; + } + if (key != null && value != null) { + sessionStorage.setItem(key, value); + } + }, + get(key: string) { + if (!sessionStorage) { + return null; + } + if (key == null) { + return null; + } + return sessionStorage.getItem(key); + }, + setJSON(key: string, jsonValue: any) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)); + } + }, + getJSON(key: string) { + const value = this.get(key); + if (value != null) { + return JSON.parse(value); + } + }, + remove(key: string) { + sessionStorage.removeItem(key); + } +}; +const localCache = { + set(key: string, value: any) { + if (!localStorage) { + return; + } + if (key != null && value != null) { + localStorage.setItem(key, value); + } + }, + get(key: string) { + if (!localStorage) { + return null; + } + if (key == null) { + return null; + } + return localStorage.getItem(key); + }, + setJSON(key: string, jsonValue: any) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)); + } + }, + getJSON(key: string) { + const value = this.get(key); + if (value != null) { + return JSON.parse(value); + } + }, + remove(key: string) { + localStorage.removeItem(key); + } +}; + +export default { + /** + * 会话级缓存 + */ + session: sessionCache, + /** + * 本地缓存 + */ + local: localCache +}; diff --git a/src/plugins/download.ts b/src/plugins/download.ts new file mode 100644 index 0000000..ef66b3a --- /dev/null +++ b/src/plugins/download.ts @@ -0,0 +1,65 @@ +import axios from 'axios'; +import FileSaver from 'file-saver'; +import errorCode from '@/utils/errorCode'; +import { blobValidate } from '@/utils/ruoyi'; +import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'; +import { globalHeaders } from '@/utils/request'; + +const baseURL = import.meta.env.VITE_APP_BASE_API; +let downloadLoadingInstance: LoadingInstance; +export default { + async oss(ossId: string | number) { + const url = baseURL + '/resource/oss/download/' + ossId; + downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' }); + try { + const res = await axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: globalHeaders() + }); + const isBlob = blobValidate(res.data); + if (isBlob) { + const blob = new Blob([res.data], { type: 'application/octet-stream' }); + FileSaver.saveAs(blob, decodeURIComponent(res.headers['download-filename'] as string)); + } else { + this.printErrMsg(res.data); + } + downloadLoadingInstance.close(); + } catch (r) { + console.error(r); + ElMessage.error('下载文件出现错误,请联系管理员!'); + downloadLoadingInstance.close(); + } + }, + async zip(url: string, name: string) { + url = baseURL + url; + downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' }); + try { + const res = await axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: globalHeaders() + }); + const isBlob = blobValidate(res.data); + if (isBlob) { + const blob = new Blob([res.data], { type: 'application/zip' }); + FileSaver.saveAs(blob, name); + } else { + this.printErrMsg(res.data); + } + downloadLoadingInstance.close(); + } catch (r) { + console.error(r); + ElMessage.error('下载文件出现错误,请联系管理员!'); + downloadLoadingInstance.close(); + } + }, + async printErrMsg(data: any) { + const resText = await data.text(); + const rspObj = JSON.parse(resText); + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']; + ElMessage.error(errMsg); + } +}; diff --git a/src/plugins/index.ts b/src/plugins/index.ts new file mode 100644 index 0000000..6c5e0c3 --- /dev/null +++ b/src/plugins/index.ts @@ -0,0 +1,43 @@ +import modal from './modal'; +import tab from './tab'; +import download from './download'; +import cache from './cache'; +import auth from './auth'; +// 预设动画 +import animate from '@/animate'; + +import { download as dl } from '@/utils/request'; +import { useDict } from '@/utils/dict'; +import { getConfigKey, updateConfigByKey } from '@/api/system/config'; +import { parseTime, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi'; + +import { App } from 'vue'; + +export default function installPlugin(app: App) { + // 页签操作 + app.config.globalProperties.$tab = tab; + + // 模态框对象 + app.config.globalProperties.$modal = modal; + + // 缓存对象 + app.config.globalProperties.$cache = cache; + + // 下载文件 + app.config.globalProperties.$download = download; + + // 认证对象 + app.config.globalProperties.$auth = auth; + + // 全局方法挂载 + app.config.globalProperties.useDict = useDict; + app.config.globalProperties.getConfigKey = getConfigKey; + app.config.globalProperties.updateConfigByKey = updateConfigByKey; + app.config.globalProperties.download = dl; + app.config.globalProperties.parseTime = parseTime; + app.config.globalProperties.handleTree = handleTree; + app.config.globalProperties.addDateRange = addDateRange; + app.config.globalProperties.selectDictLabel = selectDictLabel; + app.config.globalProperties.selectDictLabels = selectDictLabels; + app.config.globalProperties.animate = animate; +} diff --git a/src/plugins/modal.ts b/src/plugins/modal.ts new file mode 100644 index 0000000..a8b0548 --- /dev/null +++ b/src/plugins/modal.ts @@ -0,0 +1,81 @@ +import { MessageBoxData } from 'element-plus'; +import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'; +let loadingInstance: LoadingInstance; +export default { + // 消息提示 + msg(content: any) { + ElMessage.info(content); + }, + // 错误消息 + msgError(content: any) { + ElMessage.error(content); + }, + // 成功消息 + msgSuccess(content: any) { + ElMessage.success(content); + }, + // 警告消息 + msgWarning(content: any) { + ElMessage.warning(content); + }, + // 弹出提示 + alert(content: any) { + ElMessageBox.alert(content, '系统提示'); + }, + // 错误提示 + alertError(content: any) { + ElMessageBox.alert(content, '系统提示', { type: 'error' }); + }, + // 成功提示 + alertSuccess(content: any) { + ElMessageBox.alert(content, '系统提示', { type: 'success' }); + }, + // 警告提示 + alertWarning(content: any) { + ElMessageBox.alert(content, '系统提示', { type: 'warning' }); + }, + // 通知提示 + notify(content: any) { + ElNotification.info(content); + }, + // 错误通知 + notifyError(content: any) { + ElNotification.error(content); + }, + // 成功通知 + notifySuccess(content: any) { + ElNotification.success(content); + }, + // 警告通知 + notifyWarning(content: any) { + ElNotification.warning(content); + }, + // 确认窗体 + confirm(content: any): Promise { + return ElMessageBox.confirm(content, '系统提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }); + }, + // 提交内容 + prompt(content: any) { + return ElMessageBox.prompt(content, '系统提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }); + }, + // 打开遮罩层 + loading(content: string) { + loadingInstance = ElLoading.service({ + lock: true, + text: content, + background: 'rgba(0, 0, 0, 0.7)' + }); + }, + // 关闭遮罩层 + closeLoading() { + loadingInstance.close(); + } +}; diff --git a/src/plugins/svgicon.ts b/src/plugins/svgicon.ts new file mode 100644 index 0000000..8c68609 --- /dev/null +++ b/src/plugins/svgicon.ts @@ -0,0 +1,10 @@ +import * as ElementPlusIconsVue from '@element-plus/icons-vue'; +import { App } from 'vue'; + +export default { + install: (app: App) => { + for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component); + } + } +}; diff --git a/src/plugins/tab.ts b/src/plugins/tab.ts new file mode 100644 index 0000000..dd240cd --- /dev/null +++ b/src/plugins/tab.ts @@ -0,0 +1,96 @@ +import router from '@/router'; +import { RouteLocationMatched, RouteLocationNormalized } from 'vue-router'; +import useTagsViewStore from '@/store/modules/tagsView'; + +export default { + /** + * 刷新当前tab页签 + * @param obj 标签对象 + */ + async refreshPage(obj?: RouteLocationNormalized): Promise { + const { path, query, matched } = router.currentRoute.value; + if (obj === undefined) { + matched.forEach((m: RouteLocationMatched) => { + if (m.components && m.components.default && m.components.default.name) { + if (!['Layout', 'ParentView'].includes(m.components.default.name)) { + obj = { + name: m.components.default.name, + path: path, + query: query, + matched: undefined, + fullPath: undefined, + hash: undefined, + params: undefined, + redirectedFrom: undefined, + meta: undefined + }; + } + } + }); + } + let query1: undefined | {} = {}; + let path1: undefined | string = ''; + if (obj) { + query1 = obj.query; + path1 = obj.path; + } + await useTagsViewStore().delCachedView(obj); + await router.replace({ + path: '/redirect' + path1, + query: query1 + }); + }, + // 关闭当前tab页签,打开新页签 + closeOpenPage(obj: RouteLocationNormalized): void { + useTagsViewStore().delView(router.currentRoute.value); + if (obj !== undefined) { + router.push(obj); + } + }, + // 关闭指定tab页签 + async closePage(obj?: RouteLocationNormalized): Promise<{ visitedViews: RouteLocationNormalized[]; cachedViews: string[] } | any> { + if (obj === undefined) { + // prettier-ignore + const { visitedViews } = await useTagsViewStore().delView(router.currentRoute.value) + const latestView = visitedViews.slice(-1)[0]; + if (latestView) { + return router.push(latestView.fullPath); + } + return router.push('/'); + } + return useTagsViewStore().delView(obj); + }, + // 关闭所有tab页签 + closeAllPage() { + return useTagsViewStore().delAllViews(); + }, + // 关闭左侧tab页签 + closeLeftPage(obj?: RouteLocationNormalized) { + return useTagsViewStore().delLeftTags(obj || router.currentRoute.value); + }, + // 关闭右侧tab页签 + closeRightPage(obj?: RouteLocationNormalized) { + return useTagsViewStore().delRightTags(obj || router.currentRoute.value); + }, + // 关闭其他tab页签 + closeOtherPage(obj?: RouteLocationNormalized) { + return useTagsViewStore().delOthersViews(obj || router.currentRoute.value); + }, + /** + * 打开tab页签 + * @param url 路由地址 + * @param title 标题 + * @param query 参数 + */ + openPage(url: string, title?: string, query?: any) { + const obj = { path: url, query: { ...query, title } }; + return router.push(obj); + }, + /** + * 修改tab页签 + * @param obj 标签对象 + */ + updatePage(obj: RouteLocationNormalized) { + return useTagsViewStore().updateVisitedView(obj); + } +}; diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..2343685 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,198 @@ +import { createWebHistory, createRouter, RouteRecordRaw } from 'vue-router'; +/* Layout */ +import Layout from '@/layout/index.vue'; + +/** + * Note: 路由配置项 + * + * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 + * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 + * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 + * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 + * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + * name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题 + * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 + * roles: ['admin', 'common'] // 访问路由的角色权限 + * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 + * meta : { + noCache: true // 如果设置为true,则不会被 缓存(默认 false) + title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 + icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg + breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 + activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 + } + */ + +// 公共路由 +export const constantRoutes: RouteRecordRaw[] = [ + { + path: '/redirect', + component: Layout, + hidden: true, + children: [ + { + path: '/redirect/:path(.*)', + component: () => import('@/views/redirect/index.vue') + } + ] + }, + { + path: '/social-callback', + hidden: true, + component: () => import('@/layout/components/SocialCallback/index.vue') + }, + { + path: '/login', + component: () => import('@/views/login.vue'), + hidden: true + }, + { + path: '/register', + component: () => import('@/views/register.vue'), + hidden: true + }, + { + path: '/:pathMatch(.*)*', + component: () => import('@/views/error/404.vue'), + hidden: true + }, + { + path: '/401', + component: () => import('@/views/error/401.vue'), + hidden: true + }, + { + path: '', + component: Layout, + redirect: '/index', + children: [ + { + path: '/index', + component: () => import('@/views/index.vue'), + name: 'Index', + meta: { title: '首页', icon: 'dashboard', affix: true } + } + ] + }, + { + path: '/user', + component: Layout, + hidden: true, + redirect: 'noredirect', + children: [ + { + path: 'profile', + component: () => import('@/views/system/user/profile/index.vue'), + name: 'Profile', + meta: { title: '个人中心', icon: 'user' } + } + ] + } +]; + +// 动态路由,基于用户权限动态去加载 +export const dynamicRoutes: RouteRecordRaw[] = [ + { + path: '/system/user-auth', + component: Layout, + hidden: true, + permissions: ['system:user:edit'], + children: [ + { + path: 'role/:userId(\\d+)', + component: () => import('@/views/system/user/authRole.vue'), + name: 'AuthRole', + meta: { title: '分配角色', activeMenu: '/system/user', icon: '' } + } + ] + }, + { + path: '/system/role-auth', + component: Layout, + hidden: true, + permissions: ['system:role:edit'], + children: [ + { + path: 'user/:roleId(\\d+)', + component: () => import('@/views/system/role/authUser.vue'), + name: 'AuthUser', + meta: { title: '分配用户', activeMenu: '/system/role', icon: '' } + } + ] + }, + { + path: '/system/dict-data', + component: Layout, + hidden: true, + permissions: ['system:dict:list'], + children: [ + { + path: 'index/:dictId(\\d+)', + component: () => import('@/views/system/dict/data.vue'), + name: 'Data', + meta: { title: '字典数据', activeMenu: '/system/dict', icon: '' } + } + ] + }, + { + path: '/system/oss-config', + component: Layout, + hidden: true, + permissions: ['system:ossConfig:list'], + children: [ + { + path: 'index', + component: () => import('@/views/system/oss/config.vue'), + name: 'OssConfig', + meta: { title: '配置管理', activeMenu: '/system/oss', icon: '' } + } + ] + }, + { + path: '/tool/gen-edit', + component: Layout, + hidden: true, + permissions: ['tool:gen:edit'], + children: [ + { + path: 'index/:tableId(\\d+)', + component: () => import('@/views/tool/gen/editTable.vue'), + name: 'GenEdit', + meta: { title: '修改生成配置', activeMenu: '/tool/gen', icon: '' } + } + ] + }, + { + path: '/demo/leaveEdit', + component: Layout, + hidden: true, + permissions: ['demo:leave:edit'], + children: [ + { + path: 'index', + component: () => import('@/views/workflow/leave/leaveEdit.vue'), + name: 'leaveEdit', + meta: { title: '请假申请', activeMenu: '/demo/leave', noCache: true } + } + ] + } +]; + +/** + * 创建路由 + */ +const router = createRouter({ + history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH), + routes: constantRoutes, + // 刷新时,滚动条位置还原 + scrollBehavior(to, from, savedPosition) { + if (savedPosition) { + return savedPosition; + } else { + return { top: 0 }; + } + } +}); + +export default router; diff --git a/src/settings.ts b/src/settings.ts new file mode 100644 index 0000000..4b62c75 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,62 @@ +import { LanguageEnum } from '@/enums/LanguageEnum'; + +const setting: DefaultSettings = { + /** + * 网页标题 + */ + title: import.meta.env.VITE_APP_TITLE, + + theme: '#409EFF', + + /** + * 侧边栏主题 深色主题theme-dark,浅色主题theme-light + */ + sideTheme: 'theme-dark', + /** + * 是否系统布局配置 + */ + showSettings: true, + + /** + * 是否显示顶部导航 + */ + topNav: false, + + /** + * 是否显示 tagsView + */ + tagsView: true, + + /** + * 是否固定头部 + */ + fixedHeader: false, + + /** + * 是否显示logo + */ + sidebarLogo: true, + + /** + * 是否显示动态标题 + */ + dynamicTitle: false, + + /** + * @type {string | array} 'production' | ['production', 'development'] + * @description Need show err logs component. + * The default is only used in the production env + * If you want to also use it in dev, you can pass ['production', 'development'] + */ + errorLog: 'production', + + animationEnable: false, + + dark: false, + language: LanguageEnum.zh_CN, + + size: 'default', + + layout: '' +}; +export default setting; diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..069d54e --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,3 @@ +const store = createPinia(); + +export default store; diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts new file mode 100644 index 0000000..0205fab --- /dev/null +++ b/src/store/modules/app.ts @@ -0,0 +1,72 @@ +import zhCN from 'element-plus/es/locale/lang/zh-cn'; +import enUS from 'element-plus/es/locale/lang/en'; + +export const useAppStore = defineStore('app', () => { + const sidebarStatus = useStorage('sidebarStatus', '1'); + const sidebar = reactive({ + opened: sidebarStatus.value ? !!+sidebarStatus.value : true, + withoutAnimation: false, + hide: false + }); + const device = ref('desktop'); + const size = useStorage<'large' | 'default' | 'small'>('size', 'default'); + + // 语言 + const language = useStorage('language', 'zh_CN'); + const languageObj: any = { + en_US: enUS, + zh_CN: zhCN + }; + const locale = computed(() => { + return languageObj[language.value]; + }); + + const toggleSideBar = (withoutAnimation: boolean) => { + if (sidebar.hide) { + return false; + } + + sidebar.opened = !sidebar.opened; + sidebar.withoutAnimation = withoutAnimation; + if (sidebar.opened) { + sidebarStatus.value = '1'; + } else { + sidebarStatus.value = '0'; + } + }; + + const closeSideBar = ({ withoutAnimation }: any): void => { + sidebarStatus.value = '0'; + sidebar.opened = false; + sidebar.withoutAnimation = withoutAnimation; + }; + const toggleDevice = (d: string): void => { + device.value = d; + }; + const setSize = (s: 'large' | 'default' | 'small'): void => { + size.value = s; + }; + const toggleSideBarHide = (status: boolean): void => { + sidebar.hide = status; + }; + + const changeLanguage = (val: string): void => { + language.value = val; + }; + + return { + device, + sidebar, + language, + locale, + size, + changeLanguage, + toggleSideBar, + closeSideBar, + toggleDevice, + setSize, + toggleSideBarHide + }; +}); + +export default useAppStore; diff --git a/src/store/modules/dict.ts b/src/store/modules/dict.ts new file mode 100644 index 0000000..2f937b9 --- /dev/null +++ b/src/store/modules/dict.ts @@ -0,0 +1,78 @@ +export const useDictStore = defineStore('dict', () => { + const dict = ref< + Array<{ + key: string; + value: DictDataOption[]; + }> + >([]); + + /** + * 获取字典 + * @param _key 字典key + */ + const getDict = (_key: string): DictDataOption[] | null => { + if (_key == null && _key == '') { + return null; + } + try { + for (let i = 0; i < dict.value.length; i++) { + if (dict.value[i].key == _key) { + return dict.value[i].value; + } + } + } catch (e) { + return null; + } + return null; + }; + + /** + * 设置字典 + * @param _key 字典key + * @param _value 字典value + */ + const setDict = (_key: string, _value: DictDataOption[]) => { + if (_key !== null && _key !== '') { + dict.value.push({ + key: _key, + value: _value + }); + } + }; + + /** + * 删除字典 + * @param _key + */ + const removeDict = (_key: string): boolean => { + let bln = false; + try { + for (let i = 0; i < dict.value.length; i++) { + if (dict.value[i].key == _key) { + dict.value.splice(i, 1); + return true; + } + } + } catch (e) { + bln = false; + } + return bln; + }; + + /** + * 清空字典 + */ + const cleanDict = (): void => { + dict.value = []; + }; + + return { + dict, + getDict, + setDict, + removeDict, + cleanDict + }; +}); + +export default useDictStore; diff --git a/src/store/modules/modeler.ts b/src/store/modules/modeler.ts new file mode 100644 index 0000000..7b19c1a --- /dev/null +++ b/src/store/modules/modeler.ts @@ -0,0 +1,76 @@ +import { Modeler, Modeling, Canvas, ElementRegistry, Moddle, BpmnFactory } from 'bpmn'; + +type ModelerStore = { + modeler: Modeler | undefined; + moddle: Moddle | undefined; + modeling: Modeling | undefined; + canvas: Canvas | undefined; + elementRegistry: ElementRegistry | undefined; + bpmnFactory: BpmnFactory | undefined; + // 流程定义根节点信息 + procDefId: string | undefined; + procDefName: string | undefined; +}; + +const defaultState: ModelerStore = { + modeler: undefined, + moddle: undefined, + modeling: undefined, + canvas: undefined, + elementRegistry: undefined, + bpmnFactory: undefined, + procDefId: undefined, + procDefName: undefined +}; +export const useModelerStore = defineStore('modeler', () => { + let modeler = defaultState.modeler; + let moddle = defaultState.moddle; + let modeling = defaultState.modeling; + let canvas = defaultState.canvas; + let elementRegistry = defaultState.elementRegistry; + let bpmnFactory = defaultState.bpmnFactory; + const procDefId = ref(defaultState.procDefId); + const procDefName = ref(defaultState.procDefName); + + const getModeler = () => modeler; + const getModdle = () => moddle; + const getModeling = (): Modeling | undefined => modeling; + const getCanvas = (): Canvas | undefined => canvas; + const getElRegistry = (): ElementRegistry | undefined => elementRegistry; + const getBpmnFactory = (): BpmnFactory | undefined => bpmnFactory; + const getProcDefId = (): string | undefined => procDefId.value; + const getProcDefName = (): string | undefined => procDefName.value; + + // 设置根节点 + const setModeler = (modelers: Modeler | undefined) => { + if (modelers) { + modeler = modelers; + modeling = modelers.get('modeling'); + moddle = modelers.get('moddle'); + canvas = modelers.get('canvas'); + bpmnFactory = modelers.get('bpmnFactory'); + elementRegistry = modelers.get('elementRegistry'); + } else { + modeling = moddle = canvas = elementRegistry = bpmnFactory = undefined; + } + }; + // 设置流程定义根节点信息 + const setProcDef = (modeler: Modeler | undefined) => { + procDefId.value = modeler.get('canvas').getRootElement().businessObject.get('id'); + procDefName.value = modeler.get('canvas').getRootElement().businessObject.get('name'); + }; + + return { + getModeler, + getModdle, + getModeling, + getCanvas, + getElRegistry, + getBpmnFactory, + getProcDefId, + getProcDefName, + setModeler, + setProcDef + }; +}); +export default useModelerStore; diff --git a/src/store/modules/notice.ts b/src/store/modules/notice.ts new file mode 100644 index 0000000..de980b6 --- /dev/null +++ b/src/store/modules/notice.ts @@ -0,0 +1,42 @@ +import { defineStore } from 'pinia'; + +interface NoticeItem { + title?: string; + read: boolean; + message: any; + time: string; +} + +export const useNoticeStore = defineStore('notice', () => { + const state = reactive({ + notices: [] as NoticeItem[] + }); + + const addNotice = (notice: NoticeItem) => { + state.notices.push(notice); + }; + + const removeNotice = (notice: NoticeItem) => { + state.notices.splice(state.notices.indexOf(notice), 1); + }; + + //实现全部已读 + const readAll = () => { + state.notices.forEach((item: any) => { + item.read = true; + }); + }; + + const clearNotice = () => { + state.notices = []; + }; + return { + state, + addNotice, + removeNotice, + readAll, + clearNotice + }; +}); + +export default useNoticeStore; diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts new file mode 100644 index 0000000..cf12a73 --- /dev/null +++ b/src/store/modules/permission.ts @@ -0,0 +1,170 @@ +import { defineStore } from 'pinia'; +import router, { constantRoutes, dynamicRoutes } from '@/router'; +import store from '@/store'; +import { getRouters } from '@/api/menu'; +import auth from '@/plugins/auth'; +import { RouteRecordRaw } from 'vue-router'; + +import Layout from '@/layout/index.vue'; +import ParentView from '@/components/ParentView/index.vue'; +import InnerLink from '@/layout/components/InnerLink/index.vue'; + +// 匹配views里面所有的.vue文件 +const modules = import.meta.glob('./../../views/**/*.vue'); +export const usePermissionStore = defineStore('permission', () => { + const routes = ref([]); + const addRoutes = ref([]); + const defaultRoutes = ref([]); + const topbarRouters = ref([]); + const sidebarRouters = ref([]); + + const getRoutes = (): RouteRecordRaw[] => { + return routes.value; + }; + const getSidebarRoutes = (): RouteRecordRaw[] => { + return sidebarRouters.value; + }; + const getTopbarRoutes = (): RouteRecordRaw[] => { + return topbarRouters.value; + }; + + const setRoutes = (newRoutes: RouteRecordRaw[]): void => { + addRoutes.value = newRoutes; + routes.value = constantRoutes.concat(newRoutes); + }; + const setDefaultRoutes = (routes: RouteRecordRaw[]): void => { + defaultRoutes.value = constantRoutes.concat(routes); + }; + const setTopbarRoutes = (routes: RouteRecordRaw[]): void => { + topbarRouters.value = routes; + }; + const setSidebarRouters = (routes: RouteRecordRaw[]): void => { + sidebarRouters.value = routes; + }; + const generateRoutes = async (): Promise => { + const res = await getRouters(); + const { data } = res; + const sdata = JSON.parse(JSON.stringify(data)); + const rdata = JSON.parse(JSON.stringify(data)); + const defaultData = JSON.parse(JSON.stringify(data)); + const sidebarRoutes = filterAsyncRouter(sdata); + const rewriteRoutes = filterAsyncRouter(rdata, undefined, true); + const defaultRoutes = filterAsyncRouter(defaultData); + const asyncRoutes = filterDynamicRoutes(dynamicRoutes); + asyncRoutes.forEach((route) => { + router.addRoute(route); + }); + setRoutes(rewriteRoutes); + setSidebarRouters(constantRoutes.concat(sidebarRoutes)); + setDefaultRoutes(sidebarRoutes); + setTopbarRoutes(defaultRoutes); + return new Promise((resolve) => resolve(rewriteRoutes)); + }; + + /** + * 遍历后台传来的路由字符串,转换为组件对象 + * @param asyncRouterMap 后台传来的路由字符串 + * @param lastRouter 上一级路由 + * @param type 是否是重写路由 + */ + const filterAsyncRouter = (asyncRouterMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw, type = false): RouteRecordRaw[] => { + return asyncRouterMap.filter((route) => { + if (type && route.children) { + route.children = filterChildren(route.children, undefined); + } + // Layout ParentView 组件特殊处理 + if (route.component?.toString() === 'Layout') { + route.component = Layout; + } else if (route.component?.toString() === 'ParentView') { + route.component = ParentView; + } else if (route.component?.toString() === 'InnerLink') { + route.component = InnerLink; + } else { + route.component = loadView(route.component); + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, route, type); + } else { + delete route.children; + delete route.redirect; + } + return true; + }); + }; + const filterChildren = (childrenMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw): RouteRecordRaw[] => { + let children: RouteRecordRaw[] = []; + childrenMap.forEach((el) => { + if (el.children && el.children.length) { + if (el.component?.toString() === 'ParentView' && !lastRouter) { + el.children.forEach((c) => { + c.path = el.path + '/' + c.path; + if (c.children && c.children.length) { + children = children.concat(filterChildren(c.children, c)); + return; + } + children.push(c); + }); + return; + } + } + if (lastRouter) { + el.path = lastRouter.path + '/' + el.path; + if (el.children && el.children.length) { + children = children.concat(filterChildren(el.children, el)); + return; + } + } + children = children.concat(el); + }); + return children; + }; + return { + routes, + topbarRouters, + sidebarRouters, + defaultRoutes, + + getRoutes, + getSidebarRoutes, + getTopbarRoutes, + + setRoutes, + generateRoutes, + setSidebarRouters + }; +}); + +// 动态路由遍历,验证是否具备权限 +export const filterDynamicRoutes = (routes: RouteRecordRaw[]) => { + const res: RouteRecordRaw[] = []; + routes.forEach((route) => { + if (route.permissions) { + if (auth.hasPermiOr(route.permissions)) { + res.push(route); + } + } else if (route.roles) { + if (auth.hasRoleOr(route.roles)) { + res.push(route); + } + } + }); + return res; +}; + +export const loadView = (view: any) => { + let res; + for (const path in modules) { + const dir = path.split('views/')[1].split('.vue')[0]; + if (dir === view) { + res = () => modules[path](); + } + } + return res; +}; + +// 非setup +export const usePermissionStoreHook = () => { + return usePermissionStore(store); +}; + +export default usePermissionStore; diff --git a/src/store/modules/settings.ts b/src/store/modules/settings.ts new file mode 100644 index 0000000..ef49b3c --- /dev/null +++ b/src/store/modules/settings.ts @@ -0,0 +1,47 @@ +import { defineStore } from 'pinia'; +import defaultSettings from '@/settings'; +import { useDynamicTitle } from '@/utils/dynamicTitle'; + +export const useSettingsStore = defineStore('setting', () => { + const storageSetting = useStorage('layout-setting', { + topNav: defaultSettings.topNav, + tagsView: defaultSettings.tagsView, + fixedHeader: defaultSettings.fixedHeader, + sidebarLogo: defaultSettings.sidebarLogo, + dynamicTitle: defaultSettings.dynamicTitle, + sideTheme: defaultSettings.sideTheme, + theme: defaultSettings.theme + }); + const title = ref(defaultSettings.title); + const theme = ref(storageSetting.value.theme); + const sideTheme = ref(storageSetting.value.sideTheme); + const showSettings = ref(defaultSettings.showSettings); + const topNav = ref(storageSetting.value.topNav); + const tagsView = ref(storageSetting.value.tagsView); + const fixedHeader = ref(storageSetting.value.fixedHeader); + const sidebarLogo = ref(storageSetting.value.sidebarLogo); + const dynamicTitle = ref(storageSetting.value.dynamicTitle); + const animationEnable = ref(defaultSettings.animationEnable); + const dark = ref(defaultSettings.dark); + + const setTitle = (value: string) => { + title.value = value; + useDynamicTitle(); + }; + return { + title, + theme, + sideTheme, + showSettings, + topNav, + tagsView, + fixedHeader, + sidebarLogo, + dynamicTitle, + animationEnable, + dark, + setTitle + }; +}); + +export default useSettingsStore; diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts new file mode 100644 index 0000000..b9502eb --- /dev/null +++ b/src/store/modules/tagsView.ts @@ -0,0 +1,233 @@ +import { RouteLocationNormalized } from 'vue-router'; + +export const useTagsViewStore = defineStore('tagsView', () => { + const visitedViews = ref([]); + const cachedViews = ref([]); + const iframeViews = ref([]); + + const getVisitedViews = (): RouteLocationNormalized[] => { + return visitedViews.value; + }; + const getIframeViews = (): RouteLocationNormalized[] => { + return iframeViews.value; + }; + const getCachedViews = (): string[] => { + return cachedViews.value; + }; + + const addView = (view: RouteLocationNormalized) => { + addVisitedView(view); + addCachedView(view); + }; + + const addIframeView = (view: RouteLocationNormalized): void => { + if (iframeViews.value.some((v: RouteLocationNormalized) => v.path === view.path)) return; + iframeViews.value.push( + Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }) + ); + }; + const delIframeView = (view: RouteLocationNormalized): Promise => { + return new Promise((resolve) => { + iframeViews.value = iframeViews.value.filter((item: RouteLocationNormalized) => item.path !== view.path); + resolve([...iframeViews.value]); + }); + }; + const addVisitedView = (view: RouteLocationNormalized): void => { + if (visitedViews.value.some((v: RouteLocationNormalized) => v.path === view.path)) return; + visitedViews.value.push( + Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }) + ); + }; + const delView = ( + view: RouteLocationNormalized + ): Promise<{ + visitedViews: RouteLocationNormalized[]; + cachedViews: string[]; + }> => { + return new Promise((resolve) => { + delVisitedView(view); + if (!isDynamicRoute(view)) { + delCachedView(view); + } + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value] + }); + }); + }; + + const delVisitedView = (view: RouteLocationNormalized): Promise => { + return new Promise((resolve) => { + for (const [i, v] of visitedViews.value.entries()) { + if (v.path === view.path) { + visitedViews.value.splice(i, 1); + break; + } + } + resolve([...visitedViews.value]); + }); + }; + const delCachedView = (view?: RouteLocationNormalized): Promise => { + let viewName = ''; + if (view) { + viewName = view.name as string; + } + return new Promise((resolve) => { + const index = cachedViews.value.indexOf(viewName); + index > -1 && cachedViews.value.splice(index, 1); + resolve([...cachedViews.value]); + }); + }; + const delOthersViews = ( + view: RouteLocationNormalized + ): Promise<{ + visitedViews: RouteLocationNormalized[]; + cachedViews: string[]; + }> => { + return new Promise((resolve) => { + delOthersVisitedViews(view); + delOthersCachedViews(view); + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value] + }); + }); + }; + + const delOthersVisitedViews = (view: RouteLocationNormalized): Promise => { + return new Promise((resolve) => { + visitedViews.value = visitedViews.value.filter((v: RouteLocationNormalized) => { + return v.meta?.affix || v.path === view.path; + }); + resolve([...visitedViews.value]); + }); + }; + const delOthersCachedViews = (view: RouteLocationNormalized): Promise => { + const viewName = view.name as string; + return new Promise((resolve) => { + const index = cachedViews.value.indexOf(viewName); + if (index > -1) { + cachedViews.value = cachedViews.value.slice(index, index + 1); + } else { + cachedViews.value = []; + } + resolve([...cachedViews.value]); + }); + }; + + const delAllViews = (): Promise<{ visitedViews: RouteLocationNormalized[]; cachedViews: string[] }> => { + return new Promise((resolve) => { + delAllVisitedViews(); + delAllCachedViews(); + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value] + }); + }); + }; + const delAllVisitedViews = (): Promise => { + return new Promise((resolve) => { + visitedViews.value = visitedViews.value.filter((tag: RouteLocationNormalized) => tag.meta?.affix); + resolve([...visitedViews.value]); + }); + }; + + const delAllCachedViews = (): Promise => { + return new Promise((resolve) => { + cachedViews.value = []; + resolve([...cachedViews.value]); + }); + }; + + const updateVisitedView = (view: RouteLocationNormalized): void => { + for (let v of visitedViews.value) { + if (v.path === view.path) { + v = Object.assign(v, view); + break; + } + } + }; + const delRightTags = (view: RouteLocationNormalized): Promise => { + return new Promise((resolve) => { + const index = visitedViews.value.findIndex((v: RouteLocationNormalized) => v.path === view.path); + if (index === -1) { + return; + } + visitedViews.value = visitedViews.value.filter((item: RouteLocationNormalized, idx: number) => { + if (idx <= index || (item.meta && item.meta.affix)) { + return true; + } + const i = cachedViews.value.indexOf(item.name as string); + if (i > -1) { + cachedViews.value.splice(i, 1); + } + return false; + }); + resolve([...visitedViews.value]); + }); + }; + const delLeftTags = (view: RouteLocationNormalized): Promise => { + return new Promise((resolve) => { + const index = visitedViews.value.findIndex((v: RouteLocationNormalized) => v.path === view.path); + if (index === -1) { + return; + } + visitedViews.value = visitedViews.value.filter((item: RouteLocationNormalized, idx: number) => { + if (idx >= index || (item.meta && item.meta.affix)) { + return true; + } + const i = cachedViews.value.indexOf(item.name as string); + if (i > -1) { + cachedViews.value.splice(i, 1); + } + return false; + }); + resolve([...visitedViews.value]); + }); + }; + + const addCachedView = (view: RouteLocationNormalized): void => { + const viewName = view.name as string; + if (!viewName) return; + if (cachedViews.value.includes(viewName)) return; + if (!view.meta?.noCache) { + cachedViews.value.push(viewName); + } + }; + + const isDynamicRoute = (view: RouteLocationNormalized): boolean => { + // 检查匹配的路由记录中是否有动态段 + return view.matched.some((m) => m.path.includes(':')); + }; + + return { + visitedViews, + cachedViews, + iframeViews, + + getVisitedViews, + getIframeViews, + getCachedViews, + + addVisitedView, + addCachedView, + delVisitedView, + delCachedView, + updateVisitedView, + addView, + delView, + delAllViews, + delAllVisitedViews, + delAllCachedViews, + delOthersViews, + delRightTags, + delLeftTags, + addIframeView, + delIframeView + }; +}); +export default useTagsViewStore; diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts new file mode 100644 index 0000000..4122294 --- /dev/null +++ b/src/store/modules/user.ts @@ -0,0 +1,88 @@ +import { to } from 'await-to-js'; +import { getToken, removeToken, setToken } from '@/utils/auth'; +import { login as loginApi, logout as logoutApi, getInfo as getUserInfo } from '@/api/login'; +import { LoginData } from '@/api/types'; +import defAva from '@/assets/images/profile.jpg'; +import store from '@/store'; + +export const useUserStore = defineStore('user', () => { + const token = ref(getToken()); + const name = ref(''); + const nickname = ref(''); + const userId = ref(''); + const avatar = ref(''); + const roles = ref>([]); // 用户角色编码集合 → 判断路由权限 + const permissions = ref>([]); // 用户权限编码集合 → 判断按钮权限 + + /** + * 登录 + * @param userInfo + * @returns + */ + const login = async (userInfo: LoginData): Promise => { + const [err, res] = await to(loginApi(userInfo)); + if (res) { + const data = res.data; + setToken(data.access_token); + token.value = data.access_token; + return Promise.resolve(); + } + return Promise.reject(err); + }; + + // 获取用户信息 + const getInfo = async (): Promise => { + const [err, res] = await to(getUserInfo()); + if (res) { + const data = res.data; + const user = data.user; + const profile = user.avatar == '' || user.avatar == null ? defAva : user.avatar; + + if (data.roles && data.roles.length > 0) { + // 验证返回的roles是否是一个非空数组 + roles.value = data.roles; + permissions.value = data.permissions; + } else { + roles.value = ['ROLE_DEFAULT']; + } + name.value = user.userName; + nickname.value = user.nickName; + avatar.value = profile; + userId.value = user.userId; + return Promise.resolve(); + } + return Promise.reject(err); + }; + + // 注销 + const logout = async (): Promise => { + await logoutApi(); + token.value = ''; + roles.value = []; + permissions.value = []; + removeToken(); + }; + + const setAvatar = (value: string) => { + avatar.value = value; + }; + + return { + userId, + token, + nickname, + avatar, + roles, + permissions, + login, + getInfo, + logout, + setAvatar + }; +}); + +export default useUserStore; +// 非setup +export function useUserStoreHook() { + return useUserStore(store); +} diff --git a/src/types/axios.d.ts b/src/types/axios.d.ts new file mode 100644 index 0000000..9f2c6d2 --- /dev/null +++ b/src/types/axios.d.ts @@ -0,0 +1,9 @@ +export {}; +declare module 'axios' { + interface AxiosResponse { + code: number; + msg: string; + rows: T; + total: number; + } +} diff --git a/src/types/bpmn/editor/global.d.ts b/src/types/bpmn/editor/global.d.ts new file mode 100644 index 0000000..1b9a634 --- /dev/null +++ b/src/types/bpmn/editor/global.d.ts @@ -0,0 +1,13 @@ +import { MessageApiInjection } from 'naive-ui/lib/message/src/MessageProvider'; + +declare global { + interface Window { + bpmnInstances: any; + __messageBox: MessageApiInjection; + URL: any; + } +} + +declare interface Window { + bpmnInstances: any; +} diff --git a/src/types/bpmn/index.d.ts b/src/types/bpmn/index.d.ts new file mode 100644 index 0000000..f8e8d15 --- /dev/null +++ b/src/types/bpmn/index.d.ts @@ -0,0 +1,15 @@ +declare module 'bpmn' { + import type modeler from 'bpmn-js/lib/Modeler'; + import type modeling from 'bpmn-js/lib/features/modeling/Modeling'; + import type canvas from 'diagram-js/lib/core/Canvas'; + import type elementRegistry from 'diagram-js/lib/core/ElementRegistry'; + import type bpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory'; + + export type Modeler = modeler; + export type Modeling = modeling; + export type Canvas = canvas; + export type ElementRegistry = elementRegistry; + export type Moddle = import('moddle').Moddle; + export type ModdleElement = import('moddle').ModdleElement; + export type BpmnFactory = bpmnFactory; +} diff --git a/src/types/bpmn/moddle.d.ts b/src/types/bpmn/moddle.d.ts new file mode 100644 index 0000000..1ed7933 --- /dev/null +++ b/src/types/bpmn/moddle.d.ts @@ -0,0 +1,37 @@ +declare module 'moddle' { + import type { Element as element } from 'bpmn-js/lib/model/Types'; + + export type Element = { + get(name: string): T; + + set(name: string, value: any): void; + } & element; + + export interface ModdleElement extends Element { + $model: Moddle; + readonly $type: string; + $attrs: object | {}; + $parent: any; + businessObject: ModdleElement; + type: string; + + [field: string]: any; + + hasType(element: ModdleElement, type?: string): boolean; + } + + export interface Package { + name: string; + prefix: string; + } + + export interface Moddle { + typeCache: Record; + + getPackage: typeof Registry.prototype.getPackage; + + getPackages: typeof Registry.prototype.getPackages; + + create(type: string, attrs?: any): ModdleElement; + } +} diff --git a/src/types/bpmn/panel.d.ts b/src/types/bpmn/panel.d.ts new file mode 100644 index 0000000..3179261 --- /dev/null +++ b/src/types/bpmn/panel.d.ts @@ -0,0 +1,92 @@ +declare module 'bpmnDesign' { + import { AllocationTypeEnum, SpecifyDescEnum, MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums'; + + export interface ParamVO { + type: string; + name: string; + value: string; + } + + export interface TaskListenerVO { + event: string; + type: string; + name: string; + className: string; + params: ParamVO[]; + } + + export interface ExecutionListenerVO { + event: string; + type: string; + className: string; + params: ParamVO[]; + } + + interface BasePanel { + id: string; + name: string; + } + export interface ProcessPanel extends BasePanel {} + + export interface TaskPanel extends BasePanel { + allocationType: AllocationTypeEnum; + specifyDesc: SpecifyDescEnum; + multiInstanceType: MultiInstanceTypeEnum; + async?: boolean; + priority?: number; + formKey?: string; + skipExpression?: string; + isForCompensation?: boolean; + triggerServiceTask?: boolean; + autoStoreVariables?: boolean; + ruleVariablesInput?: string; + excludeTaskListener?: boolean; + exclude?: boolean; + class?: string; + dueDate?: string; + fixedAssignee?: string; + + candidateUsers?: string; + assignee?: string; + candidateGroups?: string; + collection?: string; + elementVariable?: string; + completionCondition?: string; + isSequential?: boolean; + + loopCharacteristics?: { + collection: string; + elementVariable: string; + isSequential: boolean; + completionCondition: { + body: string; + }; + }; + } + + export interface StartEndPanel extends BasePanel {} + export interface GatewayPanel extends BasePanel {} + export interface SequenceFlowPanel extends BasePanel { + conditionExpression: { + body: string; + }; + conditionExpressionValue: string; + skipExpression: string; + } + + export interface ParticipantPanel extends BasePanel {} + export interface SubProcessPanel extends BasePanel { + multiInstanceType: MultiInstanceTypeEnum; + collection?: string; + elementVariable?: string; + completionCondition?: string; + loopCharacteristics?: { + collection: string; + elementVariable: string; + isSequential: boolean; + completionCondition: { + body: string; + }; + }; + } +} diff --git a/src/types/element.d.ts b/src/types/element.d.ts new file mode 100644 index 0000000..ae6ecfc --- /dev/null +++ b/src/types/element.d.ts @@ -0,0 +1,35 @@ +import type * as ep from 'element-plus'; +declare global { + declare type ElTagType = 'primary' | 'success' | 'info' | 'warning' | 'danger'; + declare type ElFormInstance = ep.FormInstance; + declare type ElTableInstance = ep.TableInstance; + declare type ElUploadInstance = ep.UploadInstance; + declare type ElScrollbarInstance = ep.ScrollbarInstance; + declare type ElInputInstance = ep.InputInstance; + declare type ElInputNumberInstance = ep.InputNumberInstance; + declare type ElRadioInstance = ep.RadioInstance; + declare type ElRadioGroupInstance = ep.RadioGroupInstance; + declare type ElRadioButtonInstance = ep.RadioButtonInstance; + declare type ElCheckboxInstance = ep.CheckboxInstance; + declare type ElSwitchInstance = ep.SwitchInstance; + declare type ElCascaderInstance = ep.CascaderInstance; + declare type ElColorPickerInstance = ep.ColorPickerInstance; + declare type ElRateInstance = ep.RateInstance; + declare type ElSliderInstance = ep.SliderInstance; + + declare type ElTreeInstance = InstanceType; + declare type ElTreeSelectInstance = InstanceType; + declare type ElSelectInstance = InstanceType; + declare type ElCardInstance = InstanceType; + declare type ElDialogInstance = InstanceType; + declare type ElCheckboxGroupInstance = InstanceType; + declare type ElDatePickerInstance = InstanceType; + declare type ElTimePickerInstance = InstanceType; + declare type ElTimeSelectInstance = InstanceType; + + declare type TransferKey = ep.TransferKey; + declare type CheckboxValueType = ep.CheckboxValueType; + declare type ElFormRules = ep.FormRules; + declare type DateModelType = ep.DateModelType; + declare type UploadFile = ep.UploadFile; +} diff --git a/src/types/env.d.ts b/src/types/env.d.ts new file mode 100644 index 0000000..bfc37cc --- /dev/null +++ b/src/types/env.d.ts @@ -0,0 +1,27 @@ +declare module '*.vue' { + import { DefineComponent } from 'vue'; + const Component: DefineComponent<{}, {}, any>; + export default Component; +} + +// 环境变量 +interface ImportMetaEnv { + VITE_APP_TITLE: string; + VITE_APP_PORT: number; + VITE_APP_BASE_API: string; + VITE_APP_BASE_URL: string; + VITE_APP_CONTEXT_PATH: string; + VITE_APP_MONITRO_ADMIN: string; + VITE_APP_POWERJOB_ADMIN: string; + VITE_APP_EASYRETRY_ADMIN: string; + VITE_APP_ENV: string; + VITE_APP_ENCRYPT: string; + VITE_APP_RSA_PUBLIC_KEY: string; + VITE_APP_RSA_PRIVATE_KEY: string; + VITE_APP_CLIENT_ID: string; + VITE_APP_WEBSOCKET: string; +} +interface ImportMeta { + readonly env: ImportMetaEnv; + // readonly glob: any; +} diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 0000000..17476a3 --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,166 @@ +import type { PropType as VuePropType, ComponentInternalInstance as ComponentInstance } from 'vue'; +import { LanguageEnum } from '@/enums/LanguageEnum'; + +declare global { + /** vue Instance */ + declare type ComponentInternalInstance = ComponentInstance; + /**vue */ + declare type PropType = VuePropType; + + /** + * 界面字段隐藏属性 + */ + declare interface FieldOption { + key: number; + label: string; + visible: boolean; + children?: Array; + } + + /** + * 弹窗属性 + */ + declare interface DialogOption { + /** + * 弹窗标题 + */ + title?: string; + /** + * 是否显示 + */ + visible: boolean; + } + + declare interface UploadOption { + /** 设置上传的请求头部 */ + headers: { [key: string]: any }; + + /** 上传的地址 */ + url: string; + } + + /** + * 导入属性 + */ + declare interface ImportOption extends UploadOption { + /** 是否显示弹出层 */ + open: boolean; + /** 弹出层标题 */ + title: string; + /** 是否禁用上传 */ + isUploading: boolean; + + updateSupport: number; + + /** 其他参数 */ + [key: string]: any; + } + /** + * 字典数据 数据配置 + */ + declare interface DictDataOption { + label: string; + value: string; + elTagType?: ElTagType; + elTagClass?: string; + } + + declare interface BaseEntity { + createBy?: any; + createDept?: any; + createTime?: string; + updateBy?: any; + updateTime?: any; + } + + /** + * 分页数据 + * T : 表单数据 + * D : 查询参数 + */ + declare interface PageData { + form: T; + queryParams: D; + rules: ElFormRules; + } + /** + * 分页查询参数 + */ + declare interface PageQuery { + pageNum: number; + pageSize: number; + } + declare interface LayoutSetting { + /** + * 是否显示顶部导航 + */ + topNav: boolean; + + /** + * 是否显示多标签导航 + */ + tagsView: boolean; + /** + * 是否固定头部 + */ + fixedHeader: boolean; + /** + * 是否显示侧边栏Logo + */ + sidebarLogo: boolean; + /** + * 是否显示动态标题 + */ + dynamicTitle: boolean; + /** + * 侧边栏主题 theme-dark | theme-light + */ + sideTheme: string; + /** + * 主题模式 + */ + theme: string; + } + + declare interface DefaultSettings extends LayoutSetting { + /** + * 网页标题 + */ + title: string; + + /** + * 是否显示系统布局设置 + */ + showSettings: boolean; + + /** + * 导航栏布局 + */ + layout: string; + + /** + * 布局大小 + */ + size: 'large' | 'default' | 'small'; + + /** + * 语言 + */ + language: LanguageEnum; + + /** + * 是否启用动画效果 + */ + animationEnable: boolean; + /** + * 是否启用暗黑模式 + * + * true:暗黑模式 + * false: 明亮模式 + */ + dark: boolean; + + errorLog: string; + } +} +export {}; diff --git a/src/types/module.d.ts b/src/types/module.d.ts new file mode 100644 index 0000000..984df9f --- /dev/null +++ b/src/types/module.d.ts @@ -0,0 +1,39 @@ +import modal from '@/plugins/modal'; +import tab from '@/plugins/tab'; +import download from '@/plugins/download'; +import auth from '@/plugins/auth'; +import cache from '@/plugins/cache'; +import animate from '@/animate'; +import { useDict } from '@/utils/dict'; +import { handleTree, addDateRange, selectDictLabel, selectDictLabels, parseTime } from '@/utils/ruoyi'; +import { getConfigKey, updateConfigByKey } from '@/api/system/config'; +import { download as rd } from '@/utils/request'; + +export {}; + +declare module 'vue' { + interface ComponentCustomProperties { + // 全局方法声明 + $modal: typeof modal; + $tab: typeof tab; + $download: typeof download; + $auth: typeof auth; + $cache: typeof cache; + animate: typeof animate; + + useDict: typeof useDict; + addDateRange: typeof addDateRange; + download: typeof rd; + handleTree: typeof handleTree; + getConfigKey: typeof getConfigKey; + updateConfigByKey: typeof updateConfigByKey; + selectDictLabel: typeof selectDictLabel; + selectDictLabels: typeof selectDictLabels; + parseTime: typeof parseTime; + } +} + +declare module 'vform3-builds' { + const content: any; + export = content; +} diff --git a/src/types/router.d.ts b/src/types/router.d.ts new file mode 100644 index 0000000..11a60a0 --- /dev/null +++ b/src/types/router.d.ts @@ -0,0 +1,38 @@ +import { LocationQuery, type RouteMeta as VRouteMeta } from 'vue-router'; +declare module 'vue-router' { + interface RouteMeta extends VRouteMeta { + link?: string; + title?: string; + affix?: boolean; + noCache?: boolean; + activeMenu?: string; + icon?: string; + breadcrumb?: boolean; + } + + interface _RouteRecordBase { + hidden?: boolean | string | number; + permissions?: string[]; + roles?: string[]; + alwaysShow?: boolean; + query?: string; + parentPath?: string; + } + + interface _RouteLocationBase { + children?: _RouteRecordBase[]; + path?: string; + title?: string; + } + + interface TagView { + fullPath?: string; + name?: string; + path?: string; + title?: string; + meta?: RouteMeta; + query?: LocationQuery; + } +} + +export {}; diff --git a/src/utils/auth.ts b/src/utils/auth.ts new file mode 100644 index 0000000..db50ac9 --- /dev/null +++ b/src/utils/auth.ts @@ -0,0 +1,9 @@ +const TokenKey = 'Admin-Token'; + +const tokenStorage = useStorage(TokenKey, null); + +export const getToken = () => tokenStorage.value; + +export const setToken = (access_token: string) => (tokenStorage.value = access_token); + +export const removeToken = () => (tokenStorage.value = null); diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts new file mode 100644 index 0000000..8217146 --- /dev/null +++ b/src/utils/crypto.ts @@ -0,0 +1,66 @@ +import CryptoJS from 'crypto-js'; + +/** + * 随机生成32位的字符串 + * @returns {string} + */ +const generateRandomString = () => { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + const charactersLength = characters.length; + for (let i = 0; i < 32; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +}; + +/** + * 随机生成aes 密钥 + * @returns {string} + */ +export const generateAesKey = () => { + return CryptoJS.enc.Utf8.parse(generateRandomString()); +}; + +/** + * 加密base64 + * @returns {string} + */ +export const encryptBase64 = (str: CryptoJS.lib.WordArray) => { + return CryptoJS.enc.Base64.stringify(str); +}; + +/** + * 解密base64 + */ +export const decryptBase64 = (str: string) => { + return CryptoJS.enc.Base64.parse(str); +}; + +/** + * 使用密钥对数据进行加密 + * @param message + * @param aesKey + * @returns {string} + */ +export const encryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray) => { + const encrypted = CryptoJS.AES.encrypt(message, aesKey, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }); + return encrypted.toString(); +}; + +/** + * 使用密钥对数据进行解密 + * @param message + * @param aesKey + * @returns {string} + */ +export const decryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray) => { + const decrypted = CryptoJS.AES.decrypt(message, aesKey, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }); + return decrypted.toString(CryptoJS.enc.Utf8); +}; diff --git a/src/utils/dict.ts b/src/utils/dict.ts new file mode 100644 index 0000000..0575072 --- /dev/null +++ b/src/utils/dict.ts @@ -0,0 +1,27 @@ +import { getDicts } from '@/api/system/dict/data'; +import { useDictStore } from '@/store/modules/dict'; +/** + * 获取字典数据 + */ +export const useDict = (...args: string[]): { [key: string]: DictDataOption[] } => { + const res = ref<{ + [key: string]: DictDataOption[]; + }>({}); + return (() => { + args.forEach(async (dictType) => { + res.value[dictType] = []; + const dicts = useDictStore().getDict(dictType); + if (dicts) { + res.value[dictType] = dicts; + } else { + await getDicts(dictType).then((resp) => { + res.value[dictType] = resp.data.map( + (p): DictDataOption => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }) + ); + useDictStore().setDict(dictType, res.value[dictType]); + }); + } + }); + return res.value; + })(); +}; diff --git a/src/utils/dynamicTitle.ts b/src/utils/dynamicTitle.ts new file mode 100644 index 0000000..8e23ef8 --- /dev/null +++ b/src/utils/dynamicTitle.ts @@ -0,0 +1,14 @@ +import defaultSettings from '@/settings'; +import { useSettingsStore } from '@/store/modules/settings'; + +/** + * 动态修改标题 + */ +export const useDynamicTitle = () => { + const settingsStore = useSettingsStore(); + if (settingsStore.dynamicTitle) { + document.title = settingsStore.title + ' - ' + import.meta.env.VITE_APP_TITLE; + } else { + document.title = defaultSettings.title as string; + } +}; diff --git a/src/utils/errorCode.ts b/src/utils/errorCode.ts new file mode 100644 index 0000000..d85914e --- /dev/null +++ b/src/utils/errorCode.ts @@ -0,0 +1,7 @@ +export const errorCode: any = { + '401': '认证失败,无法访问系统资源', + '403': '当前操作没有权限', + '404': '访问资源不存在', + default: '系统未知错误,请反馈给管理员' +}; +export default errorCode; diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts new file mode 100644 index 0000000..ab77af0 --- /dev/null +++ b/src/utils/i18n.ts @@ -0,0 +1,16 @@ +// translate router.meta.title, be used in breadcrumb sidebar tagsview +import i18n from '@/lang/index'; + +/** + * 获取国际化路由,如果不存在则原生返回 + * @param title 路由名称 + * @returns {string} + */ +export const translateRouteTitle = (title: string): string => { + const hasKey = i18n.global.te('route.' + title); + if (hasKey) { + const translatedTitle = i18n.global.t('route.' + title); + return translatedTitle; + } + return title; +}; diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..2b0aad5 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,318 @@ +import { parseTime } from '@/utils/ruoyi'; + +/** + * 表格时间格式化 + */ +export const formatDate = (cellValue: string) => { + if (cellValue == null || cellValue == '') return ''; + const date = new Date(cellValue); + const year = date.getFullYear(); + const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1; + const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); + const hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours(); + const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(); + const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(); + return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds; +}; + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export const formatTime = (time: string, option: string) => { + let t: number; + if (('' + time).length === 10) { + t = parseInt(time) * 1000; + } else { + t = +time; + } + const d: any = new Date(t); + const now = Date.now(); + + const diff = (now - d) / 1000; + + if (diff < 30) { + return '刚刚'; + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前'; + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前'; + } else if (diff < 3600 * 24 * 2) { + return '1天前'; + } + if (option) { + return parseTime(t, option); + } else { + return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'; + } +}; + +/** + * @param {string} url + * @returns {Object} + */ +export const getQueryObject = (url: string) => { + url = url == null ? window.location.href : url; + const search = url.substring(url.lastIndexOf('?') + 1); + const obj: { [key: string]: string } = {}; + const reg = /([^?&=]+)=([^?&=]*)/g; + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1); + let val = decodeURIComponent($2); + val = String(val); + obj[name] = val; + return rs; + }); + return obj; +}; + +/** + * @param {string} input value + * @returns {number} output value + */ +export const byteLength = (str: string) => { + // returns the byte length of an utf8 string + let s = str.length; + for (let i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i); + if (code > 0x7f && code <= 0x7ff) s++; + else if (code > 0x7ff && code <= 0xffff) s += 2; + if (code >= 0xdc00 && code <= 0xdfff) i--; + } + return s; +}; + +/** + * @param {Array} actual + * @returns {Array} + */ +export const cleanArray = (actual: Array) => { + const newArray: any[] = []; + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]); + } + } + return newArray; +}; + +/** + * @param {Object} json + * @returns {Array} + */ +export const param = (json: any) => { + if (!json) return ''; + return cleanArray( + Object.keys(json).map((key) => { + if (json[key] === undefined) return ''; + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]); + }) + ).join('&'); +}; + +/** + * @param {string} url + * @returns {Object} + */ +export const param2Obj = (url: string) => { + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' '); + if (!search) { + return {}; + } + const obj: any = {}; + const searchArr = search.split('&'); + searchArr.forEach((v) => { + const index = v.indexOf('='); + if (index !== -1) { + const name = v.substring(0, index); + const val = v.substring(index + 1, v.length); + obj[name] = val; + } + }); + return obj; +}; + +/** + * @param {string} val + * @returns {string} + */ +export const html2Text = (val: string) => { + const div = document.createElement('div'); + div.innerHTML = val; + return div.textContent || div.innerText; +}; + +/** + * Merges two objects, giving the last one precedence + * @param {Object} target + * @param {(Object|Array)} source + * @returns {Object} + */ +export const objectMerge = (target: any, source: any | any[]) => { + if (typeof target !== 'object') { + target = {}; + } + if (Array.isArray(source)) { + return source.slice(); + } + Object.keys(source).forEach((property) => { + const sourceProperty = source[property]; + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty); + } else { + target[property] = sourceProperty; + } + }); + return target; +}; + +/** + * @param {HTMLElement} element + * @param {string} className + */ +export const toggleClass = (element: HTMLElement, className: string) => { + if (!element || !className) { + return; + } + let classString = element.className; + const nameIndex = classString.indexOf(className); + if (nameIndex === -1) { + classString += '' + className; + } else { + classString = classString.substring(0, nameIndex) + classString.substring(nameIndex + className.length); + } + element.className = classString; +}; + +/** + * @param {string} type + * @returns {Date} + */ +export const getTime = (type: string) => { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90; + } else { + return new Date(new Date().toDateString()); + } +}; + +/** + * @param {Function} func + * @param {number} wait + * @param {boolean} immediate + * @return {*} + */ +export const debounce = (func: any, wait: number, immediate: boolean) => { + let timeout: any, args: any, context: any, timestamp: any, result: any; + + const later = function () { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp; + + // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args); + if (!timeout) context = args = null; + } + } + }; + + return (...args: any) => { + context = this; + timestamp = +new Date(); + const callNow = immediate && !timeout; + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + return result; + }; +}; + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +export const deepClone = (source: any) => { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone' as any); + } + const targetObj: any = source.constructor === Array ? [] : {}; + Object.keys(source).forEach((keys) => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]); + } else { + targetObj[keys] = source[keys]; + } + }); + return targetObj; +}; + +/** + * @param {Array} arr + * @returns {Array} + */ +export const uniqueArr = (arr: any) => { + return Array.from(new Set(arr)); +}; + +/** + * @returns {string} + */ +export const createUniqueString = (): string => { + const timestamp = +new Date() + ''; + const num = (1 + Math.random()) * 65536; + const randomNum = parseInt(num + ''); + return (+(randomNum + timestamp)).toString(32); +}; + +/** + * Check if an element has a class + * @param ele + * @param {string} cls + * @returns {boolean} + */ +export const hasClass = (ele: HTMLElement, cls: string): boolean => { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); +}; + +/** + * Add class to element + * @param ele + * @param {string} cls + */ +export const addClass = (ele: HTMLElement, cls: string) => { + if (!hasClass(ele, cls)) ele.className += ' ' + cls; +}; + +/** + * Remove class from element + * @param ele + * @param {string} cls + */ +export const removeClass = (ele: HTMLElement, cls: string) => { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + ele.className = ele.className.replace(reg, ' '); + } +}; + +/** + * @param {string} path + * @returns {Boolean} + */ +export const isExternal = (path: string) => { + return /^(https?:|http?:|mailto:|tel:)/.test(path); +}; diff --git a/src/utils/jsencrypt.ts b/src/utils/jsencrypt.ts new file mode 100644 index 0000000..42de5a0 --- /dev/null +++ b/src/utils/jsencrypt.ts @@ -0,0 +1,21 @@ +import JSEncrypt from 'jsencrypt'; +// 密钥对生成 http://web.chacuo.net/netrsakeypair + +const publicKey = import.meta.env.VITE_APP_RSA_PUBLIC_KEY; + +// 前端不建议存放私钥 不建议解密数据 因为都是透明的意义不大 +const privateKey = import.meta.env.VITE_APP_RSA_PRIVATE_KEY; + +// 加密 +export const encrypt = (txt: string) => { + const encryptor = new JSEncrypt(); + encryptor.setPublicKey(publicKey); // 设置公钥 + return encryptor.encrypt(txt); // 对数据进行加密 +}; + +// 解密 +export const decrypt = (txt: string) => { + const encryptor = new JSEncrypt(); + encryptor.setPrivateKey(privateKey); // 设置私钥 + return encryptor.decrypt(txt); // 对数据进行解密 +}; diff --git a/src/utils/permission.ts b/src/utils/permission.ts new file mode 100644 index 0000000..eb3838a --- /dev/null +++ b/src/utils/permission.ts @@ -0,0 +1,51 @@ +import useUserStore from '@/store/modules/user'; + +/** + * 字符权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export const checkPermi = (value: any) => { + if (value && value instanceof Array && value.length > 0) { + const permissions = useUserStore().permissions; + const permissionDatas = value; + const all_permission = '*:*:*'; + + const hasPermission = permissions.some((permission) => { + return all_permission === permission || permissionDatas.includes(permission); + }); + + if (!hasPermission) { + return false; + } + return true; + } else { + console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`); + return false; + } +}; + +/** + * 角色权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export const checkRole = (value: any): boolean => { + if (value && value instanceof Array && value.length > 0) { + const roles = useUserStore().roles; + const permissionRoles = value; + const super_admin = 'admin'; + + const hasRole = roles.some((role) => { + return super_admin === role || permissionRoles.includes(role); + }); + + if (!hasRole) { + return false; + } + return true; + } else { + console.error(`need roles! Like checkRole="['admin','editor']"`); + return false; + } +}; diff --git a/src/utils/propTypes.ts b/src/utils/propTypes.ts new file mode 100644 index 0000000..24d861d --- /dev/null +++ b/src/utils/propTypes.ts @@ -0,0 +1,26 @@ +import { CSSProperties } from 'vue'; +import VueTypes, { createTypes, toValidableType, VueTypeValidableDef, VueTypesInterface } from 'vue-types'; + +type PropTypes = VueTypesInterface & { + readonly style: VueTypeValidableDef; + readonly fieldOption: VueTypeValidableDef>; +}; + +const propTypes = createTypes({ + func: undefined, + bool: undefined, + string: undefined, + number: undefined, + object: undefined, + integer: undefined +}) as PropTypes; + +export default class ProjectTypes extends VueTypes { + static get style() { + return toValidableType('style', { + type: [String, Object], + default: undefined + }); + } +} +export { propTypes }; diff --git a/src/utils/request.ts b/src/utils/request.ts new file mode 100644 index 0000000..f2a69f0 --- /dev/null +++ b/src/utils/request.ts @@ -0,0 +1,201 @@ +import axios, { AxiosResponse, InternalAxiosRequestConfig } from 'axios'; +import { useUserStore } from '@/store/modules/user'; +import { getToken } from '@/utils/auth'; +import { tansParams, blobValidate } from '@/utils/ruoyi'; +import cache from '@/plugins/cache'; +import { HttpStatus } from '@/enums/RespEnum'; +import { errorCode } from '@/utils/errorCode'; +import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'; +import FileSaver from 'file-saver'; +import { getLanguage } from '@/lang'; +import { encryptBase64, encryptWithAes, generateAesKey, decryptWithAes, decryptBase64 } from '@/utils/crypto'; +import { encrypt, decrypt } from '@/utils/jsencrypt'; + +const encryptHeader = 'encrypt-key'; +let downloadLoadingInstance: LoadingInstance; +// 是否显示重新登录 +export const isRelogin = { show: false }; +export const globalHeaders = () => { + return { + Authorization: 'Bearer ' + getToken(), + clientid: import.meta.env.VITE_APP_CLIENT_ID + }; +}; + +axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'; +axios.defaults.headers['clientid'] = import.meta.env.VITE_APP_CLIENT_ID; +// 创建 axios 实例 +const service = axios.create({ + baseURL: import.meta.env.VITE_APP_BASE_API, + timeout: 50000 +}); + +// 请求拦截器 +service.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + // 对应国际化资源文件后缀 + config.headers['Content-Language'] = getLanguage(); + + const isToken = config.headers?.isToken === false; + // 是否需要防止数据重复提交 + const isRepeatSubmit = config.headers?.repeatSubmit === false; + // 是否需要加密 + const isEncrypt = config.headers?.isEncrypt === 'true'; + + if (getToken() && !isToken) { + config.headers['Authorization'] = 'Bearer ' + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改 + } + // get请求映射params参数 + if (config.method === 'get' && config.params) { + let url = config.url + '?' + tansParams(config.params); + url = url.slice(0, -1); + config.params = {}; + config.url = url; + } + + if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { + const requestObj = { + url: config.url, + data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, + time: new Date().getTime() + }; + const sessionObj = cache.session.getJSON('sessionObj'); + if (sessionObj === undefined || sessionObj === null || sessionObj === '') { + cache.session.setJSON('sessionObj', requestObj); + } else { + const s_url = sessionObj.url; // 请求地址 + const s_data = sessionObj.data; // 请求数据 + const s_time = sessionObj.time; // 请求时间 + const interval = 500; // 间隔时间(ms),小于此时间视为重复提交 + if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { + const message = '数据正在处理,请勿重复提交'; + console.warn(`[${s_url}]: ` + message); + return Promise.reject(new Error(message)); + } else { + cache.session.setJSON('sessionObj', requestObj); + } + } + } + if (import.meta.env.VITE_APP_ENCRYPT === 'true') { + // 当开启参数加密 + if (isEncrypt && (config.method === 'post' || config.method === 'put')) { + // 生成一个 AES 密钥 + const aesKey = generateAesKey(); + config.headers[encryptHeader] = encrypt(encryptBase64(aesKey)); + config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey); + } + } + // FormData数据去请求头Content-Type + if (config.data instanceof FormData) { + delete config.headers['Content-Type']; + } + return config; + }, + (error: any) => { + return Promise.reject(error); + } +); + +// 响应拦截器 +service.interceptors.response.use( + (res: AxiosResponse) => { + if (import.meta.env.VITE_APP_ENCRYPT === 'true') { + // 加密后的 AES 秘钥 + const keyStr = res.headers[encryptHeader]; + // 加密 + if (keyStr != null && keyStr != '') { + const data = res.data; + // 请求体 AES 解密 + const base64Str = decrypt(keyStr); + // base64 解码 得到请求头的 AES 秘钥 + const aesKey = decryptBase64(base64Str.toString()); + // aesKey 解码 data + const decryptData = decryptWithAes(data, aesKey); + // 将结果 (得到的是 JSON 字符串) 转为 JSON + res.data = JSON.parse(decryptData); + } + } + // 未设置状态码则默认成功状态 + const code = res.data.code || HttpStatus.SUCCESS; + // 获取错误信息 + const msg = errorCode[code] || res.data.msg || errorCode['default']; + // 二进制数据则直接返回 + if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { + return res.data; + } + if (code === 401) { + // prettier-ignore + if (!isRelogin.show) { + isRelogin.show = true; + ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { + confirmButtonText: '重新登录', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + isRelogin.show = false; + useUserStore().logout().then(() => { + location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index'; + }); + }).catch(() => { + isRelogin.show = false; + }); + } + return Promise.reject('无效的会话,或者会话已过期,请重新登录。'); + } else if (code === HttpStatus.SERVER_ERROR) { + ElMessage({ message: msg, type: 'error' }); + return Promise.reject(new Error(msg)); + } else if (code === HttpStatus.WARN) { + ElMessage({ message: msg, type: 'warning' }); + return Promise.reject(new Error(msg)); + } else if (code !== HttpStatus.SUCCESS) { + ElNotification.error({ title: msg }); + return Promise.reject('error'); + } else { + return Promise.resolve(res.data); + } + }, + (error: any) => { + let { message } = error; + if (message == 'Network Error') { + message = '后端接口连接异常'; + } else if (message.includes('timeout')) { + message = '系统接口请求超时'; + } else if (message.includes('Request failed with status code')) { + message = '系统接口' + message.substr(message.length - 3) + '异常'; + } + ElMessage({ message: message, type: 'error', duration: 5 * 1000 }); + return Promise.reject(error); + } +); +// 通用下载方法 +export function download(url: string, params: any, fileName: string) { + downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' }); + // prettier-ignore + return service.post(url, params, { + transformRequest: [ + (params: any) => { + return tansParams(params); + } + ], + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + responseType: 'blob' + }).then(async (resp: any) => { + const isLogin = blobValidate(resp); + if (isLogin) { + const blob = new Blob([resp]); + FileSaver.saveAs(blob, fileName); + } else { + const resText = await resp.data.text(); + const rspObj = JSON.parse(resText); + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']; + ElMessage.error(errMsg); + } + downloadLoadingInstance.close(); + }).catch((r: any) => { + console.error(r); + ElMessage.error('下载文件出现错误,请联系管理员!'); + downloadLoadingInstance.close(); + }); +} +// 导出 axios 实例 +export default service; diff --git a/src/utils/ruoyi.ts b/src/utils/ruoyi.ts new file mode 100644 index 0000000..8efd12c --- /dev/null +++ b/src/utils/ruoyi.ts @@ -0,0 +1,251 @@ +// 日期格式化 +export function parseTime(time: any, pattern?: string) { + if (arguments.length === 0 || !time) { + return null; + } + const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'; + let date; + if (typeof time === 'object') { + date = time; + } else { + if (typeof time === 'string' && /^[0-9]+$/.test(time)) { + time = parseInt(time); + } else if (typeof time === 'string') { + time = time + .replace(new RegExp(/-/gm), '/') + .replace('T', ' ') + .replace(new RegExp(/\.[\d]{3}/gm), ''); + } + if (typeof time === 'number' && time.toString().length === 10) { + time = time * 1000; + } + date = new Date(time); + } + const formatObj: { [key: string]: any } = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + }; + return format.replace(/{(y|m|d|h|i|s|a)+}/g, (result: string, key: string) => { + let value = formatObj[key]; + // Note: getDay() returns 0 on Sunday + if (key === 'a') { + return ['日', '一', '二', '三', '四', '五', '六'][value]; + } + if (result.length > 0 && value < 10) { + value = '0' + value; + } + return value || 0; + }); +} + +/** + * 添加日期范围 + * @param params + * @param dateRange + * @param propName + */ +export const addDateRange = (params: any, dateRange: any[], propName?: string) => { + const search = params; + search.params = typeof search.params === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}; + dateRange = Array.isArray(dateRange) ? dateRange : []; + if (typeof propName === 'undefined') { + search.params['beginTime'] = dateRange[0]; + search.params['endTime'] = dateRange[1]; + } else { + search.params['begin' + propName] = dateRange[0]; + search.params['end' + propName] = dateRange[1]; + } + return search; +}; + +// 回显数据字典 +export const selectDictLabel = (datas: any, value: number | string) => { + if (value === undefined) { + return ''; + } + const actions: Array = []; + Object.keys(datas).some((key) => { + if (datas[key].value == '' + value) { + actions.push(datas[key].label); + return true; + } + }); + if (actions.length === 0) { + actions.push(value); + } + return actions.join(''); +}; + +// 回显数据字典(字符串数组) +export const selectDictLabels = (datas: any, value: any, separator: any) => { + if (value === undefined || value.length === 0) { + return ''; + } + if (Array.isArray(value)) { + value = value.join(','); + } + const actions: any[] = []; + const currentSeparator = undefined === separator ? ',' : separator; + const temp = value.split(currentSeparator); + Object.keys(value.split(currentSeparator)).some((val) => { + let match = false; + Object.keys(datas).some((key) => { + if (datas[key].value == '' + temp[val]) { + actions.push(datas[key].label + currentSeparator); + match = true; + } + }); + if (!match) { + actions.push(temp[val] + currentSeparator); + } + }); + return actions.join('').substring(0, actions.join('').length - 1); +}; + +// 字符串格式化(%s ) +export function sprintf(str: string) { + if (arguments.length !== 0) { + let flag = true, + i = 1; + str = str.replace(/%s/g, function () { + const arg = arguments[i++]; + if (typeof arg === 'undefined') { + flag = false; + return ''; + } + return arg; + }); + return flag ? str : ''; + } +} + +// 转换字符串,undefined,null等转化为"" +export const parseStrEmpty = (str: any) => { + if (!str || str == 'undefined' || str == 'null') { + return ''; + } + return str; +}; + +// 数据合并 +export const mergeRecursive = (source: any, target: any) => { + for (const p in target) { + try { + if (target[p].constructor == Object) { + source[p] = mergeRecursive(source[p], target[p]); + } else { + source[p] = target[p]; + } + } catch (e) { + source[p] = target[p]; + } + } + return source; +}; + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + */ +export const handleTree = (data: any[], id?: string, parentId?: string, children?: string): T[] => { + const config: { + id: string; + parentId: string; + childrenList: string; + } = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children' + }; + + const childrenListMap: any = {}; + const nodeIds: any = {}; + const tree: T[] = []; + + 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); + } + } + const adaptToChildrenList = (o: any) => { + 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); + } + } + }; + + for (const t of tree) { + adaptToChildrenList(t); + } + + return tree; +}; + +/** + * 参数处理 + * @param {*} params 参数 + */ +export const tansParams = (params: any) => { + let result = ''; + for (const propName of Object.keys(params)) { + const value = params[propName]; + const part = encodeURIComponent(propName) + '='; + if (value !== null && value !== '' && typeof value !== 'undefined') { + if (typeof value === 'object') { + for (const key of Object.keys(value)) { + if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') { + const params = propName + '[' + key + ']'; + const subPart = encodeURIComponent(params) + '='; + result += subPart + encodeURIComponent(value[key]) + '&'; + } + } + } else { + result += part + encodeURIComponent(value) + '&'; + } + } + } + return result; +}; + +// 返回项目路径 +export const getNormalPath = (p: string): string => { + if (p.length === 0 || !p || p === 'undefined') { + return p; + } + const res = p.replace('//', '/'); + if (res[res.length - 1] === '/') { + return res.slice(0, res.length - 1); + } + return res; +}; + +// 验证是否为blob格式 +export const blobValidate = (data: any) => { + return data.type !== 'application/json'; +}; + +export default { + handleTree +}; diff --git a/src/utils/scroll-to.ts b/src/utils/scroll-to.ts new file mode 100644 index 0000000..c2fa379 --- /dev/null +++ b/src/utils/scroll-to.ts @@ -0,0 +1,65 @@ +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; +}; + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +const requestAnimFrame = (function () { + return ( + window.requestAnimationFrame || + (window as any).webkitRequestAnimationFrame || + (window as any).mozRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + } + ); +})(); + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +const move = (amount: number) => { + document.documentElement.scrollTop = amount; + (document.body.parentNode as HTMLElement).scrollTop = amount; + document.body.scrollTop = amount; +}; + +const position = () => { + return document.documentElement.scrollTop || (document.body.parentNode as HTMLElement).scrollTop || document.body.scrollTop; +}; + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export const scrollTo = (to: number, duration: number, callback?: any) => { + const start = position(); + const change = to - start; + const increment = 20; + let currentTime = 0; + duration = typeof duration === 'undefined' ? 500 : duration; + const animateScroll = function () { + // increment the time + currentTime += increment; + // find the value with the quadratic in-out easing function + const val = easeInOutQuad(currentTime, start, change, duration); + // move the document.body + move(val); + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll); + } else { + if (callback && typeof callback === 'function') { + // the animation is done so lets callback + callback(); + } + } + }; + animateScroll(); +}; diff --git a/src/utils/theme.ts b/src/utils/theme.ts new file mode 100644 index 0000000..3936248 --- /dev/null +++ b/src/utils/theme.ts @@ -0,0 +1,52 @@ +// 处理主题样式 +export const handleThemeStyle = (theme: string) => { + document.documentElement.style.setProperty('--el-color-primary', theme); + for (let i = 1; i <= 9; i++) { + document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(theme, i / 10)}`); + } + for (let i = 1; i <= 9; i++) { + document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, `${getDarkColor(theme, i / 10)}`); + } +}; + +// hex颜色转rgb颜色 +export const hexToRgb = (str: string): string[] => { + str = str.replace('#', ''); + const hexs = str.match(/../g); + for (let i = 0; i < 3; i++) { + if (hexs) { + hexs[i] = String(parseInt(hexs[i], 16)); + } + } + return hexs ? hexs : []; +}; + +// rgb颜色转Hex颜色 +export const rgbToHex = (r: string, g: string, b: string) => { + const hexs = [Number(r).toString(16), Number(g).toString(16), Number(b).toString(16)]; + for (let i = 0; i < 3; i++) { + if (hexs[i].length == 1) { + hexs[i] = `0${hexs[i]}`; + } + } + return `#${hexs.join('')}`; +}; + +// 变浅颜色值 +export const getLightColor = (color: string, level: number) => { + const rgb = hexToRgb(color); + for (let i = 0; i < 3; i++) { + const s = (255 - Number(rgb[i])) * level + Number(rgb[i]); + rgb[i] = String(Math.floor(s)); + } + return rgbToHex(rgb[0], rgb[1], rgb[2]); +}; + +// 变深颜色值 +export const getDarkColor = (color: string, level: number) => { + const rgb = hexToRgb(color); + for (let i = 0; i < 3; i++) { + rgb[i] = String(Math.floor(Number(rgb[i]) * (1 - level))); + } + return rgbToHex(rgb[0], rgb[1], rgb[2]); +}; diff --git a/src/utils/validate.ts b/src/utils/validate.ts new file mode 100644 index 0000000..4d57894 --- /dev/null +++ b/src/utils/validate.ts @@ -0,0 +1,92 @@ +/** + * 判断url是否是http或https + * @returns {Boolean} + * @param url + */ +export const isHttp = (url: string): boolean => { + return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1; +}; + +/** + * 判断path是否为外链 + * @param {string} path + * @returns {Boolean} + */ +export const isExternal = (path: string) => { + return /^(https?:|mailto:|tel:)/.test(path); +}; + +/** + * @param {string} str + * @returns {Boolean} + */ +export const validUsername = (str: string) => { + const valid_map = ['admin', 'editor']; + return valid_map.indexOf(str.trim()) >= 0; +}; + +/** + * @param {string} url + * @returns {Boolean} + */ +export const validURL = (url: string) => { + const reg = + /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/; + return reg.test(url); +}; + +/** + * @param {string} str + * @returns {Boolean} + */ +export const validLowerCase = (str: string) => { + const reg = /^[a-z]+$/; + return reg.test(str); +}; + +/** + * @param {string} str + * @returns {Boolean} + */ +export const validUpperCase = (str: string) => { + const reg = /^[A-Z]+$/; + return reg.test(str); +}; + +/** + * @param {string} str + * @returns {Boolean} + */ +export const validAlphabets = (str: string) => { + const reg = /^[A-Za-z]+$/; + return reg.test(str); +}; + +/** + * @param {string} email + * @returns {Boolean} + */ +export const validEmail = (email: string) => { + const reg = + /^(([^<>()\]\\.,;:\s@"]+(\.[^<>()\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return reg.test(email); +}; + +/** + * @param {string} str + * @returns {Boolean} + */ +export const isString = (str: any) => { + return typeof str === 'string' || str instanceof String; +}; + +/** + * @param {Array} arg + * @returns {Boolean} + */ +export const isArray = (arg: string | string[]) => { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]'; + } + return Array.isArray(arg); +}; diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts new file mode 100644 index 0000000..d4dd8a8 --- /dev/null +++ b/src/utils/websocket.ts @@ -0,0 +1,139 @@ +/** + * @module initWebSocket 初始化 + * @module websocketonopen 连接成功 + * @module websocketonerror 连接失败 + * @module websocketclose 断开连接 + * @module resetHeart 重置心跳 + * @module sendSocketHeart 心跳发送 + * @module reconnect 重连 + * @module sendMsg 发送数据 + * @module websocketonmessage 接收数据 + * @module test 测试收到消息传递 + * @description socket 通信 + * @param {any} url socket地址 + * @param {any} websocket websocket 实例 + * @param {any} heartTime 心跳定时器实例 + * @param {number} socketHeart 心跳次数 + * @param {number} HeartTimeOut 心跳超时时间 + * @param {number} socketError 错误次数 + */ + +import { getToken } from '@/utils/auth'; +import { ElNotification } from 'element-plus'; +import useNoticeStore from '@/store/modules/notice'; + +let socketUrl: any = ''; // socket地址 +let websocket: any = null; // websocket 实例 +let heartTime: any = null; // 心跳定时器实例 +let socketHeart = 0 as number; // 心跳次数 +const HeartTimeOut = 10000; // 心跳超时时间 10000 = 10s +let socketError = 0 as number; // 错误次数 + +// 初始化socket +export const initWebSocket = (url: any) => { + if (import.meta.env.VITE_APP_WEBSOCKET === 'false') { + return; + } + socketUrl = url; + // 初始化 websocket + websocket = new WebSocket(url + '?Authorization=Bearer ' + getToken() + '&clientid=' + import.meta.env.VITE_APP_CLIENT_ID); + websocketonopen(); + websocketonmessage(); + websocketonerror(); + websocketclose(); + sendSocketHeart(); + return websocket; +}; + +// socket 连接成功 +export const websocketonopen = () => { + websocket.onopen = function () { + console.log('连接 websocket 成功'); + resetHeart(); + }; +}; + +// socket 连接失败 +export const websocketonerror = () => { + websocket.onerror = function (e: any) { + console.log('连接 websocket 失败', e); + }; +}; + +// socket 断开链接 +export const websocketclose = () => { + websocket.onclose = function (e: any) { + console.log('断开连接', e); + }; +}; + +// socket 重置心跳 +export const resetHeart = () => { + socketHeart = 0; + socketError = 0; + clearInterval(heartTime); + sendSocketHeart(); +}; + +// socket心跳发送 +export const sendSocketHeart = () => { + heartTime = setInterval(() => { + // 如果连接正常则发送心跳 + if (websocket.readyState == 1) { + // if (socketHeart <= 30) { + websocket.send( + JSON.stringify({ + type: 'ping' + }) + ); + socketHeart = socketHeart + 1; + } else { + // 重连 + reconnect(); + } + }, HeartTimeOut); +}; + +// socket重连 +export const reconnect = () => { + if (socketError <= 2) { + clearInterval(heartTime); + initWebSocket(socketUrl); + socketError = socketError + 1; + // eslint-disable-next-line prettier/prettier + console.log('socket重连', socketError); + } else { + // eslint-disable-next-line prettier/prettier + console.log('重试次数已用完'); + clearInterval(heartTime); + } +}; + +// socket 发送数据 +export const sendMsg = (data: any) => { + websocket.send(data); +}; + +// socket 接收数据 +export const websocketonmessage = () => { + websocket.onmessage = function (e: any) { + if (e.data.indexOf('heartbeat') > 0) { + resetHeart(); + } + if (e.data.indexOf('ping') > 0) { + return; + } + useNoticeStore().addNotice({ + message: e.data, + read: false, + time: new Date().toLocaleString() + }); + ElNotification({ + title: '消息', + message: e.data, + type: 'success', + duration: 3000 + }); + return e.data; + }; +}; diff --git a/src/views/demo/demo/index.vue b/src/views/demo/demo/index.vue new file mode 100644 index 0000000..523deff --- /dev/null +++ b/src/views/demo/demo/index.vue @@ -0,0 +1,254 @@ + + + diff --git a/src/views/demo/tree/index.vue b/src/views/demo/tree/index.vue new file mode 100644 index 0000000..921ddd9 --- /dev/null +++ b/src/views/demo/tree/index.vue @@ -0,0 +1,258 @@ + + + diff --git a/src/views/error/401.vue b/src/views/error/401.vue new file mode 100644 index 0000000..968550c --- /dev/null +++ b/src/views/error/401.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/src/views/error/404.vue b/src/views/error/404.vue new file mode 100644 index 0000000..b9a9ea3 --- /dev/null +++ b/src/views/error/404.vue @@ -0,0 +1,223 @@ + + + + + diff --git a/src/views/index.vue b/src/views/index.vue new file mode 100644 index 0000000..249a41b --- /dev/null +++ b/src/views/index.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/src/views/login.vue b/src/views/login.vue new file mode 100644 index 0000000..7e1394e --- /dev/null +++ b/src/views/login.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/src/views/monitor/admin/index.vue b/src/views/monitor/admin/index.vue new file mode 100644 index 0000000..04c63b5 --- /dev/null +++ b/src/views/monitor/admin/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/views/monitor/cache/index.vue b/src/views/monitor/cache/index.vue new file mode 100644 index 0000000..81bbf2d --- /dev/null +++ b/src/views/monitor/cache/index.vue @@ -0,0 +1,192 @@ + + + diff --git a/src/views/monitor/logininfor/index.vue b/src/views/monitor/logininfor/index.vue new file mode 100644 index 0000000..27c0a85 --- /dev/null +++ b/src/views/monitor/logininfor/index.vue @@ -0,0 +1,208 @@ + + + diff --git a/src/views/monitor/online/index.vue b/src/views/monitor/online/index.vue new file mode 100644 index 0000000..eb12364 --- /dev/null +++ b/src/views/monitor/online/index.vue @@ -0,0 +1,116 @@ + + + diff --git a/src/views/monitor/operlog/index.vue b/src/views/monitor/operlog/index.vue new file mode 100644 index 0000000..1442984 --- /dev/null +++ b/src/views/monitor/operlog/index.vue @@ -0,0 +1,305 @@ + + + diff --git a/src/views/monitor/snailjob/index.vue b/src/views/monitor/snailjob/index.vue new file mode 100644 index 0000000..89e3b23 --- /dev/null +++ b/src/views/monitor/snailjob/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/views/redirect/index.vue b/src/views/redirect/index.vue new file mode 100644 index 0000000..97a6556 --- /dev/null +++ b/src/views/redirect/index.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/views/register.vue b/src/views/register.vue new file mode 100644 index 0000000..c3d1930 --- /dev/null +++ b/src/views/register.vue @@ -0,0 +1,230 @@ + + + + + diff --git a/src/views/system/client/index.vue b/src/views/system/client/index.vue new file mode 100644 index 0000000..af8696b --- /dev/null +++ b/src/views/system/client/index.vue @@ -0,0 +1,316 @@ + + + diff --git a/src/views/system/config/index.vue b/src/views/system/config/index.vue new file mode 100644 index 0000000..138068e --- /dev/null +++ b/src/views/system/config/index.vue @@ -0,0 +1,261 @@ + + + diff --git a/src/views/system/dept/index.vue b/src/views/system/dept/index.vue new file mode 100644 index 0000000..71aeff1 --- /dev/null +++ b/src/views/system/dept/index.vue @@ -0,0 +1,319 @@ + + + diff --git a/src/views/system/dict/data.vue b/src/views/system/dict/data.vue new file mode 100644 index 0000000..33c7fc8 --- /dev/null +++ b/src/views/system/dict/data.vue @@ -0,0 +1,309 @@ + + + diff --git a/src/views/system/dict/index.vue b/src/views/system/dict/index.vue new file mode 100644 index 0000000..d44a04a --- /dev/null +++ b/src/views/system/dict/index.vue @@ -0,0 +1,246 @@ + + + diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue new file mode 100644 index 0000000..9e5b5c3 --- /dev/null +++ b/src/views/system/menu/index.vue @@ -0,0 +1,410 @@ + + + diff --git a/src/views/system/notice/index.vue b/src/views/system/notice/index.vue new file mode 100644 index 0000000..7d4bbe0 --- /dev/null +++ b/src/views/system/notice/index.vue @@ -0,0 +1,243 @@ + + + diff --git a/src/views/system/oss/config.vue b/src/views/system/oss/config.vue new file mode 100644 index 0000000..41766f6 --- /dev/null +++ b/src/views/system/oss/config.vue @@ -0,0 +1,334 @@ + + + diff --git a/src/views/system/oss/index.vue b/src/views/system/oss/index.vue new file mode 100644 index 0000000..a2f1f1b --- /dev/null +++ b/src/views/system/oss/index.vue @@ -0,0 +1,332 @@ + + + diff --git a/src/views/system/post/index.vue b/src/views/system/post/index.vue new file mode 100644 index 0000000..49b735b --- /dev/null +++ b/src/views/system/post/index.vue @@ -0,0 +1,360 @@ + + + diff --git a/src/views/system/role/authUser.vue b/src/views/system/role/authUser.vue new file mode 100644 index 0000000..6394804 --- /dev/null +++ b/src/views/system/role/authUser.vue @@ -0,0 +1,160 @@ + + + + + diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue new file mode 100644 index 0000000..bd84af3 --- /dev/null +++ b/src/views/system/role/index.vue @@ -0,0 +1,502 @@ + + + diff --git a/src/views/system/role/selectUser.vue b/src/views/system/role/selectUser.vue new file mode 100644 index 0000000..ec7a257 --- /dev/null +++ b/src/views/system/role/selectUser.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/src/views/system/tenant/index.vue b/src/views/system/tenant/index.vue new file mode 100644 index 0000000..fabe18d --- /dev/null +++ b/src/views/system/tenant/index.vue @@ -0,0 +1,349 @@ + + + diff --git a/src/views/system/tenantPackage/index.vue b/src/views/system/tenantPackage/index.vue new file mode 100644 index 0000000..55cc7d7 --- /dev/null +++ b/src/views/system/tenantPackage/index.vue @@ -0,0 +1,335 @@ + + + diff --git a/src/views/system/user/authRole.vue b/src/views/system/user/authRole.vue new file mode 100644 index 0000000..87d2334 --- /dev/null +++ b/src/views/system/user/authRole.vue @@ -0,0 +1,139 @@ + + + diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue new file mode 100644 index 0000000..c1bdcf9 --- /dev/null +++ b/src/views/system/user/index.vue @@ -0,0 +1,659 @@ + + + + + diff --git a/src/views/system/user/profile/index.vue b/src/views/system/user/profile/index.vue new file mode 100644 index 0000000..426fca8 --- /dev/null +++ b/src/views/system/user/profile/index.vue @@ -0,0 +1,122 @@ + + + diff --git a/src/views/system/user/profile/onlineDevice.vue b/src/views/system/user/profile/onlineDevice.vue new file mode 100644 index 0000000..8da96f6 --- /dev/null +++ b/src/views/system/user/profile/onlineDevice.vue @@ -0,0 +1,59 @@ + + + diff --git a/src/views/system/user/profile/resetPwd.vue b/src/views/system/user/profile/resetPwd.vue new file mode 100644 index 0000000..6d536da --- /dev/null +++ b/src/views/system/user/profile/resetPwd.vue @@ -0,0 +1,73 @@ + + + diff --git a/src/views/system/user/profile/thirdParty.vue b/src/views/system/user/profile/thirdParty.vue new file mode 100644 index 0000000..40daa86 --- /dev/null +++ b/src/views/system/user/profile/thirdParty.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/src/views/system/user/profile/userAvatar.vue b/src/views/system/user/profile/userAvatar.vue new file mode 100644 index 0000000..32b6f5c --- /dev/null +++ b/src/views/system/user/profile/userAvatar.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/src/views/system/user/profile/userInfo.vue b/src/views/system/user/profile/userInfo.vue new file mode 100644 index 0000000..d5b8a4c --- /dev/null +++ b/src/views/system/user/profile/userInfo.vue @@ -0,0 +1,69 @@ + + + diff --git a/src/views/tool/build/index.vue b/src/views/tool/build/index.vue new file mode 100644 index 0000000..ef0c079 --- /dev/null +++ b/src/views/tool/build/index.vue @@ -0,0 +1,3 @@ + diff --git a/src/views/tool/gen/basicInfoForm.vue b/src/views/tool/gen/basicInfoForm.vue new file mode 100644 index 0000000..5412088 --- /dev/null +++ b/src/views/tool/gen/basicInfoForm.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/views/tool/gen/editTable.vue b/src/views/tool/gen/editTable.vue new file mode 100644 index 0000000..bbb0889 --- /dev/null +++ b/src/views/tool/gen/editTable.vue @@ -0,0 +1,198 @@ + + + diff --git a/src/views/tool/gen/genInfoForm.vue b/src/views/tool/gen/genInfoForm.vue new file mode 100644 index 0000000..0ee9f1d --- /dev/null +++ b/src/views/tool/gen/genInfoForm.vue @@ -0,0 +1,295 @@ + + + diff --git a/src/views/tool/gen/importTable.vue b/src/views/tool/gen/importTable.vue new file mode 100644 index 0000000..50ec0ee --- /dev/null +++ b/src/views/tool/gen/importTable.vue @@ -0,0 +1,123 @@ + + + diff --git a/src/views/tool/gen/index.vue b/src/views/tool/gen/index.vue new file mode 100644 index 0000000..2618a20 --- /dev/null +++ b/src/views/tool/gen/index.vue @@ -0,0 +1,254 @@ + + + diff --git a/src/views/workflow/category/index.vue b/src/views/workflow/category/index.vue new file mode 100644 index 0000000..fd3bfc5 --- /dev/null +++ b/src/views/workflow/category/index.vue @@ -0,0 +1,263 @@ + + + diff --git a/src/views/workflow/formManage/index.vue b/src/views/workflow/formManage/index.vue new file mode 100644 index 0000000..5057b3a --- /dev/null +++ b/src/views/workflow/formManage/index.vue @@ -0,0 +1,243 @@ + + + diff --git a/src/views/workflow/leave/index.vue b/src/views/workflow/leave/index.vue new file mode 100644 index 0000000..23d5442 --- /dev/null +++ b/src/views/workflow/leave/index.vue @@ -0,0 +1,251 @@ + + + diff --git a/src/views/workflow/leave/leaveEdit.vue b/src/views/workflow/leave/leaveEdit.vue new file mode 100644 index 0000000..e978ad8 --- /dev/null +++ b/src/views/workflow/leave/leaveEdit.vue @@ -0,0 +1,273 @@ + + + diff --git a/src/views/workflow/model/index.vue b/src/views/workflow/model/index.vue new file mode 100644 index 0000000..e83cd94 --- /dev/null +++ b/src/views/workflow/model/index.vue @@ -0,0 +1,383 @@ + + + diff --git a/src/views/workflow/processDefinition/components/processPreview.vue b/src/views/workflow/processDefinition/components/processPreview.vue new file mode 100644 index 0000000..19a95df --- /dev/null +++ b/src/views/workflow/processDefinition/components/processPreview.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/views/workflow/processDefinition/index.vue b/src/views/workflow/processDefinition/index.vue new file mode 100644 index 0000000..1dacb80 --- /dev/null +++ b/src/views/workflow/processDefinition/index.vue @@ -0,0 +1,517 @@ + + + diff --git a/src/views/workflow/processInstance/index.vue b/src/views/workflow/processInstance/index.vue new file mode 100644 index 0000000..4f994af --- /dev/null +++ b/src/views/workflow/processInstance/index.vue @@ -0,0 +1,361 @@ + + + diff --git a/src/views/workflow/task/allTaskWaiting.vue b/src/views/workflow/task/allTaskWaiting.vue new file mode 100644 index 0000000..fe530b8 --- /dev/null +++ b/src/views/workflow/task/allTaskWaiting.vue @@ -0,0 +1,286 @@ + + + diff --git a/src/views/workflow/task/myDocument.vue b/src/views/workflow/task/myDocument.vue new file mode 100644 index 0000000..e8592f8 --- /dev/null +++ b/src/views/workflow/task/myDocument.vue @@ -0,0 +1,261 @@ + + + diff --git a/src/views/workflow/task/taskCopyList.vue b/src/views/workflow/task/taskCopyList.vue new file mode 100644 index 0000000..7784a08 --- /dev/null +++ b/src/views/workflow/task/taskCopyList.vue @@ -0,0 +1,150 @@ + + + diff --git a/src/views/workflow/task/taskFinish.vue b/src/views/workflow/task/taskFinish.vue new file mode 100644 index 0000000..155bd4f --- /dev/null +++ b/src/views/workflow/task/taskFinish.vue @@ -0,0 +1,136 @@ + + + diff --git a/src/views/workflow/task/taskWaiting.vue b/src/views/workflow/task/taskWaiting.vue new file mode 100644 index 0000000..dc6546a --- /dev/null +++ b/src/views/workflow/task/taskWaiting.vue @@ -0,0 +1,149 @@ + + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e4bf3ac --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", +// "useDefineForClassFields": true, + "moduleResolution": "bundler", + "strict": true, + "jsx": "preserve", + "strictNullChecks": false, + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "strictFunctionTypes": false, + "lib": ["esnext", "dom"], + "noImplicitAny": false, + "baseUrl": ".", + "allowJs": true, + "experimentalDecorators": true, + "paths": { + "@/*": ["src/*"] + }, + "compilerOptions": { + "types": ["element-plus/global"] + }, + "types": ["vite/client"], + "skipLibCheck": true, + "removeComments": true, + // 允许默认导入 + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*.ts", "src/**/*.vue", "src/types/**/*.d.ts", "vite.config.ts"], + "exclude": ["node_modules", "dist", "**/*.js", "**/*.md", "src/**/*.md"] +} diff --git a/uno.config.ts b/uno.config.ts new file mode 100644 index 0000000..0c60a22 --- /dev/null +++ b/uno.config.ts @@ -0,0 +1,33 @@ +import { + defineConfig, + presetAttributify, + presetIcons, + presetTypography, + presetUno, + presetWebFonts, + transformerDirectives, + transformerVariantGroup +} from 'unocss'; + +export default defineConfig({ + shortcuts: { + 'panel-title': + 'pb-[5px] font-sans leading-[1.1] font-medium text-base text-[#6379bb] border-b border-b-solid border-[var(--el-border-color-light)] mb-5 mt-0' + }, + theme: { + colors: { + primary: 'var(--el-color-primary)', + primary_dark: 'var(--el-color-primary-light-5)' + } + }, + presets: [ + presetUno(), + presetAttributify(), + presetIcons(), + presetTypography(), + presetWebFonts({ + fonts: {} + }) + ], + transformers: [transformerDirectives(), transformerVariantGroup()] +}); diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..eea111e --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,137 @@ +import { UserConfig, ConfigEnv, loadEnv, defineConfig } from 'vite'; + +import createPlugins from './vite/plugins'; + +import path from 'path'; +export default defineConfig(({ mode, command }: ConfigEnv): UserConfig => { + const env = loadEnv(mode, process.cwd()); + return { + // 部署生产环境和开发环境下的URL。 + // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上 + // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 + base: env.VITE_APP_CONTEXT_PATH, + resolve: { + alias: { + '~': path.resolve(__dirname, './'), + '@': path.resolve(__dirname, './src') + }, + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] + }, + // https://cn.vitejs.dev/config/#resolve-extensions + plugins: createPlugins(env, command === 'build'), + server: { + host: '0.0.0.0', + port: Number(env.VITE_APP_PORT), + open: true, + proxy: { + [env.VITE_APP_BASE_API]: { + target: 'http://localhost:8080', + changeOrigin: true, + ws: true, + rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '') + } + } + }, + css: { + preprocessorOptions: { + scss: { + javascriptEnabled: true + } + }, + postcss: { + plugins: [ + { + postcssPlugin: 'internal:charset-removal', + AtRule: { + charset: (atRule) => { + if (atRule.name === 'charset') { + atRule.remove(); + } + } + } + } + ] + } + }, + // 预编译 + optimizeDeps: { + include: [ + 'vue', + 'vue-router', + 'pinia', + 'axios', + '@vueuse/core', + 'path-to-regexp', + 'echarts', + 'vue-i18n', + '@vueup/vue-quill', + 'bpmn-js/lib/Viewer', + 'bpmn-js/lib/Modeler.js', + 'bpmn-js-properties-panel', + 'min-dash', + 'diagram-js/lib/navigation/movecanvas', + 'diagram-js/lib/navigation/zoomscroll', + 'bpmn-js/lib/features/palette/PaletteProvider', + 'bpmn-js/lib/features/context-pad/ContextPadProvider', + 'diagram-js/lib/draw/BaseRenderer', + 'tiny-svg', + 'image-conversion', + + 'element-plus/es/components/text/style/css', + 'element-plus/es/components/collapse-item/style/css', + 'element-plus/es/components/collapse/style/css', + 'element-plus/es/components/space/style/css', + 'element-plus/es/components/container/style/css', + 'element-plus/es/components/aside/style/css', + 'element-plus/es/components/main/style/css', + 'element-plus/es/components/header/style/css', + 'element-plus/es/components/button-group/style/css', + 'element-plus/es/components/radio-button/style/css', + 'element-plus/es/components/checkbox-group/style/css', + 'element-plus/es/components/form/style/css', + 'element-plus/es/components/form-item/style/css', + 'element-plus/es/components/button/style/css', + 'element-plus/es/components/input/style/css', + 'element-plus/es/components/input-number/style/css', + 'element-plus/es/components/switch/style/css', + 'element-plus/es/components/upload/style/css', + 'element-plus/es/components/menu/style/css', + 'element-plus/es/components/col/style/css', + 'element-plus/es/components/icon/style/css', + 'element-plus/es/components/row/style/css', + 'element-plus/es/components/tag/style/css', + 'element-plus/es/components/dialog/style/css', + 'element-plus/es/components/loading/style/css', + 'element-plus/es/components/radio/style/css', + 'element-plus/es/components/radio-group/style/css', + 'element-plus/es/components/popover/style/css', + 'element-plus/es/components/scrollbar/style/css', + 'element-plus/es/components/tooltip/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/sub-menu/style/css', + 'element-plus/es/components/menu-item/style/css', + 'element-plus/es/components/divider/style/css', + 'element-plus/es/components/card/style/css', + 'element-plus/es/components/link/style/css', + 'element-plus/es/components/breadcrumb/style/css', + 'element-plus/es/components/breadcrumb-item/style/css', + 'element-plus/es/components/table/style/css', + 'element-plus/es/components/tree-select/style/css', + 'element-plus/es/components/table-column/style/css', + 'element-plus/es/components/select/style/css', + 'element-plus/es/components/option/style/css', + 'element-plus/es/components/pagination/style/css', + 'element-plus/es/components/tree/style/css', + 'element-plus/es/components/alert/style/css', + 'element-plus/es/components/checkbox/style/css', + 'element-plus/es/components/date-picker/style/css', + 'element-plus/es/components/transfer/style/css', + 'element-plus/es/components/tabs/style/css', + 'element-plus/es/components/image/style/css', + 'element-plus/es/components/tab-pane/style/css' + ] + } + }; +}); diff --git a/vite/plugins/auto-import.ts b/vite/plugins/auto-import.ts new file mode 100644 index 0000000..072ef61 --- /dev/null +++ b/vite/plugins/auto-import.ts @@ -0,0 +1,24 @@ +import AutoImport from 'unplugin-auto-import/vite'; +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; +import IconsResolver from 'unplugin-icons/resolver'; + +export default (path: any) => { + return AutoImport({ + // 自动导入 Vue 相关函数 + imports: ['vue', 'vue-router', '@vueuse/core', 'pinia'], + eslintrc: { + enabled: false, + filepath: './.eslintrc-auto-import.json', + globalsPropValue: true + }, + resolvers: [ + // 自动导入 Element Plus 相关函数ElMessage, ElMessageBox... (带样式) + ElementPlusResolver(), + IconsResolver({ + prefix: 'Icon' + }) + ], + vueTemplate: true, // 是否在 vue 模板中自动导入 + dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'auto-imports.d.ts') + }); +}; diff --git a/vite/plugins/components.ts b/vite/plugins/components.ts new file mode 100644 index 0000000..336f5cf --- /dev/null +++ b/vite/plugins/components.ts @@ -0,0 +1,17 @@ +import Components from 'unplugin-vue-components/vite'; +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; +import IconsResolver from 'unplugin-icons/resolver'; + +export default (path: any) => { + return Components({ + resolvers: [ + // 自动导入 Element Plus 组件 + ElementPlusResolver(), + // 自动注册图标组件 + IconsResolver({ + enabledCollections: ['ep'] + }) + ], + dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'components.d.ts') + }); +}; diff --git a/vite/plugins/compression.ts b/vite/plugins/compression.ts new file mode 100644 index 0000000..aa8c779 --- /dev/null +++ b/vite/plugins/compression.ts @@ -0,0 +1,28 @@ +import compression from 'vite-plugin-compression'; + +export default (env: any) => { + const { VITE_BUILD_COMPRESS } = env; + const plugin: any[] = []; + if (VITE_BUILD_COMPRESS) { + const compressList = VITE_BUILD_COMPRESS.split(','); + if (compressList.includes('gzip')) { + // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件 + plugin.push( + compression({ + ext: '.gz', + deleteOriginFile: false + }) + ); + } + if (compressList.includes('brotli')) { + plugin.push( + compression({ + ext: '.br', + algorithm: 'brotliCompress', + deleteOriginFile: false + }) + ); + } + } + return plugin; +}; diff --git a/vite/plugins/i18n.ts b/vite/plugins/i18n.ts new file mode 100644 index 0000000..8777d1a --- /dev/null +++ b/vite/plugins/i18n.ts @@ -0,0 +1,6 @@ +import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'; +export default (path: any) => { + return VueI18nPlugin({ + include: [path.resolve(__dirname, '../../src/lang/**.json')] + }); +}; diff --git a/vite/plugins/icons.ts b/vite/plugins/icons.ts new file mode 100644 index 0000000..883f230 --- /dev/null +++ b/vite/plugins/icons.ts @@ -0,0 +1,8 @@ +import Icons from 'unplugin-icons/vite'; + +export default () => { + return Icons({ + // 自动安装图标库 + autoInstall: true + }); +}; diff --git a/vite/plugins/index.ts b/vite/plugins/index.ts new file mode 100644 index 0000000..0ec5b8d --- /dev/null +++ b/vite/plugins/index.ts @@ -0,0 +1,24 @@ +import vue from '@vitejs/plugin-vue'; +import createUnoCss from './unocss'; +import createAutoImport from './auto-import'; +import createComponents from './components'; +import createIcons from './icons'; +import createSvgIconsPlugin from './svg-icon'; +import createCompression from './compression'; +import createSetupExtend from './setup-extend'; +import createI18n from './i18n'; +import path from 'path'; + +export default (viteEnv: any, isBuild = false): [] => { + const vitePlugins: any = []; + vitePlugins.push(vue()); + vitePlugins.push(createUnoCss()); + vitePlugins.push(createAutoImport(path)); + vitePlugins.push(createComponents(path)); + vitePlugins.push(createCompression(viteEnv)); + vitePlugins.push(createIcons()); + vitePlugins.push(createSvgIconsPlugin(path, isBuild)); + vitePlugins.push(createSetupExtend()); + vitePlugins.push(createI18n(path)); + return vitePlugins; +}; diff --git a/vite/plugins/setup-extend.ts b/vite/plugins/setup-extend.ts new file mode 100644 index 0000000..ed3423f --- /dev/null +++ b/vite/plugins/setup-extend.ts @@ -0,0 +1,5 @@ +import setupExtend from 'unplugin-vue-setup-extend-plus/vite'; + +export default () => { + return setupExtend({}); +}; diff --git a/vite/plugins/svg-icon.ts b/vite/plugins/svg-icon.ts new file mode 100644 index 0000000..70296da --- /dev/null +++ b/vite/plugins/svg-icon.ts @@ -0,0 +1,10 @@ +import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'; +export default (path: any, isBuild: boolean) => { + return createSvgIconsPlugin({ + // 指定需要缓存的图标文件夹 + iconDirs: [path.resolve(path.resolve(__dirname, '../../src'), 'assets/icons/svg')], + // 指定symbolId格式 + symbolId: 'icon-[dir]-[name]', + svgoOptions: isBuild + }); +}; diff --git a/vite/plugins/unocss.ts b/vite/plugins/unocss.ts new file mode 100644 index 0000000..08e186b --- /dev/null +++ b/vite/plugins/unocss.ts @@ -0,0 +1,7 @@ +import UnoCss from 'unocss/vite'; + +export default () => { + return UnoCss({ + hmrTopLevelAwait: false // unocss默认是true,低版本浏览器是不支持的,启动后会报错 + }); +};