Browse Source

jeecgboot-vue 1.1.0-beta版本源码发布

zhangdaiscott 2 years ago
parent
commit
40c2fc9f64
96 changed files with 2605 additions and 881 deletions
  1. 1 1
      .env
  2. 2 0
      .gitignore
  3. 3 6
      README.md
  4. 6 0
      build/vite/plugin/index.ts
  5. 124 6
      package.json
  6. 3 3
      prettier.config.js
  7. 18 3
      src/api/sys/user.ts
  8. 4 0
      src/components/Application/src/search/useMenuSearch.ts
  9. 24 3
      src/components/Dropdown/src/Dropdown.vue
  10. 4 0
      src/components/Form/src/componentMap.ts
  11. 2 1
      src/components/Form/src/components/FormItem.vue
  12. 1 1
      src/components/Form/src/jeecg/components/JAreaLinkage.vue
  13. 5 0
      src/components/Form/src/jeecg/components/JCheckbox.vue
  14. 14 1
      src/components/Form/src/jeecg/components/JCodeEditor.vue
  15. 62 5
      src/components/Form/src/jeecg/components/JDictSelectTag.vue
  16. 1 0
      src/components/Form/src/jeecg/components/JInputPop.vue
  17. 213 0
      src/components/Form/src/jeecg/components/JOnlineSelectCascade.vue
  18. 19 6
      src/components/Form/src/jeecg/components/JSearchSelect.vue
  19. 15 1
      src/components/Form/src/jeecg/components/JSelectDept.vue
  20. 3 3
      src/components/Form/src/jeecg/components/JSelectInput.vue
  21. 3 3
      src/components/Form/src/jeecg/components/JSelectMultiple.vue
  22. 6 1
      src/components/Form/src/jeecg/components/JSelectPosition.vue
  23. 6 1
      src/components/Form/src/jeecg/components/JSelectRole.vue
  24. 7 2
      src/components/Form/src/jeecg/components/JSelectUser.vue
  25. 6 1
      src/components/Form/src/jeecg/components/JSelectUserByDept.vue
  26. 1 1
      src/components/Form/src/jeecg/components/JTreeSelect.vue
  27. 11 4
      src/components/Form/src/jeecg/components/JUpload/JUpload.vue
  28. 12 3
      src/components/Form/src/jeecg/components/base/JSelectBiz.vue
  29. 5 4
      src/components/Form/src/jeecg/components/modal/DeptSelectModal.vue
  30. 16 6
      src/components/Form/src/jeecg/hooks/useSelectBiz.ts
  31. 34 6
      src/components/Form/src/jeecg/hooks/useTreeBiz.ts
  32. 3 1
      src/components/Form/src/jeecg/props/props.ts
  33. 2 0
      src/components/Form/src/types/index.ts
  34. 108 0
      src/components/Form/src/utils/Area.ts
  35. 1 1
      src/components/Form/src/utils/areaDataUtil.js
  36. 7 1
      src/components/Form/src/utils/formUtils.ts
  37. 1 1
      src/components/Icon/src/IconPicker.vue
  38. 7 1
      src/components/SimpleMenu/src/SimpleMenu.vue
  39. 1 1
      src/components/Table/src/BasicTable.vue
  40. 6 3
      src/components/Table/src/hooks/useDataSource.ts
  41. 6 1
      src/components/Tinymce/src/Editor.vue
  42. 19 3
      src/components/chart/Pie.vue
  43. 19 3
      src/components/chart/StackBar.vue
  44. 3 1
      src/components/jeecg/JVxeTable/src/components/cells/JVxeDateCell.vue
  45. 3 1
      src/components/jeecg/JVxeTable/src/components/cells/JVxeSelectCell.vue
  46. 3 1
      src/components/jeecg/JVxeTable/src/components/cells/JVxeTextareaCell.vue
  47. 3 1
      src/components/jeecg/JVxeTable/src/components/cells/JVxeTimeCell.vue
  48. 17 0
      src/components/jeecg/JVxeTable/src/hooks/useData.ts
  49. 4 1
      src/components/jeecg/JVxeTable/src/hooks/useFinallyProps.ts
  50. 37 0
      src/components/jeecg/JVxeTable/src/hooks/useKeyboardEdit.ts
  51. 5 1
      src/components/jeecg/JVxeTable/src/hooks/useWebSocket.ts
  52. 1 1
      src/components/jeecg/JVxeTable/src/types/index.ts
  53. 32 7
      src/components/jeecg/JVxeTable/src/utils/enhancedUtils.ts
  54. 2 0
      src/components/jeecg/JVxeTable/src/vxe.data.ts
  55. 9 1
      src/components/registerGlobComp.ts
  56. 2 0
      src/enums/httpEnum.ts
  57. 9 1
      src/enums/jeecgEnum.ts
  58. 85 0
      src/hooks/jeecg/useAdaptiveWidth.ts
  59. 2 2
      src/hooks/setting/index.ts
  60. 12 4
      src/hooks/system/useListPage.ts
  61. 21 5
      src/hooks/system/useMethods.ts
  62. 5 0
      src/hooks/web/useWebSocket.ts
  63. 6 1
      src/layouts/default/header/components/user-dropdown/index.vue
  64. 5 0
      src/router/helper/routeHelper.ts
  65. 30 0
      src/utils/desform/customExpression.ts
  66. 24 16
      src/utils/encryption/signMd5Utils.js
  67. 2 2
      src/utils/env.ts
  68. 12 3
      src/utils/http/axios/index.ts
  69. 21 1
      src/utils/index.ts
  70. 5 2
      src/utils/lib/echarts.ts
  71. 143 0
      src/views/demo/jeecg/JVxeTableDemo/JVxeDemo5.vue
  72. 4 0
      src/views/demo/jeecg/JVxeTableDemo/index.vue
  73. 61 41
      src/views/demo/jeecg/JeecgComponents.vue
  74. 621 593
      src/views/demo/jeecg/jeecgComponents.data.ts
  75. 2 1
      src/views/demo/page/account/setting/BaseSetting.vue
  76. 120 33
      src/views/monitor/route/RouteModal.vue
  77. 10 1
      src/views/system/depart/components/DepartRuleTab.vue
  78. 6 1
      src/views/system/menu/menu.data.ts
  79. 1 1
      src/views/system/notice/DetailModal.vue
  80. 4 1
      src/views/system/notice/index.vue
  81. 1 1
      src/views/system/position/index.vue
  82. 4 0
      src/views/system/position/position.data.ts
  83. 57 0
      src/views/system/role/components/RoleIndexModal.vue
  84. 5 5
      src/views/system/role/components/RoleUserTable.vue
  85. 13 0
      src/views/system/role/index.vue
  86. 18 0
      src/views/system/role/role.api.ts
  87. 33 3
      src/views/system/role/role.data.ts
  88. 5 1
      src/views/system/tenant/TenantModal.vue
  89. 1 0
      src/views/system/tenant/index.vue
  90. 2 5
      src/views/system/tenant/tenant.data.ts
  91. 7 3
      src/views/system/user/UserDrawer.vue
  92. 11 1
      src/views/system/user/UserRecycleBinModal.vue
  93. 1 1
      src/views/system/user/user.data.ts
  94. 1 1
      types/config.d.ts
  95. 1 0
      types/store.d.ts
  96. 302 48
      yarn.lock

+ 1 - 1
.env

@@ -8,7 +8,7 @@ VITE_GLOB_APP_TITLE = JeecgBoot 企业级低代码平台
 VITE_GLOB_APP_SHORT_NAME = JeecgBootAdmin
 
 # 单点登录服务端地址
-VITE_GLOBE_APP_CAS_BASE_URL=http://cas.test.com:8443/cas
+VITE_GLOB_APP_CAS_BASE_URL=http://cas.test.com:8443/cas
 
 # 是否开启单点登录
 VITE_GLOB_APP_OPEN_SSO = false

+ 2 - 0
.gitignore

@@ -1,5 +1,6 @@
 node_modules
 .DS_Store
+.github
 dist
 .npmrc
 .cache
@@ -27,3 +28,4 @@ pnpm-debug.log*
 *.njsproj
 *.sln
 *.sw?
+/os_del.cmd

+ 3 - 6
README.md

@@ -1,13 +1,13 @@
 JEECG BOOT 低代码平台(Vue3前端)
 ===============
 
-当前最新版本: 1.0.0(20220321
+当前最新版本: 1.1.0-beta(预计发布时间:20220516
 
 
 ## 简介
 JeecgBoot-Vue3采用 Vue3.0、Vite、 Ant-Design-Vue、TypeScript 等新技术方案,包括二次封装组件、utils、hooks、动态菜单、权限校验、按钮级别权限控制等功能。
 是在 Vben-Admin 基础上研发的,适合于JeecgBoot的新版前端VUE3框架。
- 
+
 > 全新的VUE3技术栈,不只是追赶技术潮流,用了之后才能体会到Vue3的好处,的确比2更加适合大型项目。
 
 
@@ -37,7 +37,7 @@ JeecgBoot-Vue3采用 Vue3.0、Vite、 Ant-Design-Vue、TypeScript 等新技术
 ## 安装与使用
 
 
-  
+
 - Get the project code
 
 ```bash
@@ -224,6 +224,3 @@ yarn build
 
 
 
-
-
-

+ 6 - 0
build/vite/plugin/index.ts

@@ -15,6 +15,8 @@ import { configThemePlugin } from './theme';
 import { configImageminPlugin } from './imagemin';
 import { configSvgIconsPlugin } from './svgSprite';
 import { configHmrPlugin } from './hmr';
+import OptimizationPersist from 'vite-plugin-optimize-persist'
+import PkgConfig from 'vite-plugin-package-config'
 
 export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
     const {
@@ -78,5 +80,9 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
         vitePlugins.push(configPwaConfig(viteEnv));
     }
 
+    //vite-plugin-theme【解决vite首次打开界面加载慢问题】
+    vitePlugins.push(PkgConfig());
+    vitePlugins.push(OptimizationPersist());
+
     return vitePlugins;
 }

+ 124 - 6
package.json

@@ -10,7 +10,7 @@
     "bootstrap": "yarn install",
     "serve": "npm run dev",
     "dev": "vite",
-    "build": "cross-env NODE_ENV=production vite build && esno ./build/script/postBuild.ts",
+    "build": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=4096 vite build && esno ./build/script/postBuild.ts",
     "build:test": "cross-env vite build --mode test && esno ./build/script/postBuild.ts",
     "build:no-cache": "yarn clean:cache && npm run build",
     "report": "cross-env REPORT=true npm run build",
@@ -35,11 +35,11 @@
   },
   "dependencies": {
     "@iconify/iconify": "^2.0.4",
-    "@fullcalendar/core": "^5.8.0",
-    "@fullcalendar/daygrid": "^5.8.0",
-    "@fullcalendar/interaction": "^5.8.0",
-    "@fullcalendar/timegrid": "^5.8.0",
-    "@fullcalendar/vue3": "^5.8.0",
+    "@fullcalendar/core": "^5.10.1",
+    "@fullcalendar/daygrid": "^5.10.1",
+    "@fullcalendar/interaction": "^5.10.1",
+    "@fullcalendar/timegrid": "^5.10.1",
+    "@fullcalendar/vue3": "^5.10.1",
     "@vueuse/core": "^6.6.2",
     "@zxcvbn-ts/core": "^1.0.0-beta.0",
     "ant-design-vue": "2.2.8",
@@ -152,6 +152,8 @@
     "vite-plugin-html": "^2.1.0",
     "vite-plugin-imagemin": "^0.4.6",
     "vite-plugin-mock": "^2.9.6",
+    "vite-plugin-optimize-persist": "^0.1.2",
+    "vite-plugin-package-config": "^0.1.1",
     "vite-plugin-purge-icons": "^0.7.0",
     "vite-plugin-pwa": "^0.11.3",
     "vite-plugin-style-import": "^1.2.1",
@@ -178,5 +180,121 @@
   "homepage": "https://github.com/jeecgboot/jeecgboot-vue3",
   "engines": {
     "node": "^12 || >=14"
+  },
+  "vite": {
+    "optimizeDeps": {
+      "include": [
+        "@ant-design/colors",
+        "@ant-design/icons-vue",
+        "@fullcalendar/core/vdom",
+        "@fullcalendar/daygrid",
+        "@fullcalendar/interaction",
+        "@fullcalendar/timegrid",
+        "@fullcalendar/vue3",
+        "@vueuse/core",
+        "@vueuse/shared",
+        "@zxcvbn-ts/core",
+        "ant-design-vue",
+        "axios",
+        "china-area-data",
+        "clipboard",
+        "codemirror",
+        "codemirror/addon/fold/brace-fold.js",
+        "codemirror/addon/fold/comment-fold.js",
+        "codemirror/addon/fold/foldcode.js",
+        "codemirror/addon/fold/foldgutter.js",
+        "codemirror/addon/fold/indent-fold.js",
+        "codemirror/addon/hint/anyword-hint.js",
+        "codemirror/addon/hint/show-hint.js",
+        "codemirror/addon/selection/active-line.js",
+        "codemirror/mode/clike/clike.js",
+        "codemirror/mode/css/css.js",
+        "codemirror/mode/javascript/javascript.js",
+        "codemirror/mode/markdown/markdown.js",
+        "codemirror/mode/python/python.js",
+        "codemirror/mode/r/r.js",
+        "codemirror/mode/shell/shell.js",
+        "codemirror/mode/sql/sql.js",
+        "codemirror/mode/swift/swift.js",
+        "codemirror/mode/vue/vue.js",
+        "codemirror/mode/xml/xml.js",
+        "cron-parser",
+        "cropperjs",
+        "crypto-js/aes",
+        "crypto-js/enc-base64",
+        "crypto-js/enc-utf8",
+        "crypto-js/md5",
+        "crypto-js/mode-ecb",
+        "crypto-js/pad-pkcs7",
+        "dom-align",
+        "echarts",
+        "echarts/charts",
+        "echarts/components",
+        "echarts/core",
+        "echarts/renderers",
+        "intro.js",
+        "lodash-es",
+        "md5",
+        "moment",
+        "nprogress",
+        "path-to-regexp",
+        "pinia",
+        "print-js",
+        "qiankun",
+        "qrcode",
+        "qs",
+        "resize-observer-polyfill",
+        "showdown",
+        "sortablejs",
+        "tinymce/icons/default/icons",
+        "tinymce/plugins/advlist",
+        "tinymce/plugins/anchor",
+        "tinymce/plugins/autolink",
+        "tinymce/plugins/autosave",
+        "tinymce/plugins/code",
+        "tinymce/plugins/codesample",
+        "tinymce/plugins/contextmenu",
+        "tinymce/plugins/directionality",
+        "tinymce/plugins/fullscreen",
+        "tinymce/plugins/hr",
+        "tinymce/plugins/image",
+        "tinymce/plugins/insertdatetime",
+        "tinymce/plugins/link",
+        "tinymce/plugins/lists",
+        "tinymce/plugins/media",
+        "tinymce/plugins/nonbreaking",
+        "tinymce/plugins/noneditable",
+        "tinymce/plugins/pagebreak",
+        "tinymce/plugins/paste",
+        "tinymce/plugins/preview",
+        "tinymce/plugins/print",
+        "tinymce/plugins/save",
+        "tinymce/plugins/searchreplace",
+        "tinymce/plugins/spellchecker",
+        "tinymce/plugins/tabfocus",
+        "tinymce/plugins/table",
+        "tinymce/plugins/template",
+        "tinymce/plugins/textcolor",
+        "tinymce/plugins/textpattern",
+        "tinymce/plugins/visualblocks",
+        "tinymce/plugins/visualchars",
+        "tinymce/plugins/wordcount",
+        "tinymce/themes/silver",
+        "tinymce/tinymce",
+        "vditor",
+        "vite-plugin-theme/es/client",
+        "vite-plugin-theme/es/colorUtils",
+        "vue",
+        "vue-i18n",
+        "vue-print-nb-jeecg/src/printarea",
+        "vue-router",
+        "vue-types",
+        "vue3-drag-test4",
+        "vxe-table",
+        "vxe-table-plugin-antd",
+        "xe-utils",
+        "xlsx"
+      ]
+    }
   }
 }

+ 3 - 3
prettier.config.js

@@ -1,10 +1,10 @@
 module.exports = {
-  printWidth: 100,
+  printWidth: 200,
   tabWidth: 2,
   useTabs: false,
-  semi: true,
+  semi: true, //语句末尾使用分号
   vueIndentScriptAndStyle: true,
-  singleQuote: true,
+  singleQuote: true, // 使用单引号
   quoteProps: 'as-needed',
   bracketSpacing: true,
   trailingComma: 'es5',

+ 18 - 3
src/api/sys/user.ts

@@ -3,8 +3,13 @@ import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userMod
 
 import { ErrorMessageMode } from '/#/axios';
 import {useMessage} from "/@/hooks/web/useMessage";
+import {useUserStoreWithOut} from "/@/store/modules/user";
+import {setAuthCache} from "/@/utils/auth";
+import {TOKEN_KEY} from "/@/enums/cacheEnum";
+import {router} from "/@/router";
+import {PageEnum} from "/@/enums/pageEnum";
 
-const { createMessage, createErrorModal } = useMessage();
+const { createErrorModal } = useMessage();
 enum Api {
 
   Login = '/sys/login',
@@ -73,8 +78,18 @@ export function phoneLoginApi(params: LoginParams, mode: ErrorMessageMode = 'mod
 /**
  * @description: getUserInfo
  */
-export function getUserInfo() {
-  return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' });
+export  function getUserInfo() {
+  return  defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' }).catch((e)=>{
+      // update-begin--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面
+      if (e && e.message.includes('timeout')) {
+          //接口不通时跳转到登录界面
+          const userStore = useUserStoreWithOut();
+          userStore.setToken('');
+          setAuthCache(TOKEN_KEY, null);
+          router.push(PageEnum.BASE_LOGIN);
+      }
+      // update-end--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面
+  });
 }
 
 export function getPermCode() {

+ 4 - 0
src/components/Application/src/search/useMenuSearch.ts

@@ -55,6 +55,10 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
     }
     const reg = createSearchReg(unref(keyword));
     const filterMenu = filter(menuList, (item) => {
+      // 【issues/33】包含子菜单时,不添加到搜索队列
+      if (Array.isArray(item.children)) {
+        return false
+      }
       return reg.test(item.name) && !item.hideMenu;
     });
     searchResult.value = handlerSearchResult(filterMenu, reg);

+ 24 - 3
src/components/Dropdown/src/Dropdown.vue

@@ -1,15 +1,16 @@
 <template>
-  <a-dropdown :trigger="trigger" v-bind="$attrs">
+  <a-dropdown :class="[prefixCls]" :trigger="trigger" v-bind="$attrs">
     <span>
       <slot></slot>
     </span>
     <template #overlay>
-      <a-menu :selectedKeys="selectedKeys">
+      <a-menu :class="[`${prefixCls}-menu`]" :selectedKeys="selectedKeys">
         <template v-for="item in dropMenuList" :key="`${item.event}`">
           <a-menu-item
                   v-bind="getAttr(item.event)"
                   @click="handleClickMenu(item)"
                   :disabled="item.disabled"
+                  :class="[{'is-pop-confirm': item.popConfirm}]"
           >
             <a-popconfirm
                     v-if="popconfirm && item.popConfirm"
@@ -18,7 +19,7 @@
               <template #icon v-if="item.popConfirm.icon">
                 <Icon :icon="item.popConfirm.icon" />
               </template>
-              <div>
+              <div class="dropdown-event-area">
                 <Icon :icon="item.icon" v-if="item.icon" />
                 <span class="ml-1">{{ item.text }}</span>
               </div>
@@ -42,6 +43,7 @@
   import { Icon } from '/@/components/Icon';
   import { omit } from 'lodash-es';
   import { isFunction } from '/@/utils/is';
+  import {useDesign} from '/@/hooks/web/useDesign'
 
   const ADropdown = Dropdown;
   const AMenu = Menu;
@@ -49,6 +51,7 @@
   const AMenuDivider = Menu.Divider;
   const APopconfirm = Popconfirm;
 
+  const { prefixCls } = useDesign('basic-dropdown');
   const props = defineProps({
     popconfirm: Boolean,
     /**
@@ -94,3 +97,21 @@
 
   const getAttr = (key: string | number) => ({ key });
 </script>
+
+<style lang="less">
+@prefix-cls: ~'@{namespace}-basic-dropdown';
+
+.@{prefix-cls} {
+
+  // update-begin--author:sunjianlei---date:20220322---for: 【VUEN-180】更多下拉菜单,只有点到字上才有效,点到空白处什么都不会发生,体验不好
+  &-menu .ant-dropdown-menu-item.is-pop-confirm {
+    padding: 0;
+
+    .dropdown-event-area {
+      padding: 5px 12px;
+    }
+  }
+  // update-end--author:sunjianlei---date:20220322---for: 【VUEN-180】更多下拉菜单,只有点到字上才有效,点到空白处什么都不会发生,体验不好
+
+}
+</style>

+ 4 - 0
src/components/Form/src/componentMap.ts

@@ -56,9 +56,12 @@ import JSelectUserByDept from './jeecg/components/JSelectUserByDept.vue';
 import JUpload from './jeecg/components/JUpload/JUpload.vue'
 import JSearchSelect from './jeecg/components/JSearchSelect.vue'
 import JAddInput from './jeecg/components/JAddInput.vue'
+import {Time} from '/@/components/Time';
+import JOnlineSelectCascade from './jeecg/components/JOnlineSelectCascade.vue'
 
 const componentMap = new Map<ComponentType, Component>();
 
+componentMap.set('Time', Time);
 componentMap.set('Input', Input);
 componentMap.set('InputGroup', Input.Group);
 componentMap.set('InputPassword', Input.Password);
@@ -121,6 +124,7 @@ componentMap.set('JSelectUserByDept', JSelectUserByDept);
 componentMap.set('JUpload', JUpload);
 componentMap.set('JSearchSelect', JSearchSelect);
 componentMap.set('JAddInput', JAddInput);
+componentMap.set('JOnlineSelectCascade', JOnlineSelectCascade)
 
 export function add(compName: ComponentType, component: Component) {
   componentMap.set(compName, component);

+ 2 - 1
src/components/Form/src/components/FormItem.vue

@@ -346,7 +346,8 @@
                             wrapperCol={wrapperCol}
                         >
                             <div style="display:flex">
-                                <div style="flex:1;">{getContent()}</div>
+                                {/* author: sunjianlei for: 【VUEN-744】此处加上 width: 100%; 因为要防止组件宽度超出 FormItem */ }
+                                <div style="flex:1; width: 100%;">{getContent()}</div>
                                 {showSuffix && <span class="suffix">{getSuffix}</span>}
                             </div>
                         </Form.Item>

+ 1 - 1
src/components/Form/src/jeecg/components/JAreaLinkage.vue

@@ -4,7 +4,7 @@
 <script lang="ts">
     import {defineComponent, PropType, ref,reactive, watchEffect, computed, unref, watch, onMounted} from 'vue';
     import {Cascader} from 'ant-design-vue';
-    import {provinceAndCityData, regionData, provinceAndCityDataPlus, regionDataPlus, CodeToText, TextToCode} from "../../utils/areaDataUtil";
+    import {provinceAndCityData, regionData, provinceAndCityDataPlus, regionDataPlus} from "../../utils/areaDataUtil";
     import {useRuleFormItem} from "/@/hooks/component/useFormItem";
     import {propTypes} from "/@/utils/propTypes";
     import {useAttrs} from "/@/hooks/core/useAttrs";

+ 5 - 0
src/components/Form/src/jeecg/components/JCheckbox.vue

@@ -30,6 +30,11 @@
              */
             watchEffect(() => {
                 props.value && (checkboxArray.value = props.value ? props.value.split(",") : []);
+                //update-begin-author:taoyan date:20220401 for: 调用表单的 resetFields不会清空当前信息,界面显示上一次的数据
+                if(props.value === '' || props.value === undefined){
+                  checkboxArray.value = []
+                }
+                //update-end-author:taoyan date:20220401 for: 调用表单的 resetFields不会清空当前信息,界面显示上一次的数据
             });
             /**
              * 监听字典code

+ 14 - 1
src/components/Form/src/jeecg/components/JCodeEditor.vue

@@ -44,6 +44,7 @@
   import "codemirror/addon/hint/anyword-hint.js";
   import { useAttrs } from '/@/hooks/core/useAttrs';
   import {useDesign} from '/@/hooks/web/useDesign'
+  import {isJsonObjectString} from '/@/utils/is.ts'
 
   export default defineComponent({
     name: 'JCodeEditor',
@@ -86,6 +87,14 @@
         // 启用代码折叠相关功能:结束
         // 光标行高亮  
         styleActiveLine: true,
+          //代码格式化
+        extraKeys:{
+           Tab: function autoFormat(editor) {
+               //var totalLines = editor.lineCount();
+               //editor.autoFormatRange({line:0, ch:0}, {line:totalLines});
+              setValue(innerValue,false)
+           }
+        }
       });
       let innerValue = ''
       // 全屏状态
@@ -125,6 +134,9 @@
        * @param trigger 是否触发 change 事件
        */
       function setValue(value: string, trigger = true) {
+        if(value && isJsonObjectString(value)){
+           value = JSON.stringify(JSON.parse(value),null,2);
+        }
         coder?.setValue(value ?? '')
         innerValue = value
         trigger && emitChange(innerValue)
@@ -132,7 +144,8 @@
 
       //编辑器值修改事件
       function onChange(obj) {
-        innerValue = obj.getValue() ?? '';
+        let value = obj.getValue();
+        innerValue = value || '';
         if (props.value != innerValue) {
           emitChange(innerValue)
         }

+ 62 - 5
src/components/Form/src/jeecg/components/JDictSelectTag.vue

@@ -15,7 +15,14 @@
         </template>
     </a-radio-group>
 
-    <a-select v-else-if="compType===CompTypeEnum.Select" :placeholder="placeholder" v-bind="attrs" v-model:value="state" @change="handleChange">
+  <template v-else-if="compType===CompTypeEnum.Select">
+    <!-- 显示加载效果 -->
+    <a-input v-if="loadingEcho" readOnly placeholder="加载中…">
+      <template #prefix>
+        <LoadingOutlined/>
+      </template>
+    </a-input>
+    <a-select v-else :placeholder="placeholder" v-bind="attrs" v-model:value="state" :filterOption="handleFilterOption" :getPopupContainer="getPopupContainer" @change="handleChange">
         <a-select-option v-if="showChooseOption" :value="undefined">请选择</a-select-option>
         <template v-for="item in dictOptions" :key="`${item.value}`">
             <a-select-option :value="item.value">
@@ -25,6 +32,7 @@
             </a-select-option>
         </template>
     </a-select>
+  </template>
 </template>
 <script lang="ts">
     import {defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted} from 'vue';
@@ -34,9 +42,12 @@
     import {get, omit} from 'lodash-es';
     import {useRuleFormItem} from '/@/hooks/component/useFormItem';
     import {CompTypeEnum} from '/@/enums/CompTypeEnum.ts';
+    import {LoadingOutlined} from '@ant-design/icons-vue'
+
     export default defineComponent({
         name: 'JDictSelectTag',
         inheritAttrs: false,
+        components: {LoadingOutlined},
         props: {
             value: propTypes.oneOfType([
                 propTypes.string,
@@ -51,8 +62,14 @@
                 type: Function,
                 default: (node) => node.parentNode
             },
-          // 是否显示【请选择】选项
-          showChooseOption: propTypes.bool.def(true),
+            // 是否显示【请选择】选项
+            showChooseOption: propTypes.bool.def(true),
+            // 下拉项-online使用
+            options: {
+                type: Array,
+                default: [],
+                required: false
+            },
         },
         emits: ['options-change', 'change'],
         setup(props, {emit, refs}) {
@@ -61,6 +78,11 @@
             const attrs = useAttrs();
             const [state] = useRuleFormItem(props, 'value', 'change', emitData);
             const getBindValue = Object.assign({}, unref(props), unref(attrs));
+            // 是否正在加载回显数据
+            const loadingEcho = ref<boolean>(false)
+            // 是否是首次加载回显,只有首次加载,才会显示 loading
+            let isFirstLoadEcho = true
+
             //组件类型
             const compType = computed(() => {
                 return (!props.type || props.type === "list") ? 'select' : props.type;
@@ -69,8 +91,30 @@
              * 监听字典code
              */
             watchEffect(() => {
-                props.dictCode && initDictData();
+              if (props.dictCode) {
+                loadingEcho.value = isFirstLoadEcho
+                isFirstLoadEcho = false
+                initDictData().finally(() => {
+                  loadingEcho.value = isFirstLoadEcho
+                })
+              }
+                //update-begin-author:taoyan date: 如果没有提供dictCode 可以走options的配置--
+                if(!props.dictCode){
+                  dictOptions.value = props.options
+                }
+                //update-end-author:taoyan date: 如果没有提供dictCode 可以走options的配置--
+              
+            });
+            
+            //update-begin-author:taoyan date:20220404 for: 使用useRuleFormItem定义的value,会有一个问题,如果不是操作设置的值而是代码设置的控件值而不能触发change事件
+            // 此处添加空值的change事件,即当组件调用地代码设置value为''也能触发change事件
+            watch(()=>props.value, ()=>{
+              if(props.value===''){
+                emit('change', '')
+              }
             });
+            //update-end-author:taoyan date:20220404 for: 使用useRuleFormItem定义的value,会有一个问题,如果不是操作设置的值而是代码设置的控件值而不能触发change事件
+    
 
             async function initDictData() {
                 let {dictCode, stringToNumber} = props;
@@ -93,14 +137,27 @@
             emitData.value = [e?.target?.value || e];
           }
 
+          /** 用于搜索下拉框中的内容 */
+          function handleFilterOption(input, option) {
+            // 在 label 中搜索
+            let labelIf = option?.children[0]?.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            if (labelIf) {
+              return true
+            }
+            // 在 value 中搜索
+            return (option.value || '').toString().toLowerCase().indexOf(input.toLowerCase()) >= 0
+          }
+
             return {
                 state,
                 compType,
                 attrs,
+                loadingEcho,
                 getBindValue,
                 dictOptions,
                 CompTypeEnum,
-                handleChange
+                handleChange,
+                handleFilterOption,
             };
         },
     });

+ 1 - 0
src/components/Form/src/jeecg/components/JInputPop.vue

@@ -3,6 +3,7 @@
       trigger="contextmenu"
       v-model:visible="visible"
       :overlayClassName="`${prefixCls}-popover`"
+      :getPopupContainer="getPopupContainer"
       :placement="position">
     <template #title>
       <span>{{ title }}</span>

+ 213 - 0
src/components/Form/src/jeecg/components/JOnlineSelectCascade.vue

@@ -0,0 +1,213 @@
+<template>
+    <!-- 级联下拉框 form组件 暂且只在online使用 不对外提供api -->
+    <a-select :placeholder="placeholder" :value="selectedValue" @change="handleChange" allowClear style="width:100%">
+        <a-select-option v-for="(item, index) in dictOptions" :key="index" :value="item.store">
+             <span style="display: inline-block;width: 100%" :title=" item.label ">{{ item.label }}</span>
+        </a-select-option>
+    </a-select>
+</template>
+
+<script lang="ts">
+  import { defineComponent, watch, ref } from 'vue';
+  import { defHttp } from '/@/utils/http/axios';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  
+  /**获取下拉选项*/
+  const SELECT_OPTIONS_URL = '/online/cgform/api/querySelectOptions';
+  
+  export default defineComponent({
+    name: "JOnlineSelectCascade",
+    props:{
+      table:{ type: String, default: '' },
+      txt: { type: String, default: '' },
+      store: { type: String, default: '' },
+      idField: { type: String, default: '' },
+      pidField: { type: String, default: '' },
+      pidValue: { type: String, default: '-1' },
+      origin: { type: Boolean, default: false },
+      condition: { type: String, default: '' },
+      value:{ type: String, default: '' },
+      isNumber:{ type: Boolean, default: false },
+      placeholder: { type: String, default: '请选择' },
+    },
+    emits: [ 'change', 'next'],
+    setup(props, { emit }){
+      const { createMessage: $message } = useMessage()
+      // 选中值
+      const selectedValue = ref<any>('');
+      // 选项数组
+      const dictOptions = ref<any[]>([])
+      const optionsLoad = ref(true)
+      // 选项改变事件
+      function handleChange(value) {
+        console.log('handleChange', value)
+        // 这个value是 存储的值 实际还需要获取id值
+        let temp = value || ''
+        emit('change', temp)
+        valueChangeThenEmitNext(temp)
+      }
+
+      // 第一个节点 选项加载走condition
+      watch(()=>props.condition, (val)=>{
+        optionsLoad.value = true;
+        if(val){
+          loadOptions();
+        }
+      }, {immediate:true});
+
+      // 被联动节点 选项加载走pidValue
+      watch(()=>props.pidValue, (val)=>{
+        if(val === '-1'){
+          dictOptions.value = []
+        }else{
+          loadOptions();
+        }
+      });
+
+      // 值回显
+      watch(()=>props.value, (newVal, oldVal)=>{
+        console.log('值改变事件', newVal, oldVal)
+        if(!newVal){
+          // value不存在的时候--
+          selectedValue.value = []
+          if(oldVal){
+            // 如果oldVal存在, 需要往上抛事件
+            emit('change', '')
+            emit('next', '-1')
+          }
+        }else{
+          // value存在的时候
+          selectedValue.value = newVal
+        }
+        if(newVal && !oldVal){
+          // 有新值没有旧值 表单第一次加载赋值 需要往外抛一个事件 触发下级options的加载
+          handleFirstValueSetting(newVal)
+        }
+      }, {immediate:true});
+
+
+      /**
+       * 第一次加载赋值
+       */ 
+      async function handleFirstValueSetting(value){
+        if(props.idField === props.store){
+          // 如果id字段就是存储字段 那么可以不用调用请求
+          emit('next', value)
+        }else{
+          if(props.origin===true){
+            // 如果是联动组件的第一个组件,等待options加载完后从options中取值
+            await getSelfOptions()
+            valueChangeThenEmitNext(value)
+          }else{
+            // 如果是联动组件的后续组件,根据选中的value加载一遍数据
+            let arr = await loadValueText();
+            valueChangeThenEmitNext(value, arr)
+          }
+        }
+      }
+      
+      function loadOptions() {
+        let params = getQueryParams();
+        if(props.origin===true){
+          params['condition'] = props.condition
+        }else{
+          params['pidValue'] = props.pidValue
+        }
+        console.log("请求参数", params)
+        dictOptions.value = []
+        defHttp.get({ url: SELECT_OPTIONS_URL, params},{ isTransformResponse: false }).then(res=>{
+          if(res.success){
+            dictOptions.value = [...res.result]
+            console.log("请求结果", res.result, dictOptions)
+          }else{
+            $message.warning('联动组件数据加载失败,请检查配置!')
+          }
+        })
+      }
+      
+      function getQueryParams(){
+        let params = {
+          table: props.table, 
+          txt: props.txt,
+          key: props.store,
+          idField: props.idField,
+          pidField: props.pidField
+        }
+        return params;
+      }
+
+
+      function loadValueText() {
+        return new Promise(resolve => {
+          if(!props.value){
+            selectedValue.value = []
+            resolve([])
+          }else{
+            let params = getQueryParams();
+            if(props.isNumber === true){
+              params['condition'] = `${props.store} = ${props.value}`
+            }else{
+              params['condition'] = `${props.store} = '${props.value}'`
+            }
+            defHttp.get({ url: SELECT_OPTIONS_URL, params},{ isTransformResponse: false }).then(res=>{
+              if(res.success){
+                resolve(res.result)
+              }else{
+                $message.warning('联动组件数据加载失败,请检查配置!')
+                resolve([])
+              }
+            })
+          }
+        })
+      }
+
+      /**
+       * 获取下拉选项 
+       */
+      function getSelfOptions() {
+        return new Promise((resolve) => {
+          let index = 0;
+          (function next(index) {
+            if(index>10){
+              resolve([])
+            }
+            let arr = dictOptions.value
+            if (arr && arr.length>0) {
+              resolve(arr)
+            } else {
+              setTimeout(() => {
+                next(index++)
+              }, 300)
+            }
+          })(index)
+        })
+      }
+
+      /**
+       * 值改变后 需要往外抛事件 触发下级节点的选项改变
+       */
+      function valueChangeThenEmitNext(value, arr:any=[]){
+        if(value && value.length>0){
+          if(!arr || arr.length==0){
+            arr = dictOptions.value
+          }
+          let selected = arr.filter(item=>item.store===value)
+          if(selected && selected.length>0){
+            let id = selected[0].id
+            emit('next', id)
+          }
+        }
+      }
+      
+      return {
+        selectedValue,
+        dictOptions,
+        handleChange
+      }
+    }
+  })
+</script>
+
+<style scoped>
+
+</style>

+ 19 - 6
src/components/Form/src/jeecg/components/JSearchSelect.vue

@@ -76,6 +76,8 @@
             const selectedValue = ref([]);
             const selectedAsyncValue = ref([]);
             const lastLoad = ref(0);
+            // 是否根据value加载text
+            const loadSelectText = ref(true);
             /**
              * 监听字典code
              */
@@ -88,7 +90,7 @@
             watch(
                 () => props.value,
                 (val) => {
-                    if (val || val == 0) {
+                    if (val || val === 0) {
                         initSelectValue()
                     } else {
                         selectedValue.value = []
@@ -132,6 +134,12 @@
              * 初始化value
              */
             function initSelectValue() {
+                //update-begin-author:taoyan date:2022-4-24 for: 下拉搜索组件每次选中值会触发value的监听事件,触发此方法,但是实际不需要
+                if(loadSelectText.value===false){
+                    loadSelectText.value = true
+                    return
+                }
+                //update-end-author:taoyan date:2022-4-24 for: 下拉搜索组件每次选中值会触发value的监听事件,触发此方法,但是实际不需要
                 let {async, value, dict} = props;
                 if (async) {
                     if (!selectedAsyncValue || !selectedAsyncValue.key || selectedAsyncValue.key !== value) {
@@ -216,6 +224,7 @@
              *回调方法
              * */
             function callback(){
+                loadSelectText.value = false;
                 emit('change', unref(selectedValue));
                 emit('update:value', unref(selectedValue));
             }
@@ -227,13 +236,17 @@
             }
             
             function getParentContainer(node){
+              // update-begin-author:taoyan date:20220407 for: getPopupContainer一直有值 导致popContainer的逻辑永远走不进去,把它挪到前面判断
+              if(props.popContainer){
+                return document.querySelector(props.popContainer)
+              }else{
                 if(typeof props.getPopupContainer === 'function'){
-                    return props.getPopupContainer(node)
-                } else if(!props.popContainer){
-                    return node.parentNode
-                }else{
-                    return document.querySelector(props.popContainer)
+                  return props.getPopupContainer(node)
+                } else {
+                  return node.parentNode
                 }
+              }
+              // update-end-author:taoyan date:20220407 for: getPopupContainer一直有值 导致popContainer的逻辑永远走不进去,把它挪到前面判断
             }
             return {
                 attrs,

+ 15 - 1
src/components/Form/src/jeecg/components/JSelectDept.vue

@@ -1,7 +1,7 @@
 <!--部门选择组件-->
 <template>
     <div>
-        <JSelectBiz @handleOpen="handleOpen"  v-bind="attrs"/>
+        <JSelectBiz @handleOpen="handleOpen" :loading="loadingEcho" v-bind="attrs"/>
         <DeptSelectModal @register="regModal" @getSelectResult="setValue" v-bind="getBindValue" />
     </div>
 </template>
@@ -24,6 +24,8 @@
     inheritAttrs: false,
     props: {
       value: propTypes.oneOfType([propTypes.string, propTypes.array]),
+      // 是否允许多选,默认 true
+      multiple: propTypes.bool.def(true),
     },
     emits: ['options-change', 'change','select','update:value'],
     setup(props, { emit, refs }) {
@@ -38,10 +40,14 @@
       let selectValues = reactive<object>({
         value: []
       });
+      // 是否正在加载回显数据
+      const loadingEcho = ref<boolean>(false)
       //下发 selectOptions,xxxBiz组件接收
       provide('selectOptions', selectOptions);
       //下发 selectValues,xxxBiz组件接收
       provide('selectValues', selectValues);
+      //下发 loadingEcho,xxxBiz组件接收
+      provide('loadingEcho', loadingEcho);
 
       const tag = ref(false);
       const attrs = useAttrs();
@@ -51,6 +57,12 @@
        */
       watchEffect(() => {
         props.value && initValue();
+        // update-begin-author:taoyan date:20220401 for:调用表单的 resetFields不会清空当前部门信息,界面显示上一次的数据
+        if(props.value==='' || props.value===undefined){
+          state.value = []
+          selectValues.value = []
+        }
+        // update-end-author:taoyan date:20220401 for:调用表单的 resetFields不会清空当前部门信息,界面显示上一次的数据
       });
 
       /**
@@ -89,6 +101,7 @@
           state.value = value.split(',');
           selectValues.value = value.split(',');
         }
+     
       }
 
       /**
@@ -107,6 +120,7 @@
         attrs,
         selectOptions,
         selectValues,
+        loadingEcho,
         getBindValue,
         tag,
         regModal,

+ 3 - 3
src/components/Form/src/jeecg/components/JSelectInput.vue

@@ -6,7 +6,7 @@
 import { propTypes } from '/@/utils/propTypes'
 import { defineComponent, ref, watch, computed } from 'vue'
 
-// 可以输入的下拉框
+// 可以输入的下拉框(此组件暂时没有人用)
 export default defineComponent({
   name: 'JSelectInput',
   props: {
@@ -50,11 +50,11 @@ export default defineComponent({
     }
 
     // 删除无用的因搜索(用户输入)而创建的项
-    function deleteSearchAdd(value) {
+    function deleteSearchAdd(value = '') {
       let indexes: any[] = []
       options.value.forEach((option, index) => {
         if (option.searchAdd) {
-          if (option.value.toLocaleString() !== value.toLocaleString()) {
+          if ((option.value ?? '').toString() !== value.toString()) {
             indexes.push(index)
           }
         }

+ 3 - 3
src/components/Form/src/jeecg/components/JSelectMultiple.vue

@@ -1,6 +1,6 @@
 <!--字典下拉多选-->
 <template>
-    <a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder">
+    <a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder" allowClear :getPopupContainer="getParentContainer">
         <a-select-option v-for="(item,index) in dictOptions" :key="index" :getPopupContainer="getParentContainer" :value="item.value">
             {{ item.text || item.label }}
         </a-select-option>
@@ -61,7 +61,7 @@
     },
     emits: ['options-change', 'change', 'input', 'update:value'],
     setup(props, { emit, refs }) {
-      console.info(props);
+      //console.info(props);
       const emitData = ref<any[]>([]);
       const arrayValue = ref<any[]>(!props.value ? [] : props.value.split(props.spliter));
       const dictOptions = ref<any[]>([]);
@@ -109,7 +109,7 @@
         getDictItems(props.dictCode).then(res => {
           if (res) {
             dictOptions.value = res.map(item => ({ value: item.value, label: item.text }));
-            console.info('res', dictOptions.value);
+            //console.info('res', dictOptions.value);
           } else {
             console.error('getDictItems error: : ', res);
             dictOptions.value = [];

+ 6 - 1
src/components/Form/src/jeecg/components/JSelectPosition.vue

@@ -1,7 +1,7 @@
 <!--职务选择组件-->
 <template>
     <div>
-        <JSelectBiz @handleOpen="handleOpen" v-bind="attrs"></JSelectBiz>
+        <JSelectBiz @handleOpen="handleOpen" :loading="loadingEcho" v-bind="attrs"></JSelectBiz>
         <PositionSelectModal @register="regModal" @getSelectResult="setValue" v-bind="getBindValue"></PositionSelectModal>
     </div>
 </template>
@@ -54,10 +54,14 @@
                 value: [],
                 change: false
             });
+            // 是否正在加载回显数据
+            const loadingEcho = ref<boolean>(false)
             //下发 selectOptions,xxxBiz组件接收
             provide('selectOptions', selectOptions);
             //下发 selectValues,xxxBiz组件接收
             provide('selectValues', selectValues);
+            //下发 loadingEcho,xxxBiz组件接收
+            provide('loadingEcho', loadingEcho);
 
             const tag = ref(false);
             const attrs = useAttrs();
@@ -117,6 +121,7 @@
                 attrs,
                 selectOptions,
                 selectValues,
+                loadingEcho,
                 tag,
                 regModal,
                 setValue,

+ 6 - 1
src/components/Form/src/jeecg/components/JSelectRole.vue

@@ -1,7 +1,7 @@
 <!--角色选择组件-->
 <template>
     <div>
-        <JSelectBiz @handleOpen="handleOpen"  v-bind="attrs"></JSelectBiz>
+        <JSelectBiz @handleOpen="handleOpen" :loading="loadingEcho" v-bind="attrs"></JSelectBiz>
         <RoleSelectModal @register="regModal" @getSelectResult="setValue" v-bind="getBindValue"></RoleSelectModal>
     </div>
 </template>
@@ -54,10 +54,14 @@
                 value: [],
                 change: false
             });
+            // 是否正在加载回显数据
+            const loadingEcho = ref<boolean>(false)
             //下发 selectOptions,xxxBiz组件接收
             provide('selectOptions', selectOptions);
             //下发 selectValues,xxxBiz组件接收
             provide('selectValues', selectValues);
+            //下发 loadingEcho,xxxBiz组件接收
+            provide('loadingEcho', loadingEcho);
 
             const tag = ref(false);
             const attrs = useAttrs();
@@ -116,6 +120,7 @@
                 getBindValue,
                 selectOptions,
                 selectValues,
+                loadingEcho,
                 tag,
                 regModal,
                 setValue,

+ 7 - 2
src/components/Form/src/jeecg/components/JSelectUser.vue

@@ -1,7 +1,7 @@
 <!--用户选择组件-->
 <template>
     <div>
-        <JSelectBiz @handleOpen="handleOpen" v-bind="attrs"></JSelectBiz>
+        <JSelectBiz @handleOpen="handleOpen" :loading="loadingEcho" v-bind="attrs"></JSelectBiz>
         <UserSelectModal :rowKey="rowKey" @register="regModal" @getSelectResult="setValue"  v-bind="getBindValue"></UserSelectModal>
     </div>
 </template>
@@ -15,7 +15,7 @@
     import {useRuleFormItem} from "/@/hooks/component/useFormItem";
     import {useAttrs} from "/@/hooks/core/useAttrs";
     import {SelectTypes} from 'ant-design-vue/es/select';
-    import {unref} from 'vue';
+
     export default defineComponent({
         name: 'JSelectUser',
         components: {
@@ -55,10 +55,14 @@
                 value: [],
                 change: false
             });
+            // 是否正在加载回显数据
+            const loadingEcho = ref<boolean>(false)
             //下发 selectOptions,xxxBiz组件接收
             provide('selectOptions', selectOptions);
             //下发 selectValues,xxxBiz组件接收
             provide('selectValues', selectValues);
+            //下发 loadingEcho,xxxBiz组件接收
+            provide('loadingEcho', loadingEcho);
 
             const tag = ref(false);
             const attrs = useAttrs();
@@ -122,6 +126,7 @@
                 selectOptions,
                 getBindValue,
                 selectValues,
+                loadingEcho,
                 tag,
                 regModal,
                 setValue,

+ 6 - 1
src/components/Form/src/jeecg/components/JSelectUserByDept.vue

@@ -1,7 +1,7 @@
 <!--用户选择组件-->
 <template>
     <div>
-        <JSelectBiz @change="handleChange" @handleOpen="handleOpen"  v-bind="attrs"></JSelectBiz>
+        <JSelectBiz @change="handleChange" @handleOpen="handleOpen" :loading="loadingEcho" v-bind="attrs"></JSelectBiz>
         <UserSelectByDepModal :rowKey="rowKey" @register="regModal" @getSelectResult="setValue"  v-bind="getBindValue"></UserSelectByDepModal>
     </div>
 </template>
@@ -49,10 +49,14 @@
                 value: [],
                 change: false
             });
+            // 是否正在加载回显数据
+            const loadingEcho = ref<boolean>(false)
             //下发 selectOptions,xxxBiz组件接收
             provide('selectOptions', selectOptions);
             //下发 selectValues,xxxBiz组件接收
             provide('selectValues', selectValues);
+            //下发 loadingEcho,xxxBiz组件接收
+            provide('loadingEcho', loadingEcho);
 
             const tag = ref(false);
             const attrs = useAttrs();
@@ -118,6 +122,7 @@
                 selectOptions,
                 getBindValue,
                 selectValues,
+                loadingEcho,
                 tag,
                 regModal,
                 setValue,

+ 1 - 1
src/components/Form/src/jeecg/components/JTreeSelect.vue

@@ -38,7 +38,7 @@
         dict: propTypes.string.def('id'),
         parentCode: propTypes.string.def(''),
         pidField: propTypes.string.def('pid'),
-        pidValue: propTypes.string.def(''),
+        pidValue: propTypes.string.def('0'),
         hasChildField: propTypes.string.def(''),
         condition: propTypes.string.def(''),
         multiple: propTypes.bool.def(false),

+ 11 - 4
src/components/Form/src/jeecg/components/JUpload/JUpload.vue

@@ -6,7 +6,6 @@
         :action="uploadUrl"
         :fileList="fileList"
         :disabled="disabled"
-        :beforeUpload="onBeforeUpload"
         :remove="onRemove"
         v-bind="bindProps"
         @change="onFileChange"
@@ -87,7 +86,15 @@ const bindProps = computed(() => {
   bind.listType = isImageMode.value ? 'picture-card' : 'text'
   bind.class = [bind.class, { 'upload-disabled': props.disabled }]
   bind.data = { 'biz': props.bizPath, ...(bind.data) }
-  bind.beforeUpload = onBeforeUpload
+  //update-begin-author:taoyan date:20220407 for: 自定义beforeUpload return false,并不能中断上传过程
+  if(!bind.beforeUpload){
+    bind.beforeUpload = onBeforeUpload
+  }
+  //update-end-author:taoyan date:20220407 for: 自定义beforeUpload return false,并不能中断上传过程
+  // 如果当前是图片上传模式,就只能上传图片
+  if (isImageMode.value && !bind.accept) {
+    bind.accept = 'image/*'
+  }
   return bind
 })
 
@@ -150,8 +157,8 @@ function onAddActionsButton(event) {
   createApp(UploadItemActions, {
     element: uploadItem,
     fileList: fileList,
-    mover: true,
-    download: true,
+    mover: props.mover,
+    download: props.download,
     emitValue: emitValue,
   }).mount(div)
   actions[0].appendChild(div)

+ 12 - 3
src/components/Form/src/jeecg/components/base/JSelectBiz.vue

@@ -2,7 +2,13 @@
     <div>
         <a-row class="j-select-row" type="flex" :gutter="8">
             <a-col class="left" :class="{'full': !showButton}">
-                <a-select ref="select" v-model:value="selectValues.value" :placeholder="placeholder" :mode="multiple" :open="false" :disabled="disabled" :options="options" @change="handleChange" style="width: 100%;" @click="openModal(false)"></a-select>
+              <!-- 显示加载效果 -->
+              <a-input v-if="loading" readOnly placeholder="加载中…">
+                <template #prefix>
+                  <LoadingOutlined/>
+                </template>
+              </a-input>
+              <a-select v-else ref="select" v-model:value="selectValues.value" :placeholder="placeholder" :mode="multiple" :open="false" :disabled="disabled" :options="options" @change="handleChange" style="width: 100%;" @click="!disabled && openModal(false)"></a-select>
             </a-col>
             <a-col v-if="showButton" class="right">
                 <a-button type="primary" @click="openModal(true)" :disabled="disabled">选择</a-button>
@@ -14,10 +20,11 @@
     import {defineComponent, ref, inject, reactive} from 'vue';
     import {propTypes} from "/@/utils/propTypes";
     import {useAttrs} from "/@/hooks/core/useAttrs";
+    import {LoadingOutlined} from '@ant-design/icons-vue'
 
     export default defineComponent({
         name: 'JSelectBiz',
-        components: {},
+        components: {LoadingOutlined},
         inheritAttrs: false,
         props: {
             showButton: propTypes.bool.def(true),
@@ -30,7 +37,9 @@
             multiple: {
                 type: String,
                 default: 'multiple',
-            }
+            },
+            // 是否正在加载
+            loading: propTypes.bool.def(false),
         },
         emits: ['handleOpen','change'],
         setup(props, {emit, refs}) {

+ 5 - 4
src/components/Form/src/jeecg/components/modal/DeptSelectModal.vue

@@ -2,14 +2,14 @@
 <template>
     <div>
         <BasicModal v-bind="$attrs" @register="register" title="部门选择" width="500px"  @ok="handleOk" destroyOnClose @visible-change="visibleChange">
-            <BasicTree ref="treeRef" :treeData="treeData" :load-data="sync==false?null:onLoadData" v-bind="getBindValue" @select="onSelect"   @check="onCheck" :replaceFields="replaceFields" :checkedKeys="checkedKeys"/>
+            <BasicTree ref="treeRef" :treeData="treeData" :load-data="sync==false?null:onLoadData" v-bind="getBindValue" @select="onSelect" @check="onCheck" :replaceFields="replaceFields" :checkedKeys="checkedKeys" :checkStrictly="getCheckStrictly"/>
             <!--树操作部分-->
             <template #insertFooter>
                 <a-dropdown placement="topCenter">
                     <template #overlay>
                         <a-menu>
-                            <a-menu-item key="1" @click="checkALL(true)">全部勾选</a-menu-item>
-                            <a-menu-item key="2" @click="checkALL(false)">取消全选</a-menu-item>
+                            <a-menu-item v-if="multiple" key="1" @click="checkALL(true)">全部勾选</a-menu-item>
+                            <a-menu-item v-if="multiple" key="2" @click="checkALL(false)">取消全选</a-menu-item>
                             <a-menu-item key="3" @click="expandAll(true)">展开全部</a-menu-item>
                             <a-menu-item key="4" @click="expandAll(false)">折叠全部</a-menu-item>
                         </a-menu>
@@ -48,7 +48,7 @@
       const treeRef = ref<Nullable<TreeActionType>>(null);
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
       const queryUrl=props.sync?queryDepartTreeSync:queryTreeList;
-      const [{ visibleChange,checkedKeys, getSelectTreeData,onCheck,onLoadData,treeData,checkALL,expandAll,onSelect }] = useTreeBiz(treeRef,queryUrl, getBindValue);
+      const [{ visibleChange,checkedKeys, getCheckStrictly, getSelectTreeData,onCheck,onLoadData,treeData,checkALL,expandAll,onSelect }] = useTreeBiz(treeRef,queryUrl, getBindValue);
       const searchInfo = ref(props.params);
       const tree = ref([]);
       //替换treeNode中key字段为treeData中对应的字段
@@ -81,6 +81,7 @@
         checkedKeys,
         register,
         getBindValue,
+        getCheckStrictly,
         visibleChange,
         onLoadData,
       };

+ 16 - 6
src/components/Form/src/jeecg/hooks/useSelectBiz.ts

@@ -1,4 +1,4 @@
-import {inject, reactive, ref, watch,unref} from "vue";
+import {inject, reactive, ref, watch, unref, Ref} from 'vue'
 import {useMessage} from "/@/hooks/web/useMessage";
 
 export function useSelectBiz(getList,props) {
@@ -6,6 +6,8 @@ export function useSelectBiz(getList,props) {
     const selectOptions = inject('selectOptions', ref<Array<object>>([]));
     //接收已选择的值
     const selectValues = <object>inject('selectValues', reactive({}));
+    // 是否正在加载回显
+    const loadingEcho = inject<Ref<boolean>>('loadingEcho', ref(false))
     //数据集
     const dataSource = ref<Array<object>>([]);
     //已选择的值
@@ -14,19 +16,27 @@ export function useSelectBiz(getList,props) {
     const selectRows = ref<Array<object>>([]);
     //提示弹窗
     const $message = useMessage()
+    // 是否是首次加载回显,只有首次加载,才会显示 loading
+    let isFirstLoadEcho = true
+
     /**
      * 监听selectValues变化
      */
     watch(selectValues, () => {
         if (selectValues["change"] == false) {
-            let params={
-              code: selectValues["value"].join(",")
-            };
-            getDataSource(params, true).then();
+            //update-begin---author:wangshuai ---date:20220412  for:[VUEN-672]发文草稿箱编辑时拟稿人显示用户名------------
+            let params={};
+            params[props.rowKey] = selectValues["value"].join(",")
+            //update-end---author:wangshuai ---date:20220412  for:[VUEN-672]发文草稿箱编辑时拟稿人显示用户名--------------
+            loadingEcho.value = isFirstLoadEcho
+            isFirstLoadEcho = false
+            getDataSource(params, true).then().finally(()=>{
+                loadingEcho.value = isFirstLoadEcho
+            });
         }
         //设置列表默认选中
         checkedKeys["value"] = selectValues["value"]
-    })
+    }, {immediate: true})
 
     async function onSelectChange(selectedRowKeys: (string | number)[], selectRow) {
         checkedKeys.value = selectedRowKeys;

+ 34 - 6
src/components/Form/src/jeecg/hooks/useTreeBiz.ts

@@ -1,4 +1,5 @@
-import { inject, reactive, ref, unref, watch, nextTick } from 'vue';
+import type {Ref} from 'vue'
+import { inject, reactive, ref, computed, unref, watch, nextTick } from 'vue';
 import { TreeActionType } from '/@/components/Tree';
 import { listToTree } from '/@/utils/common/compUtils';
 
@@ -7,6 +8,8 @@ export function useTreeBiz(treeRef, getList, props) {
   const selectOptions = inject('selectOptions', ref<Array<object>>([]));
   //接收已选择的值
   const selectValues = <object>inject('selectValues', reactive({}));
+  // 是否正在加载回显
+  const loadingEcho = inject<Ref<boolean>>('loadingEcho', ref(false))
   //数据集
   const treeData = ref<Array<object>>([]);
   //已选择的值
@@ -15,15 +18,23 @@ export function useTreeBiz(treeRef, getList, props) {
   const selectRows = ref<Array<object>>([]);
   //是否是打开弹框模式
   const openModal = ref(false);
+  // 是否开启父子关联,如果不可以多选,就始终取消父子关联
+  const getCheckStrictly = computed(() => props.multiple ? props.checkStrictly : true)
+  // 是否是首次加载回显,只有首次加载,才会显示 loading
+  let isFirstLoadEcho = true
 
   /**
    * 监听selectValues变化
    */
-  watch(selectValues, () => {
-    if (openModal.value == false) {
-      onLoadData(null, selectValues['value'].join(',')).then();
+  watch(selectValues, ({value: values}: Recordable) => {
+    if (openModal.value == false && values.length > 0) {
+      loadingEcho.value = isFirstLoadEcho
+      isFirstLoadEcho = false
+      onLoadData(null, values.join(',')).finally(() => {
+        loadingEcho.value = false
+      })
     }
-  });
+  }, {immediate: true});
 
 
   /**
@@ -72,6 +83,20 @@ export function useTreeBiz(treeRef, getList, props) {
    */
   function onCheck(keys, info) {
     if(props.checkable==true) {
+      // 如果不能多选,就只保留最后一个选中的
+      if (!props.multiple) {
+        if (info.checked) {
+          //update-begin-author:taoyan date:20220408 for: 单选模式下,设定rowKey,无法选中数据-
+          checkedKeys.value = [info.node.eventKey]
+          let temp = info.checkedNodes.find(n => n.key === info.node.eventKey)
+          selectRows.value = [temp.props.node]
+          //update-end-author:taoyan date:20220408 for: 单选模式下,设定rowKey,无法选中数据-
+        } else {
+          checkedKeys.value = []
+          selectRows.value = []
+        }
+        return
+      }
       checkedKeys.value = props.checkStrictly?keys.checked:keys;
       const { checkedNodes } = info;
       let rows = <any[]>[];
@@ -105,7 +130,9 @@ export function useTreeBiz(treeRef, getList, props) {
     let startPid=''
     if (treeNode) {
       startPid=treeNode.eventKey;
-      params['pid'] = treeNode.eventKey;
+      //update-begin---author:wangshuai ---date:20220407  for:rowkey不设置成id,sync开启异步的时候,点击上级下级不显示------------
+      params['pid'] = treeNode.value;
+      //update-end---author:wangshuai ---date:20220407  for:rowkey不设置成id,sync开启异步的时候,点击上级下级不显示------------
     }
     if (ids) {
       startPid='';
@@ -215,6 +242,7 @@ export function useTreeBiz(treeRef, getList, props) {
       checkedKeys,
       selectRows,
       treeData,
+      getCheckStrictly,
       getSelectTreeData,
     },
   ];

+ 3 - 1
src/components/Form/src/jeecg/props/props.ts

@@ -75,5 +75,7 @@ export const treeProps = {
   //是否显示复选框
   checkable: propTypes.bool.def(true),
   //checkable 状态下节点选择完全受控(父子节点选中状态不再关联)
-  checkStrictly: propTypes.bool.def(false)
+  checkStrictly: propTypes.bool.def(false),
+  // 是否允许多选,默认 true
+  multiple: propTypes.bool.def(true),
 };

+ 2 - 0
src/components/Form/src/types/index.ts

@@ -140,4 +140,6 @@ export type ComponentType =
     | 'JUpload'
     | 'JSearchSelect'
     | 'JAddInput'
+    | 'Time'
+    | 'JOnlineSelectCascade'
   ;

+ 108 - 0
src/components/Form/src/utils/Area.ts

@@ -0,0 +1,108 @@
+import REGION_DATA from 'china-area-data'
+
+/**
+ * Area 属性all的类型
+ */
+interface PlainPca {
+  id:string,
+  text:string,
+  pid:string,
+  index:Number
+}
+
+/**
+ * 省市区工具类 -解决列表省市区组件的翻译问题
+ */
+class Area {
+  
+  all: PlainPca[];
+  
+  /**
+   * 构造器
+   * @param express
+   */
+  constructor(pcaa?) {
+    if(!pcaa){
+      pcaa = REGION_DATA;
+    }
+    let arr:PlainPca[] = []
+    const province = pcaa['86']
+    Object.keys(province).map(key=>{
+      arr.push({id:key, text:province[key], pid:'86', index:1});
+      const city = pcaa[key];
+      Object.keys(city).map(key2=>{
+        arr.push({id:key2, text:city[key2], pid:key, index:2});
+        const qu = pcaa[key2];
+        if(qu){
+          Object.keys(qu).map(key3=>{
+            arr.push({id:key3, text:qu[key3], pid:key2, index:3});
+          })
+        }
+      })
+    })
+    this.all = arr;
+  }
+
+  get pca(){
+    return this.all;
+  }
+
+  getCode(text){
+    if(!text || text.length==0){
+      return ''
+    }
+    for(let item of this.all){
+      if(item.text === text){
+        return item.id;
+      }
+    }
+  }
+
+  getText(code){
+    if(!code || code.length==0){
+      return ''
+    }
+    let arr = []
+    this.getAreaBycode(code, arr, 3);
+    return arr.join('/')
+  }
+
+  getRealCode(code){
+    let arr = []
+    this.getPcode(code, arr, 3)
+    return arr;
+  }
+
+  getPcode(id, arr, index){
+    for(let item of this.all){
+      if(item.id === id && item.index == index){
+        arr.unshift(id)
+        if(item.pid != '86'){
+          this.getPcode(item.pid, arr, --index)
+        }
+      }
+    }
+  }
+
+  getAreaBycode(code, arr, index){
+    for(let item of this.all){
+      if(item.id === code && item.index == index){
+        arr.unshift(item.text);
+        if(item.pid != '86'){
+          this.getAreaBycode(item.pid, arr, --index)
+        }
+
+      }
+    }
+  }
+}
+const jeecgAreaData = new Area();
+
+// 根据code找文本
+const getAreaTextByCode = function(code) {
+  return jeecgAreaData.getText(code)
+}
+
+export {
+  getAreaTextByCode
+}

+ 1 - 1
src/components/Form/src/utils/areaDataUtil.js

@@ -190,4 +190,4 @@ function getPcode(id, arr, index){
     }
 }
 //--end--@updateBy:liusq----date:20210922---for:省市区三级联动需求方法-----
-export {provinceAndCityData, regionData, provinceAndCityDataPlus, regionDataPlus, CodeToText, TextToCode,getDataByCode,provinceOptions,getRealCode}
+export {provinceAndCityData, regionData, provinceAndCityDataPlus, regionDataPlus,getDataByCode,provinceOptions,getRealCode}

+ 7 - 1
src/components/Form/src/utils/formUtils.ts

@@ -29,7 +29,13 @@ export function handleRangeTimeValue(props, values) {
     if (!field || !startTimeKey || !endTimeKey || !values[field]) {
       continue;
     }
-    const [startTime, endTime]: string[] = values[field];
+
+    // 【issues/I53G9Y】 日期区间组件有可能是字符串
+    let timeValue = values[field]
+    if (!Array.isArray(timeValue)) {
+      timeValue = timeValue.split(',')
+    }
+    const [startTime, endTime]: string[] = timeValue;
     values[startTimeKey] = dateUtil(startTime).format(format);
     values[endTimeKey] = dateUtil(endTime).format(format);
     Reflect.deleteProperty(values, field);

+ 1 - 1
src/components/Icon/src/IconPicker.vue

@@ -75,7 +75,7 @@
     </template>
   </a-input>
 </template>
-<script lang="ts" setup>
+<script lang="ts" setup name="icon-picker">
   import { ref, watchEffect, watch, unref } from 'vue';
   import { useDesign } from '/@/hooks/web/useDesign';
   import { ScrollContainer } from '/@/components/Container';

+ 7 - 1
src/components/SimpleMenu/src/SimpleMenu.vue

@@ -33,6 +33,8 @@
   import { openWindow } from '/@/utils';
 
   import { useOpenKeys } from './useOpenKeys';
+  import {URL_HASH_TAB} from '/@/utils'
+
   export default defineComponent({
     name: 'SimpleMenu',
     components: {
@@ -129,7 +131,11 @@
 
       async function handleSelect(key: string) {
         if (isUrl(key)) {
-          openWindow(key);
+          // update-begin--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开,原因是带了#号,需要替换一下
+          let url = key.replace(URL_HASH_TAB, '#')
+          openWindow(url)
+          // openWindow(key);
+          // update-begin--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开,原因是带了#号,需要替换一下
           return;
         }
         const { beforeClickFn } = props;

+ 1 - 1
src/components/Table/src/BasicTable.vue

@@ -142,7 +142,7 @@
                     getPaginationInfo,
                     setLoading,
                     setPagination,
-                    getFieldsValue: formActions.getFieldsValue,
+                    validate: formActions.validate,
                     clearSelectedRowKeys,
                 },
                 emit

+ 6 - 3
src/components/Table/src/hooks/useDataSource.ts

@@ -21,7 +21,9 @@ interface ActionType {
     getPaginationInfo: ComputedRef<boolean | PaginationProps>;
     setPagination: (info: Partial<PaginationProps>) => void;
     setLoading: (loading: boolean) => void;
-    getFieldsValue: () => Recordable;
+    // update-begin--author:sunjianlei---date:220220419---for:由于 getFieldsValue 返回的不是逗号分割的数据,所以改用 validate
+    validate: () => Recordable;
+    // update-end--author:sunjianlei---date:220220419---for:由于 getFieldsValue 返回的不是逗号分割的数据,所以改用 validate
     clearSelectedRowKeys: () => void;
     tableData: Ref<Recordable[]>;
 }
@@ -36,7 +38,7 @@ export function useDataSource(
         getPaginationInfo,
         setPagination,
         setLoading,
-        getFieldsValue,
+        validate,
         clearSelectedRowKeys,
         tableData,
     }: ActionType,
@@ -264,7 +266,8 @@ export function useDataSource(
 
             let params: Recordable = {
                 ...pageParams,
-                ...(useSearchForm ? getFieldsValue() : {}),
+                // 由于 getFieldsValue 返回的不是逗号分割的数据,所以改用 validate
+                ...(useSearchForm ? (await validate()) : {}),
                 ...searchInfo,
                 ...defSort,
                 ...(opt?.searchInfo ?? {}),

+ 6 - 1
src/components/Tinymce/src/Editor.vue

@@ -205,9 +205,14 @@
                 const { options } = props;
                 const getdDisabled = options && Reflect.get(options, 'readonly');
                 const editor = unref(editorRef);
+                // update-begin-author:taoyan date:20220407 for: 设置disabled,图片上传没有被禁用
                 if (editor) {
-                    editor.setMode(getdDisabled ? 'readonly' : 'design');
+                    editor.setMode(getdDisabled||(attrs.disabled === true)  ? 'readonly' : 'design');
                 }
+                if(attrs.disabled === true){
+                  return true;
+                }
+                // update-end-author:taoyan date:20220407 for: 设置disabled,图片上传没有被禁用
                 return getdDisabled ?? false;
             });
 

+ 19 - 3
src/components/chart/Pie.vue

@@ -2,7 +2,7 @@
   <div ref="chartRef" :style="{ height, width }"></div>
 </template>
 <script lang="ts">
-  import { defineComponent, PropType, ref, Ref,watchEffect,reactive } from 'vue';
+  import { defineComponent, PropType, ref, Ref,watchEffect,reactive,watch } from 'vue';
   import { useECharts } from '/@/hooks/web/useECharts';
 
   export default defineComponent({
@@ -12,6 +12,10 @@
         type: Array,
         default: () => ([]),
       },
+      size: {
+        type: Object,
+        default: ()=>{}
+      },
       option:{
         type: Object,
         default: () => ({}),
@@ -28,7 +32,7 @@
       emits: ['click'],
       setup(props, {emit}) {
       const chartRef = ref<HTMLDivElement | null>(null);
-      const { setOptions, getInstance } = useECharts(chartRef as Ref<HTMLDivElement>);
+      const { setOptions, getInstance,resize } = useECharts(chartRef as Ref<HTMLDivElement>);
         const option = reactive({
             tooltip: {
                 formatter: '{b} ({c})',
@@ -52,13 +56,25 @@
         watchEffect(() => {
             props.chartData && initCharts();
         });
-
+          /**
+           * 监听拖拽大小变化
+           */
+        watch(
+          () => props.size,
+          () => {
+              resize();
+          },
+          {
+              immediate: true,
+          }
+        );
         function initCharts(){
             if(props.option){
                 Object.assign(option,props.option);
             }
             option.series[0].data = props.chartData;
             setOptions(option);
+            resize();
             getInstance()?.off('click', onClick)
             getInstance()?.on('click', onClick)
         }

+ 19 - 3
src/components/chart/StackBar.vue

@@ -2,7 +2,7 @@
     <div ref="chartRef" :style="{ height, width }"></div>
 </template>
 <script lang="ts">
-    import {defineComponent, PropType, ref, Ref, reactive, watchEffect} from 'vue';
+    import {defineComponent, PropType, ref, Ref, reactive, watchEffect,watch} from 'vue';
     import {useECharts} from '/@/hooks/web/useECharts';
 
     export default defineComponent({
@@ -13,6 +13,10 @@
                 default: () => ([]),
                 required: true
             },
+            size: {
+                type: Object,
+                default: ()=>{}
+            },
             option: {
                 type: Object,
                 default: () => ({}),
@@ -32,7 +36,7 @@
         },
         setup(props) {
             const chartRef = ref<HTMLDivElement | null>(null);
-            const {setOptions, echarts} = useECharts(chartRef as Ref<HTMLDivElement>);
+            const {setOptions, echarts,resize} = useECharts(chartRef as Ref<HTMLDivElement>);
             const option = reactive({
                 tooltip: {
                     trigger: 'axis',
@@ -63,7 +67,19 @@
             watchEffect(() => {
                 props.chartData && initCharts();
             });
-
+            /**
+             * 监听拖拽大小变化
+             */
+            watch(
+                () => props.size,
+                () => {
+                    console.log("props.size",props.size)
+                    resize();
+                },
+                {
+                    immediate: true,
+                }
+            );
             function initCharts() {
                 if (props.option) {
                     Object.assign(option, props.option);

+ 3 - 1
src/components/jeecg/JVxeTable/src/components/cells/JVxeDateCell.vue

@@ -58,10 +58,12 @@ export default defineComponent({
   // 【组件增强】注释详见:JVxeComponent.Enhanced
   enhanced: {
     aopEvents: {
-      editActived({ $event }) {
+      editActived({$event, row, column}) {
         dispatchEvent({
           $event,
+          row, column,
           props: this.props,
+          instance: this,
           className: '.ant-calendar-picker',
           isClick: false,
           handler: (el) => el.children[0].click(),

+ 3 - 1
src/components/jeecg/JVxeTable/src/components/cells/JVxeSelectCell.vue

@@ -175,10 +175,12 @@ export default defineComponent({
   // 【组件增强】注释详见:JVxeComponent.Enhanced
   enhanced: {
     aopEvents: {
-      editActived({ $event }) {
+      editActived({$event, row, column}) {
         dispatchEvent({
           $event,
+          row, column,
           props: this.props,
+          instance: this,
           className: '.ant-select .ant-select-selection-search-input',
           isClick: false,
           handler: (el) => el.focus(),

+ 3 - 1
src/components/jeecg/JVxeTable/src/components/cells/JVxeTextareaCell.vue

@@ -31,12 +31,14 @@ export default defineComponent({
       autofocus: '.ant-input',
     },
     aopEvents: {
-      editActived({$event, column}) {
+      editActived({$event, row, column}) {
         // 是否默认打开右侧弹窗
         if ((column.params.defaultOpen ?? false)) {
           dispatchEvent({
             $event,
+            row, column,
             props: this.props,
+            instance: this,
             className: '.ant-input-suffix .app-iconify',
             isClick: true,
           })

+ 3 - 1
src/components/jeecg/JVxeTable/src/components/cells/JVxeTimeCell.vue

@@ -57,10 +57,12 @@ export default defineComponent({
   // 【组件增强】注释详见:JVxeComponent.Enhanced
   enhanced: {
     aopEvents: {
-      editActived({ $event }) {
+      editActived({ $event, row, column }) {
         dispatchEvent({
           $event,
+          row, column,
           props: this.props,
+          instance: this,
           className: '.ant-time-picker-input',
           isClick: true,
         })

+ 17 - 0
src/components/jeecg/JVxeTable/src/hooks/useData.ts

@@ -41,6 +41,23 @@ export function useData(props: JVxeTableProps): JVxeDataProps {
       // },
       radioConfig: { highlight: true },
       checkboxConfig: { highlight: true },
+      mouseConfig: {selected: false},
+      keyboardConfig: {
+        // 删除键功能
+        isDel: false,
+        // Esc键关闭编辑功能
+        isEsc: true,
+        // Tab 键功能
+        isTab: true,
+        // 任意键进入编辑(功能键除外)
+        isEdit: true,
+        // 方向键功能
+        isArrow: true,
+        // 回车键功能
+        isEnter: true,
+        // 如果功能被支持,用于 column.type=checkbox|radio,开启空格键切换复选框或单选框状态功能
+        isChecked: true,
+      },
     }),
     selectedRows: ref<any[]>([]),
     selectedRowIds: ref<string[]>([]),

+ 4 - 1
src/components/jeecg/JVxeTable/src/hooks/useFinallyProps.ts

@@ -2,10 +2,13 @@ import { unref, computed } from 'vue'
 import { merge } from 'lodash-es'
 import { isArray } from '/@/utils/is'
 import { useAttrs } from '/@/hooks/core/useAttrs'
+import {useKeyboardEdit} from '../hooks/useKeyboardEdit'
 import { JVxeDataProps, JVxeTableMethods, JVxeTableProps } from '../types'
 
 export function useFinallyProps(props: JVxeTableProps, data: JVxeDataProps, methods: JVxeTableMethods) {
   const attrs = useAttrs()
+  // vxe 键盘操作配置
+  const {keyboardEditConfig} = useKeyboardEdit(props)
   // vxe 最终 editRules
   const vxeEditRules = computed(() => merge({}, props.editRules, data.innerEditRules))
   // vxe 最终 events
@@ -63,7 +66,7 @@ export function useFinallyProps(props: JVxeTableProps, data: JVxeDataProps, meth
       checkboxConfig: {
         checkMethod: methods.handleCheckMethod,
       },
-    }, unref(vxeEvents))
+    }, unref(vxeEvents), unref(keyboardEditConfig))
   })
   return {
     vxeProps,

+ 37 - 0
src/components/jeecg/JVxeTable/src/hooks/useKeyboardEdit.ts

@@ -0,0 +1,37 @@
+/*
+* JVxeTable 键盘操作 
+*/
+import type {VxeTablePropTypes} from 'vxe-table'
+import type {JVxeTableProps} from '../types'
+import {computed} from 'vue'
+
+/**
+ * JVxeTable 键盘操作
+ *
+ * @param props
+ */
+export function useKeyboardEdit(props: JVxeTableProps) {
+  // 是否开启了键盘操作
+  const enabledKeyboard = computed(() => props.keyboardEdit ?? false)
+  // 重写 keyboardConfig
+  const keyboardConfig: VxeTablePropTypes.KeyboardConfig = {
+    editMethod({row, column, $table}) {
+      // 重写默认的覆盖式,改为追加式
+      $table.setActiveCell(row, column)
+      return true
+    },
+  }
+  // 键盘操作配置
+  const keyboardEditConfig = computed(() => {
+    return {
+      mouseConfig: {
+        selected: enabledKeyboard.value,
+      },
+      keyboardConfig,
+    }
+  })
+
+  return {
+    keyboardEditConfig,
+  }
+}

+ 5 - 1
src/components/jeecg/JVxeTable/src/hooks/useWebSocket.ts

@@ -4,6 +4,7 @@ import { useGlobSetting } from '/@/hooks/setting'
 import { useUserStore } from '/@/store/modules/user'
 import { JVxeDataProps, JVxeTableMethods, JVxeTableProps } from '../types'
 import { isArray } from '/@/utils/is'
+import { getToken } from '/@/utils/auth';
 
 // vxe socket
 const vs = {
@@ -55,7 +56,10 @@ const vs = {
       const domainURL = useGlobSetting().uploadUrl!
       const domain = domainURL.replace('https://', 'wss://').replace('http://', 'ws://')
       const url = `${domain}/vxeSocket/${userId}/${this.pageId}`
-      this.ws = new WebSocket(url)
+      //update-begin-author:taoyan date:2022-4-24 for: v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
+      let token = (getToken() || '') as string
+      this.ws = new WebSocket(url, [token])
+      //update-end-author:taoyan date:2022-4-24 for: v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
       this.ws.onopen = this.on.open.bind(this)
       this.ws.onerror = this.on.error.bind(this)
       this.ws.onmessage = this.on.message.bind(this)

+ 1 - 1
src/components/jeecg/JVxeTable/src/types/index.ts

@@ -5,7 +5,7 @@ import type { VxeGridInstance, VxeTablePropTypes } from 'vxe-table'
 import { JVxeTypes } from './JVxeTypes'
 import { vxeProps } from '../vxe.data'
 import { useMethods } from '../hooks/useMethods'
-import { getJVxeAuths } from '/@/utils/auth/authFilter'
+import { getJVxeAuths } from '../utils/authUtils'
 
 export type JVxeTableProps = Partial<ExtractPropTypes<ReturnType<typeof vxeProps>>>;
 export type JVxeTableMethods = ReturnType<typeof useMethods>['methods']

+ 32 - 7
src/components/jeecg/JVxeTable/src/utils/enhancedUtils.ts

@@ -1,4 +1,5 @@
-import { unref, isRef, Ref } from 'vue'
+import type {Ref, ComponentInternalInstance} from 'vue'
+import {unref, isRef} from 'vue'
 import { useDefaultEnhanced } from '../hooks/useJVxeComponent'
 import { isFunction, isObject, isString } from '/@/utils/is'
 import { JVxeTypes } from '../types'
@@ -53,12 +54,27 @@ export function replaceProps(col, value) {
   return value
 }
 
-type dispatchEventOptions = { props, $event, className: string, handler?: Fn, isClick?: boolean }
+type dispatchEventOptions = {
+  // JVxeTable 的 props
+  props,
+  // 触发的 event 事件对象
+  $event,
+  // 行、列
+  row?, column?,
+  // JVxeTable的vue3实例
+  instance?: ComponentInternalInstance,
+  // 要寻找的className
+  className: string,
+  // 重写找到dom后的处理方法
+  handler?: Fn,
+  // 是否直接执行click方法而不是模拟click事件
+  isClick?: boolean,
+}
 
 /** 模拟触发事件 */
 export function dispatchEvent(options: dispatchEventOptions) {
-  const { props, $event, className, handler, isClick } = options
-  if (!$event || !$event.path) {
+  const {props, $event, row, column, instance, className, handler, isClick} = options
+  if ((!$event || !$event.path) && !instance) {
     return
   }
   // alwaysEdit 下不模拟触发事件,否者会导致触发两次
@@ -66,9 +82,18 @@ export function dispatchEvent(options: dispatchEventOptions) {
     return
   }
   let getCell = () => {
-    for (const el of $event.path) {
-      if (el.classList.contains('vxe-body--column')) {
-        return el as HTMLElement
+    let paths: HTMLElement[] = [...($event?.path ?? [])]
+    // 通过 instance 获取 cell dom对象
+    if (row && column) {
+      let selector = `table.vxe-table--body tbody tr[rowid='${row.id}'] td[colid='${column.id}']`
+      let cellDom = instance!.vnode?.el?.querySelector(selector)
+      if (cellDom) {
+        paths.unshift(cellDom)
+      }
+    }
+    for (const el of paths) {
+      if (el.classList?.contains('vxe-body--column')) {
+        return el
       }
     }
     return null

+ 2 - 0
src/components/jeecg/JVxeTable/src/vxe.data.ts

@@ -89,6 +89,8 @@ export const vxeProps = () => ({
   socketKey: propTypes.string.def('vxe-default'),
   // 新增行时切换行的激活状态
   addSetActive: propTypes.bool.def(true),
+  // 是否开启键盘编辑
+  keyboardEdit: propTypes.bool.def(false),
 })
 
 export const vxeEmits = ['save', 'added', 'removed', 'inserted', 'dragged', 'selectRowChange', 'pageChange', 'valueChange']

+ 9 - 1
src/components/registerGlobComp.ts

@@ -42,7 +42,11 @@ import {
     Spin,
     Space,
     Layout,
-    Collapse
+    Collapse,
+    Slider,
+    InputNumber,
+    Carousel,
+    Popconfirm,
 } from 'ant-design-vue';
 
 const compList = [AntButton.Group, Icon, AIcon,UploadButton];
@@ -91,4 +95,8 @@ export function registerGlobComp(app: App) {
         .use(Space)
         .use(Layout)
         .use(Collapse)
+        .use(Slider)
+        .use(InputNumber)
+        .use(Carousel)
+        .use(Popconfirm)
 }

+ 2 - 0
src/enums/httpEnum.ts

@@ -43,4 +43,6 @@ export enum ConfigEnum {
   Sign = 'X-Sign',
   // 租户id
   TENANT_ID = 'tenant-id',
+  // 版本
+  VERSION = 'X-Version',
 }

+ 9 - 1
src/enums/jeecgEnum.ts

@@ -8,6 +8,14 @@ export enum JInputTypeEnum {
     JINPUT_QUERY_NE = 'ne',
     //大于等于
     JINPUT_QUERY_GE = 'ge',
-    //小等于
+    //小等于
     JINPUT_QUERY_LE = 'le',
 }
+
+/**
+ * 面板设计器需要的常量定义
+ */
+export enum JDragConfigEnum {
+    //baseURL
+    DRAG_BASE_URL = 'drag-base-url',
+}

+ 85 - 0
src/hooks/jeecg/useAdaptiveWidth.ts

@@ -0,0 +1,85 @@
+/**
+ * 自适应宽度构造器
+ *
+ * @time 2022-4-8
+ * @author sunjianlei
+ */
+import {ref} from 'vue'
+import {useDebounceFn, tryOnUnmounted} from '@vueuse/core'
+import {useEventListener} from '/@/hooks/event/useEventListener'
+
+// key = js运算符+数字
+const defWidthConfig: configType = {
+  '<=565': '100%',
+  '<=1366': '800px',
+  '<=1600': '600px',
+  '<=1920': '600px',
+  '>1920': '500px',
+}
+
+type configType = Record<string, string | number>
+
+/**
+ * 自适应宽度
+ * 
+ * @param widthConfig 宽度配置,可参考 defWidthConfig 配置
+ * @param assign 是否合并默认配置
+ * @param debounce 去抖毫秒数
+ */
+export function useAdaptiveWidth(widthConfig = defWidthConfig, assign = true, debounce = 50) {
+  const widthConfigAssign = assign ? Object.assign({}, defWidthConfig, widthConfig) : widthConfig
+  const configKeys = Object.keys(widthConfigAssign)
+
+  const adaptiveWidth = ref<string | number>()
+
+  /**
+   * 进行计算宽度
+   * @param innerWidth
+   */
+  function calcWidth(innerWidth) {
+    let width
+    for (const key of configKeys) {
+      try {
+        // 通过js运算
+        let flag = new Function(`return ${innerWidth} ${key}`)()
+        if (flag) {
+          width = widthConfigAssign[key]
+          break
+        }
+      } catch (e) {
+        console.error(e)
+      }
+    }
+    if (width) {
+      adaptiveWidth.value = width
+    } else {
+      console.warn('没有找到匹配的自适应宽度')
+    }
+  }
+
+  // 初始计算
+  calcWidth(window.innerWidth)
+
+  // 监听 resize 事件
+  const {removeEvent} = useEventListener({
+    el: window,
+    name: 'resize',
+    listener: useDebounceFn(() => calcWidth(window.innerWidth), debounce),
+  })
+  // 卸载组件时取消监听事件
+  tryOnUnmounted(() => removeEvent())
+
+  return {adaptiveWidth}
+}
+
+/**
+ * 抽屉自适应宽度
+ */
+export function useDrawerAdaptiveWidth() {
+  return useAdaptiveWidth({
+    '<=620': '100%',
+    '<=1600': 600,
+    '<=1920': 650,
+    '>1920': 700,
+  }, false)
+}

+ 2 - 2
src/hooks/setting/index.ts

@@ -8,7 +8,7 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
     VITE_GLOB_API_URL,
     VITE_GLOB_APP_SHORT_NAME,
     VITE_GLOB_API_URL_PREFIX,
-    VITE_GLOBE_APP_CAS_BASE_URL,
+    VITE_GLOB_APP_CAS_BASE_URL,
     VITE_GLOB_APP_OPEN_SSO,
     VITE_GLOB_APP_OPEN_QIANKUN,
     VITE_GLOB_DOMAIN_URL,
@@ -29,7 +29,7 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
     shortName: VITE_GLOB_APP_SHORT_NAME,
     openSso:VITE_GLOB_APP_OPEN_SSO,
     openQianKun:VITE_GLOB_APP_OPEN_QIANKUN,
-    casBaseUrl:VITE_GLOBE_APP_CAS_BASE_URL,
+    casBaseUrl:VITE_GLOB_APP_CAS_BASE_URL,
     urlPrefix: VITE_GLOB_API_URL_PREFIX,
     uploadUrl: VITE_GLOB_DOMAIN_URL,
     viewUrl: VITE_GLOB_ONLINE_VIEW_URL,

+ 12 - 4
src/hooks/system/useListPage.ts

@@ -23,6 +23,8 @@ interface ListPageOptions {
     url: string,
     // 导出文件名
     name?: string | (() => string),
+    //导出参数
+    params?: object,
   }
   // 导入配置
   importConfig?: {
@@ -59,14 +61,20 @@ export function useListPage(options: ListPageOptions) {
 
   // 导出 excel
   async function onExportXls() {
-    let { url, name } = options?.exportConfig ?? {}
+    //update-begin---author:wangshuai ---date:20220411  for:导出新增自定义参数------------
+    let { url, name,params } = options?.exportConfig ?? {}
     if (url) {
       let title = typeof name === 'function' ? name() : name
-      const params = await getForm().validate()
+      let paramsForm = await getForm().validate()
+      //如果参数不为空,则整合到一起
+      if(params){
+        paramsForm = Object.assign({},paramsForm,params);
+      }
       if(selectedRowKeys.value && selectedRowKeys.value.length>0){
-        params['selections'] = selectedRowKeys.value.join(",")
+        paramsForm['selections'] = selectedRowKeys.value.join(",")
       }
-      return handleExportXls(title as string, url,filterObj(params))
+      return handleExportXls(title as string, url,filterObj(paramsForm))
+     //update-end---author:wangshuai ---date:20220411  for:导出新增自定义参数--------------
     } else {
       $message.createMessage.warn('没有传递 exportConfig.url 参数')
       return Promise.reject()

+ 21 - 5
src/hooks/system/useMethods.ts

@@ -5,13 +5,22 @@ import {useGlobSetting} from '/@/hooks/setting';
 const {createMessage, createWarningModal} = useMessage();
 const glob = useGlobSetting();
 
+/**
+ * 导出文件xlsx的mime-type
+ */
+export const XLSX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+/**
+ * 导出文件xlsx的文件后缀
+ */
+export const XLSX_FILE_SUFFIX = ".xlsx";
+
 export function useMethods() {
   /**
    * 导出xls
    * @param name
    * @param url
    */
-  async function exportXls(name,url,params) {
+  async function exportXls(name,url,params, isXlsx=false) {
     const data = await defHttp.get({url: url, params:params,responseType: 'blob'}, {isTransformResponse: false})
     if (!data) {
       createMessage.warning("文件下载失败")
@@ -20,14 +29,20 @@ export function useMethods() {
     if(!name || typeof name != "string"){
       name = "导出文件"
     }
+    let blobOptions = { type: 'application/vnd.ms-excel' }
+    let fileSuffix = '.xls'
+    if(isXlsx===true){
+      blobOptions['type'] = XLSX_MIME_TYPE
+      fileSuffix = XLSX_FILE_SUFFIX
+    }
     if (typeof window.navigator.msSaveBlob !== 'undefined') {
-      window.navigator.msSaveBlob(new Blob([data], {type: 'application/vnd.ms-excel'}), name + '.xls')
+      window.navigator.msSaveBlob(new Blob([data], blobOptions), name + fileSuffix)
     } else {
-      let url = window.URL.createObjectURL(new Blob([data], {type: 'application/vnd.ms-excel'}))
+      let url = window.URL.createObjectURL(new Blob([data], blobOptions))
       let link = document.createElement('a')
       link.style.display = 'none'
       link.href = url
-      link.setAttribute('download', name + '.xls')
+      link.setAttribute('download', name + fileSuffix)
       document.body.appendChild(link)
       link.click()
       document.body.removeChild(link); //下载完成移除元素
@@ -70,7 +85,8 @@ export function useMethods() {
   }
 
   return {
-    handleExportXls: (name: string, url: string,params:object) => exportXls(name, url,params),
+    handleExportXls: (name: string, url: string,params?:object) => exportXls(name, url,params),
     handleImportXls: (data, url, success) => importXls(data, url, success),
+    handleExportXlsx: (name: string, url: string, params?:object) => exportXls(name, url,params, true),
   };
 }

+ 5 - 0
src/hooks/web/useWebSocket.ts

@@ -2,6 +2,7 @@
 
 import { computed, reactive, ref, unref } from 'vue'
 import { useWebSocket as $useWebSocket, WebSocketResult } from '@vueuse/core'
+import { getToken } from '/@/utils/auth';
 
 const state = reactive({
   server: '',
@@ -20,12 +21,16 @@ const listeners = new Map()
 export function connectWebSocket(url: string) {
   if (!unref(getIsOpen)) {
     state.server = url
+    //update-begin-author:taoyan date:2022-4-24 for: v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
+    let token = (getToken() || '') as string
     result.value = $useWebSocket(state.server, {
       // 自动重连
       autoReconnect: true,
       // 心跳检测
       heartbeat: false,
+      protocols: [token],
     })
+    //update-end-author:taoyan date:2022-4-24 for: v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
     result.value.open()
     const ws = unref(result.value.ws)
     if (ws) {

+ 6 - 1
src/layouts/default/header/components/user-dropdown/index.vue

@@ -1,7 +1,7 @@
 <template>
   <Dropdown placement="bottomLeft" :overlayClassName="`${prefixCls}-dropdown-overlay`">
     <span :class="[prefixCls, `${prefixCls}--${theme}`]" class="flex">
-      <img :class="`${prefixCls}__header`" :src="getUserInfo.avatar" />
+      <img :class="`${prefixCls}__header`" :src="getAvatarUrl" />
       <span :class="`${prefixCls}__info hidden md:block`">
         <span :class="`${prefixCls}__name  `" class="truncate">
           {{ getUserInfo.realname }}
@@ -80,6 +80,8 @@
   import {refreshCache,queryAllDictItems} from '/@/views/system/dict/dict.api';
   import { DB_DICT_DATA_KEY } from '/src/enums/cacheEnum';
   import { removeAuthCache,setAuthCache } from '/src/utils/auth';
+  import {getFileAccessHttpUrl} from '/@/utils/common/compUtils'
+
   type MenuEvent = 'logout' | 'doc' | 'lock'|'cache' | 'depart';
   const {createMessage} = useMessage();
   export default defineComponent({
@@ -108,6 +110,8 @@
         return { realname, avatar: avatar || headerImg, desc };
       });
 
+      const getAvatarUrl = computed(() => getFileAccessHttpUrl(getUserInfo.value?.avatar))
+      
       const [register, { openModal }] = useModal();
        /**
        * 多部门弹窗逻辑
@@ -179,6 +183,7 @@
         prefixCls,
         t,
         getUserInfo,
+        getAvatarUrl,
         handleMenuClick,
         getShowDoc,
         register,

+ 5 - 0
src/router/helper/routeHelper.ts

@@ -6,6 +6,8 @@ import { cloneDeep, omit } from 'lodash-es';
 import { warn } from '/@/utils/log';
 import { createRouter, createWebHashHistory } from 'vue-router';
 import { getToken } from '/@/utils/auth';
+import {URL_HASH_TAB} from '/@/utils'
+
 export type LayoutMapKey = 'LAYOUT';
 const IFRAME = () => import('/@/views/sys/iframe/FrameBlank.vue');
 const LayoutContent = () => import('/@/layouts/default/content/index.vue');
@@ -49,6 +51,9 @@ function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
       if (item.meta?.internalOrExternal) {
         // @ts-ignore 外部打开
         item.path = item.component
+        // update-begin--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开,原因是带了#号,需要替换一下
+        item.path = item.path.replace('#', URL_HASH_TAB)
+        // update-end--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开,原因是带了#号,需要替换一下
       } else {
         // @ts-ignore 内部打开
         item.meta.frameSrc = item.component

+ 30 - 0
src/utils/desform/customExpression.ts

@@ -0,0 +1,30 @@
+/*
+*
+* 这里填写用户自定义的表达式
+* 可用在Online表单的默认值表达式中使用
+* 需要外部使用的变量或方法一定要 export,否则无法识别
+* 示例:
+*   export const name = '张三'; // const 是常量
+*   export let age = 17; // 看情况 export const 还是 let ,两者都可正常使用
+*   export function content(arg) { // export 方法,可传参数,使用时要加括号,值一定要return回去,可以返回Promise
+*     return 'content' + arg;
+*   }
+*   export const address = (arg) => content(arg) + ' | 北京市'; // export 箭头函数也可以
+*
+*/
+
+/** 字段默认值官方示例:获取地址 */
+export function demoFieldDefVal_getAddress(arg) {
+  if (!arg) {
+    arg = '朝阳区'
+  }
+  return `北京市 ${arg}`
+}
+
+/** 自定义JS函数示例 */
+export function sayHi(name) {
+  if (!name) {
+    name = '张三'
+  }
+  return `您好,我叫: ${name}`
+}

+ 24 - 16
src/utils/encryption/signMd5Utils.js

@@ -103,23 +103,31 @@ export default class signMd5Utils {
     return paramStr;
   };
 
-  static getDateTimeToString() {
-    const date_ = new Date()
-    const year = date_.getFullYear()
-    let month = date_.getMonth() + 1
-    let day = date_.getDate()
-    if (month < 10) month = '0' + month
-    if (day < 10) day = '0' + day
-    let hours = date_.getHours()
-    let mins = date_.getMinutes()
-    let secs = date_.getSeconds()
-    const msecs = date_.getMilliseconds()
-    if (hours < 10) hours = '0' + hours
-    if (mins < 10) mins = '0' + mins
-    if (secs < 10) secs = '0' + secs
-    if (msecs < 10) secs = '0' + msecs
-    return year + '' + month + '' + day + '' + hours + '' + mins + '' + secs
+  /**
+   * 接口签名用 生成header中的时间戳
+   * @returns {number}
+   */
+  static getTimestamp(){
+    return new Date().getTime()
   }
+
+  // static getDateTimeToString() {
+  //   const date_ = new Date()
+  //   const year = date_.getFullYear()
+  //   let month = date_.getMonth() + 1
+  //   let day = date_.getDate()
+  //   if (month < 10) month = '0' + month
+  //   if (day < 10) day = '0' + day
+  //   let hours = date_.getHours()
+  //   let mins = date_.getMinutes()
+  //   let secs = date_.getSeconds()
+  //   const msecs = date_.getMilliseconds()
+  //   if (hours < 10) hours = '0' + hours
+  //   if (mins < 10) mins = '0' + mins
+  //   if (secs < 10) secs = '0' + secs
+  //   if (msecs < 10) secs = '0' + msecs
+  //   return year + '' + month + '' + day + '' + hours + '' + mins + '' + secs
+  // }
     // true:数值型的,false:非数值型
   static myIsNaN(value) {
     return typeof value === 'number' && !isNaN(value);

+ 2 - 2
src/utils/env.ts

@@ -30,7 +30,7 @@ export function getAppEnvConfig() {
     VITE_GLOB_API_URL_PREFIX,
     VITE_GLOB_APP_OPEN_SSO,
     VITE_GLOB_APP_OPEN_QIANKUN,
-    VITE_GLOBE_APP_CAS_BASE_URL,
+    VITE_GLOB_APP_CAS_BASE_URL,
     VITE_GLOB_DOMAIN_URL,
     VITE_GLOB_ONLINE_VIEW_URL,
   } = ENV;
@@ -49,7 +49,7 @@ export function getAppEnvConfig() {
     VITE_GLOB_API_URL_PREFIX,
     VITE_GLOB_APP_OPEN_SSO,
     VITE_GLOB_APP_OPEN_QIANKUN,
-    VITE_GLOBE_APP_CAS_BASE_URL,
+    VITE_GLOB_APP_CAS_BASE_URL,
     VITE_GLOB_DOMAIN_URL,
     VITE_GLOB_ONLINE_VIEW_URL,
   };

+ 12 - 3
src/utils/http/axios/index.ts

@@ -138,7 +138,7 @@ const transform: AxiosTransform = {
   /**
    * @description: 请求拦截器处理
    */
-  requestInterceptors: (config, options) => {
+  requestInterceptors: (config:Recordable, options) => {
     // 请求之前处理config
     const token = getToken();
     let tenantid = getTenantId();
@@ -147,7 +147,11 @@ const transform: AxiosTransform = {
       config.headers.Authorization = options.authenticationScheme ? `${options.authenticationScheme} ${token}` : token;
       config.headers[ConfigEnum.TOKEN] = token
       //--update-begin--author:liusq---date:20210831---for:将签名和时间戳,添加在请求接口 Header
-      config.headers[ConfigEnum.TIMESTAMP] = signMd5Utils.getDateTimeToString();
+      
+      // update-begin--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
+      config.headers[ConfigEnum.TIMESTAMP] = signMd5Utils.getTimestamp();
+      // update-end--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
+      
       config.headers[ConfigEnum.Sign] = signMd5Utils.getSign(config.url, config.params);
       //--update-end--author:liusq---date:20210831---for:将签名和时间戳,添加在请求接口 Header
       //--update-begin--author:liusq---date:20211105---for: for:将多租户id,添加在请求接口 Header
@@ -155,6 +159,9 @@ const transform: AxiosTransform = {
          tenantid = 0;
       }
       config.headers[ConfigEnum.TENANT_ID ] = tenantid
+      //--update-begin--author:liusq---date:20220325---for: 增加vue3标记
+       config.headers[ConfigEnum.VERSION] = "v3"
+      //--update-end--author:liusq---date:20220325---for:增加vue3标记
       //--update-end--author:liusq---date:20211105---for:将多租户id,添加在请求接口 Header
 
     }
@@ -177,7 +184,9 @@ const transform: AxiosTransform = {
     errorLogStore.addAjaxErrorInfo(error);
     const { response, code, message, config } = error || {};
     const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none';
-    const msg: string = response?.data?.error?.message ?? '';
+    //scott 20211022 token失效提示信息
+    //const msg: string = response?.data?.error?.message ?? '';
+    const msg: string = response?.data?.message ?? '';
     const err: string = error?.toString?.() ?? '';
     let errMessage = '';
 

+ 21 - 1
src/utils/index.ts

@@ -4,6 +4,10 @@ import type { App, Plugin } from 'vue';
 import { unref } from 'vue';
 import { isObject } from '/@/utils/is';
 
+// update-begin--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开,原因是带了#号,需要替换一下
+export const URL_HASH_TAB = `__AGWE4H__HASH__TAG__PWHRG__`
+// update-end--author:sunjianlei---date:20220408---for: 【VUEN-656】配置外部网址打不开,原因是带了#号,需要替换一下
+
 export const noop = () => {};
 
 /**
@@ -71,7 +75,7 @@ export function getDynamicProps<T, U>(props: T): Partial<U> {
  * @updateBy:zyf
  */
 export function getValueType(props,field){
-  let formSchema = unref(props.schemas);
+  let formSchema = unref(unref(props)?.schemas)
   let valueType = "string";
   if (formSchema) {
     let schema = formSchema.filter((item) => item.field === field)[0];
@@ -151,3 +155,19 @@ export function sleep(ms: number, fn?: Fn) {
     resolve()
   }, ms))
 }
+
+/**
+ * 不用正则的方式替换所有值
+ * @param text 被替换的字符串
+ * @param checker  替换前的内容
+ * @param replacer 替换后的内容
+ * @returns {String} 替换后的字符串
+ */
+export function replaceAll(text, checker, replacer) {
+  let lastText = text
+  text = text.replace(checker, replacer)
+  if (lastText !== text) {
+    return replaceAll(text, checker, replacer)
+  }
+  return text
+}

+ 5 - 2
src/utils/lib/echarts.ts

@@ -26,7 +26,9 @@ import {
   GraphicComponent,
 } from 'echarts/components';
 
-import { SVGRenderer } from 'echarts/renderers';
+// TODO 如果想换成SVG渲染,就导出SVGRenderer,
+//  并且放到 echarts.use 里,注释掉 CanvasRenderer
+import { /*SVGRenderer*/ CanvasRenderer } from 'echarts/renderers';
 
 echarts.use([
   LegendComponent,
@@ -41,7 +43,8 @@ echarts.use([
   PieChart,
   MapChart,
   RadarChart,
-  SVGRenderer,
+  // TODO 因为要兼容Online图表自适应打印,所以改成 CanvasRenderer,可能会模糊
+  CanvasRenderer,
   PictorialBarChart,
   RadarComponent,
   ToolboxComponent,

+ 143 - 0
src/views/demo/jeecg/JVxeTableDemo/JVxeDemo5.vue

@@ -0,0 +1,143 @@
+<template>
+  <div>
+    <b>键盘操作快捷键:</b>
+    <div style="border: 1px solid #cccccc; padding: 8px; width: 740px;">
+        <pre>
+             F2 | 如果存在,激活单元格为编辑状态
+            Esc | 如果存在,取消单元格编辑状态
+              ↑ | 如果存在,则移动到上面的单元格
+              ↓ | 如果存在,则移动到下面的单元格
+              ← | 如果存在,则移动到左边的单元格
+              → | 如果存在,则移动到右边的单元格
+            Tab | 如果存在,则移动到右边单元格;如果移动到最后一列,则从下一行开始移到,以此循环
+    Shift + Tab | 如果存在,则移动到左边单元格,如果移动到第一列,则从上一行开始移到,以此循环
+          Enter | 如果存在,取消单元格编辑并移动到下面的单元格
+  Shift + Enter | 如果存在,取消单元格编辑并移动到上面的单元格</pre>
+    </div>
+
+    <JVxeTable
+        ref="tableRef"
+        stripe
+        toolbar
+        rowNumber
+        rowSelection
+        keyboardEdit
+        :columns="columns"
+        :dataSource="dataSource"
+    >
+    </JVxeTable>
+
+  </div>
+</template>
+<script lang="ts">
+import {ref, onMounted, nextTick, defineComponent} from 'vue'
+import {Popconfirm} from 'ant-design-vue'
+import {JVxeTypes, JVxeColumn, JVxeTableInstance} from '/@/components/jeecg/JVxeTable/types'
+
+export default defineComponent({
+  name: 'JVxeDemo5',
+  components: {[Popconfirm.name]: Popconfirm},
+  setup() {
+    const tableRef = ref<JVxeTableInstance>()
+    const columns = ref<JVxeColumn[]>([
+      {
+        title: '单行文本',
+        key: 'input',
+        type: JVxeTypes.input,
+        width: 220,
+        defaultValue: '',
+        placeholder: '请输入${title}',
+      },
+      {
+        title: '多行文本',
+        key: 'textarea',
+        type: JVxeTypes.textarea,
+        width: 240,
+      },
+      {
+        title: '数字',
+        key: 'number',
+        type: JVxeTypes.inputNumber,
+        width: 120,
+        defaultValue: 32,
+      },
+      {
+        title: '日期时间',
+        key: 'datetime',
+        type: JVxeTypes.datetime,
+        width: 240,
+        defaultValue: '2019-4-30 14:52:22',
+        placeholder: '请选择',
+      },
+      {
+        title: '时间',
+        key: 'time',
+        type: JVxeTypes.time,
+        width: 220,
+        defaultValue: '14:52:22',
+        placeholder: '请选择',
+      },
+      {
+        title: '下拉框',
+        key: 'select',
+        type: JVxeTypes.select,
+        width: 220,
+        // 下拉选项
+        options: [
+          {title: 'String', value: 'string'},
+          {title: 'Integer', value: 'int'},
+          {title: 'Double', value: 'double'},
+          {title: 'Boolean', value: 'boolean'},
+        ],
+        // allowInput: true,
+        allowSearch: true,
+        placeholder: '请选择',
+      },
+      {
+        title: '复选框',
+        key: 'checkbox',
+        type: JVxeTypes.checkbox,
+        // width: 100,
+        customValue: ['Y', 'N'], // true ,false
+        defaultChecked: false,
+      },
+    ])
+    const dataSource = ref([])
+
+    function handleView(props) {
+      // 参数介绍:
+      // props.value          当前单元格的值
+      // props.row            当前行的数据
+      // props.rowId          当前行ID
+      // props.rowIndex       当前行下标
+      // props.column         当前列的配置
+      // props.columnIndex    当前列下标
+      // props.$table         vxe-table实例,可以调用vxe-table内置方法
+      // props.scrolling      是否正在滚动
+      // props.reloadEffect   是否开启了数据刷新特效
+      // props.triggerChange  触发change事件,用于更改slot的值
+      console.log('props: ', props)
+    }
+
+    function handleDelete({row}) {
+      // 使用实例:删除当前操作的行
+      tableRef.value?.removeRows(row)
+    }
+
+    onMounted(async () => {
+      console.log(tableRef.value)
+      await nextTick()
+      // 默认添加五行数据
+      tableRef.value!.addRows([
+        {input: 'input_1'},
+        {input: 'input_2'},
+        {input: 'input_3'},
+        {input: 'input_4'},
+        {input: 'input_5'},
+      ], {setActive: false})
+    })
+
+    return {tableRef, columns, dataSource, handleView, handleDelete}
+  },
+})
+</script>

+ 4 - 0
src/views/demo/jeecg/JVxeTableDemo/index.vue

@@ -14,6 +14,9 @@
         <a-tab-pane tab="联动示例" key="4">
           <JVxeDemo4/>
         </a-tab-pane>
+        <a-tab-pane tab="键盘操作" key="5">
+          <JVxeDemo5/>
+        </a-tab-pane>
       </a-tabs>
     </a-card>
   </PageWrapper>
@@ -26,4 +29,5 @@ import JVxeDemo1 from './JVxeDemo1.vue'
 import JVxeDemo2 from './JVxeDemo2.vue'
 import JVxeDemo3 from './JVxeDemo3.vue'
 import JVxeDemo4 from './JVxeDemo4.vue'
+import JVxeDemo5 from './JVxeDemo5.vue'
 </script>

+ 61 - 41
src/views/demo/jeecg/JeecgComponents.vue

@@ -1,60 +1,72 @@
 <template>
-    <BasicForm ref="formElRef" :labelCol="{span: 5}" :wrapperCol="{ span: 15}" :showResetButton="false" :showSubmitButton="false" :schemas="schemas" :actionColOptions="{ span: 24 }" @submit="handleSubmit" @reset="handleReset"  style="height: 100%">
-        <template #jAreaLinkage="{model, field }">
-            <JAreaLinkage  v-model:value="model[field]" :showArea="true" :showAll="false"/>
-        </template>
-        <template #jAreaLinkage1="{model, field }">
-            <JAreaLinkage :disabled="isDisabledAuth(['demo.dbarray'])" v-model:value="model[field]" :showArea="true" :showAll="false"/>
-        </template>
-        <template #JPopup="{model, field }">
-            <JPopup v-model:value="model[field]" :formElRef="formElRef" code="report_user" :fieldConfig="[{source:'username',target:'pop1'}]"/>
-        </template>
-        <template #JAreaSelect="{model, field }">
-            <JAreaSelect v-model:value="model[field]"/>
-        </template>
-        <template #JCheckbox="{model, field }">
-            <JCheckbox v-model:value="model[field]" dictCode="remindMode"/>
-        </template>
-        <template #JInput="{model, field }">
-            <JInput v-model:value="model[field]" :type="model['jinputtype']"/>
-        </template>
-        <template #dargVerify="{model, field }">
-            <BasicDragVerify v-model:value="model[field]"/>
-        </template>
-    </BasicForm>
+  <BasicForm
+    ref="formElRef"
+    :class="'jee-select-demo-form'"
+    :labelCol="{ span: 5 }"
+    :wrapperCol="{ span: 15 }"
+    :showResetButton="false"
+    :showSubmitButton="false"
+    :schemas="schemas"
+    :actionColOptions="{ span: 24 }"
+    @submit="handleSubmit"
+    @reset="handleReset"
+    style="height: 100%"
+  >
+    <template #jAreaLinkage="{ model, field }">
+      <JAreaLinkage v-model:value="model[field]" :showArea="true" :showAll="false" />
+    </template>
+    <template #jAreaLinkage1="{ model, field }">
+      <JAreaLinkage :disabled="isDisabledAuth(['demo.dbarray'])" v-model:value="model[field]" :showArea="true" :showAll="false" />
+    </template>
+    <template #JPopup="{ model, field }">
+      <JPopup v-model:value="model[field]" :formElRef="formElRef" code="report_user" :fieldConfig="[{ source: 'username', target: 'pop1' }]" />
+    </template>
+    <template #JAreaSelect="{ model, field }">
+      <JAreaSelect v-model:value="model[field]" />
+    </template>
+    <template #JCheckbox="{ model, field }">
+      <JCheckbox v-model:value="model[field]" dictCode="remindMode" />
+    </template>
+    <template #JInput="{ model, field }">
+      <JInput v-model:value="model[field]" :type="model['jinputtype']" />
+    </template>
+    <template #dargVerify="{ model, field }">
+      <BasicDragVerify v-model:value="model[field]" />
+    </template>
+  </BasicForm>
 </template>
 <script lang="ts">
   import { computed, defineComponent, unref, ref } from 'vue';
-  import {
-    BasicForm,
-    ApiSelect,
-    JAreaLinkage,
-    JPopup,
-    JAreaSelect,
-    FormActionType,
-    JCheckbox,
-    JInput,
-    JEllipsis
-  } from '/@/components/Form'
+  import { BasicForm, ApiSelect, JAreaLinkage, JPopup, JAreaSelect, FormActionType, JCheckbox, JInput, JEllipsis } from '/@/components/Form';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { optionsListApi } from '/@/api/demo/select';
   import { useDebounceFn } from '@vueuse/core';
   import { schemas } from './jeecgComponents.data.ts';
   import { usePermission } from '/@/hooks/web/usePermission';
   import { BasicDragVerify } from '/@/components/Verify/index';
-  
+
   export default defineComponent({
-    components: { BasicForm, ApiSelect, JAreaLinkage ,JPopup,JAreaSelect,JCheckbox,JInput,JEllipsis,BasicDragVerify },
-    name:'JeecgComponents',
+    components: {
+      BasicForm,
+      ApiSelect,
+      JAreaLinkage,
+      JPopup,
+      JAreaSelect,
+      JCheckbox,
+      JInput,
+      JEllipsis,
+      BasicDragVerify,
+    },
+    name: 'JeecgComponents',
     setup() {
       const { isDisabledAuth } = usePermission();
       const check = ref(null);
       const formElRef = ref<Nullable<FormActionType>>(null);
       const { createMessage } = useMessage();
       const keyword = ref<string>('');
-      const submitButtonOptions=ref({
-        text:"确定"
-      })
+      const submitButtonOptions = ref({
+        text: '确定',
+      });
       const searchParams = computed<Recordable>(() => {
         return { keyword: unref(keyword) };
       });
@@ -75,7 +87,7 @@
           keyword.value = '';
         },
         handleSubmit: (values: any) => {
-          console.log("values:",values)
+          console.log('values:', values);
           createMessage.success('click search,values:' + JSON.stringify(values));
         },
         check,
@@ -83,3 +95,11 @@
     },
   });
 </script>
+<style lang="less" scoped>
+  /**update-begin-author:taoyan date:20220324 for: VUEN-351【vue3】展示不全*/
+  .jee-select-demo-form .ant-col-5 {
+    flex: 0 0 159px;
+    max-width: 159px;
+  }
+  /**update-end-author:taoyan date:20220324 for: VUEN-351【vue3】展示不全*/
+</style>

+ 621 - 593
src/views/demo/jeecg/jeecgComponents.data.ts

@@ -1,596 +1,624 @@
-import {FormSchema, JCronValidator} from '/@/components/Form'
-import {usePermission} from '/@/hooks/web/usePermission';
+import { FormSchema, JCronValidator } from '/@/components/Form';
+import { usePermission } from '/@/hooks/web/usePermission';
 
-const {isDisabledAuth} = usePermission();
+const { isDisabledAuth } = usePermission();
 export const schemas: FormSchema[] = [
-    {
-        field: 'jdst',
-        component: 'JDictSelectTag',
-        label: '性别下拉',
-        helpMessage: ['component模式'],
-        componentProps: {
-            dictCode: 'sex',
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'jdst',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'jdst1',
-        component: 'JDictSelectTag',
-        label: '性别选择',
-        helpMessage: ['component模式'],
-        componentProps: {
-            dictCode: 'sex',
-            type: 'radioButton'
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'jdst1',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'jdst2',
-        component: 'JDictSelectTag',
-        label: '字典表下拉',
-        helpMessage: ['component模式'],
-        componentProps: {
-            dictCode: 'sys_user,realname,id',
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'jdst2',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'jdst3',
-        component: 'JDictSelectTag',
-        label: '字典表下拉(带条件)',
-        helpMessage: ['component模式'],
-        componentProps: {
-            dictCode: 'sys_user,realname,id,username!=\'admin\' order by create_time',
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'jdst3',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'jsst',
-        component: 'JSearchSelect',
-        label: '字典搜索(同步)',
-        colProps: {span: 12},
-        componentProps: {
-            //dict: "sys_depart,depart_name,id",
-            dictOptions: [{
-                text: "选项一",
-                value: "1"
-            }, {
-                text: "选项二",
-                value: "2"
-            }, {
-                text: "选项三",
-                value: "3"
-            }]
-        },
-    },
-    {
-        field: 'jsst',
-        component: 'JEllipsis',
-        label: '选择值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'jsst2',
-        component: 'JSearchSelect',
-        label: '字典搜索(异步)',
-        colProps: {span: 12},
-        componentProps: {
-            dict: "sys_depart,depart_name,id",
-            pageSize: 6,
-            async: true
-        },
-    },
-    {
-        field: 'jsst2',
-        component: 'JEllipsis',
-        label: '选择值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'xldx',
-        component: 'JDictSelectTag',
-        label: '字典下拉多选',
-        colProps: {span: 12},
-        componentProps: {
-            dictCode: 'sex',
-            mode:'multiple',
-        },
-    },
-    {
-        field: 'xldx',
-        component: 'JEllipsis',
-        label: '选择值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'dxxlk',
-        component: 'JDictSelectTag',
-        label: '字典下拉单选',
-        colProps: {span: 12},
-        componentProps: {
-            dictCode: 'sex',
-        },
-    },
-    {
-        field: 'dxxlk',
-        component: 'JEllipsis',
-        label: '选择值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'depart3',
-        component: 'JSelectDept',
-        label: '选择部门—自定义值',
-        helpMessage: ['component模式'],
-        componentProps: {showButton: false,rowKey:'orgCode',primaryKey:'orgCode'},
-        colProps: {
-            span: 12,
-        }
-    },
-    {
-        field: 'depart3',
-        component: 'JEllipsis',
-        label: '选中部门',
-        colProps: {span: 12},
-    },
-    {
-        field: 'depart2',
-        component: 'JSelectDept',
-        label: '选择部门',
-        helpMessage: ['component模式'],
-        componentProps: {showButton: false},
-        colProps: {
-            span: 12,
-        }
-    },
-    {
-        field: 'depart2',
-        component: 'JEllipsis',
-        label: '选中部门',
-        colProps: {span: 12},
-    },
-    {
-        field: 'user2',
-        component: 'JSelectUser',
-        label: '用户选择组件',
-        helpMessage: ['component模式'],
-        componentProps: {
-            labelKey: 'realname',
-            rowKey: 'id',
-            showSelectTable: false
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'user2',
-        component: 'JEllipsis',
-        label: '选中用户',
-        colProps: {span: 12},
-    },
-    {
-        field: 'user3',
-        component: 'JSelectUserByDept',
-        label: '部门选择用户',
-        helpMessage: ['component模式'],
-        componentProps: {
-            labelKey: 'realname',
-            rowKey: 'username',
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'user3',
-        component: 'JEllipsis',
-        label: '选中用户',
-        colProps: {span: 12},
-    },
-    {
-        field: 'role2',
-        component: 'JSelectRole',
-        label: '角色选择组件',
-        helpMessage: ['component模式'],
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'role2',
-        component: 'JEllipsis',
-        label: '选中角色',
-        colProps: {span: 12},
-    },
-    {
-        field: 'position2',
-        component: 'JSelectPosition',
-        label: '职务选择组件',
-        helpMessage: ['component模式'],
-        colProps: {span: 12},
-        componentProps: {async: true, showSelectTable: true},
-    },
-    {
-        field: 'position2',
-        component: 'JEllipsis',
-        label: '选中职务',
-        colProps: {span: 12},
-    },
-    {
-        field: 'checkbox1',
-        component: 'JCheckbox',
-        label: 'JCheckbox组件1',
-        helpMessage: ['component模式'],
-        defaultValue: '1,2',
-        componentProps: {
-            options: [{label: '男', value: '1'}, {label: '女', value: '2'}]
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'checkbox1',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'checkbox2',
-        component: 'Input',
-        label: 'JCheckbox组件2',
-        defaultValue: '1',
-        helpMessage: ['插槽模式'],
-        slot: 'JCheckbox',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'checkbox2',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'data1',
-        label: '日期选择',
-        component: 'DatePicker',
-        componentProps: {
-            showTime: true,
-            valueFormat: 'YYYY-MM-DD HH:mm:ss',
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'data1',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'hk',
-        component: 'Input',
-        label: '滑块验证码',
-        helpMessage: ['插槽模式'],
-        slot: 'dargVerify',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'hk',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'JTreeDict',
-        component: 'JTreeDict',
-        label: '树字典',
-        helpMessage: ['component模式'],
-        colProps: {span: 12},
-    },
-    {
-        field: 'JTreeDict',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'ts',
-        component: 'JTreeSelect',
-        label: '下拉树选择',
-        helpMessage: ['component模式'],
-        componentProps: {
-            dict: "sys_permission,name,id",
-            pidField: "parent_id",
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'ts',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'ts1',
-        component: 'JTreeSelect',
-        label: '下拉树多选',
-        helpMessage: ['component模式'],
-        componentProps: {
-            dict: "sys_permission,name,id",
-            pidField: "parent_id",
-            multiple: true
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'ts1',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'category',
-        component: 'JCategorySelect',
-        label: '分类字典树',
-        helpMessage: ['component模式'],
-        defaultValue:'',
-        componentProps: {
-            pcode: "B01",
-            multiple: true
-        },
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'category',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'JEasyCron',
-        component: 'JEasyCron',
-        label: 'JEasyCron',
-        helpMessage: ['component模式'],
-        colProps: {span: 12},
-        defaultValue: '* * * * * ? *',
-        rules: [{validator: JCronValidator}],
-    },
-    {
-        field: 'JEasyCron',
-        component: 'JEllipsis',
-        label: '选择值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'JInput',
-        component: 'JInput',
-        label: '特殊查询组件',
-        helpMessage: ['插槽模式'],
-        slot: 'JInput',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'jinputtype',
-        component: 'Select',
-        label: '查询类型',
-        componentProps: {
-            options: [
-                {value: 'like', label: '模糊(like)'},
-                {value: 'ne', label: '不等于(ne)'},
-                {value: 'ge', label: '大于等于(ge)'},
-                {value: 'le', label: '小于等于(le)'}
-            ]
-        },
-        colProps: {
-            span: 6,
-        },
-    },
-    {
-        field: 'JInput',
-        component: 'JEllipsis',
-        label: '输入值',
-        colProps: {span: 6},
-    },
-    {
-        field: 'field1',
-        component: 'Select',
-        label: '省市区选择',
-        helpMessage: ['插槽模式'],
-        slot: 'jAreaLinkage',
-        colProps: {
-            span: 12,
-        },
-        defaultValue: ['130000', '130200'],
-    },
-    {
-        field: 'field1',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'field0',
-        component: 'Select',
-        label: '禁用组件(方式一)',
-        helpMessage: ['插槽模式'],
-        slot: 'jAreaLinkage1',
-        colProps: {
-            span: 12,
-        },
-        defaultValue: ['130000', '130200'],
-    },
+  {
+    field: 'jdst',
+    component: 'JDictSelectTag',
+    label: '性别下拉',
+    helpMessage: ['component模式'],
+    componentProps: {
+      dictCode: 'sex',
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'jdst',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'jdst1',
+    component: 'JDictSelectTag',
+    label: '性别选择',
+    helpMessage: ['component模式'],
+    componentProps: {
+      dictCode: 'sex',
+      type: 'radioButton',
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'jdst1',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'jdst2',
+    component: 'JDictSelectTag',
+    label: '字典表下拉',
+    helpMessage: ['component模式'],
+    componentProps: {
+      dictCode: 'sys_user,realname,id',
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'jdst2',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'jdst3',
+    component: 'JDictSelectTag',
+    label: '字典表下拉(带条件)',
+    helpMessage: ['component模式'],
+    componentProps: {
+      dictCode: "sys_user,realname,id,username!='admin' order by create_time",
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'jdst3',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'jsst',
+    component: 'JSearchSelect',
+    label: '字典搜索(同步)',
+    colProps: { span: 12 },
+    componentProps: {
+      //dict: "sys_depart,depart_name,id",
+      dictOptions: [
+        {
+          text: '选项一',
+          value: '1',
+        },
+        {
+          text: '选项二',
+          value: '2',
+        },
+        {
+          text: '选项三',
+          value: '3',
+        },
+      ],
+    },
+  },
+  {
+    field: 'jsst',
+    component: 'JEllipsis',
+    label: '选择值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'jsst2',
+    component: 'JSearchSelect',
+    label: '字典搜索(异步)',
+    colProps: { span: 12 },
+    componentProps: {
+      dict: 'sys_depart,depart_name,id',
+      pageSize: 6,
+      async: true,
+    },
+  },
+  {
+    field: 'jsst2',
+    component: 'JEllipsis',
+    label: '选择值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'xldx',
+    component: 'JDictSelectTag',
+    label: '字典下拉多选',
+    colProps: { span: 12 },
+    componentProps: {
+      dictCode: 'sex',
+      mode: 'multiple',
+    },
+  },
+  {
+    field: 'xldx',
+    component: 'JEllipsis',
+    label: '选择值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'dxxlk',
+    component: 'JDictSelectTag',
+    label: '字典下拉单选',
+    colProps: { span: 12 },
+    componentProps: {
+      dictCode: 'sex',
+    },
+  },
+  {
+    field: 'dxxlk',
+    component: 'JEllipsis',
+    label: '选择值',
+    colProps: { span: 12 },
+  },
+  {
+    label: '可输入下拉',
+    field: 'selectInput',
+    component: 'JSelectInput',
+    componentProps: {
+      options: [
+        { label: '选项一', value: '1' },
+        { label: '选项二', value: '2' },
+        { label: '选项三', value: '3' },
+      ],
+    },
+    colProps: { span: 12 },
+  },
+  {
+    field: 'selectInput',
+    component: 'JEllipsis',
+    label: '选择值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'depart3',
+    component: 'JSelectDept',
+    label: '选择部门—自定义值',
+    helpMessage: ['component模式'],
+    componentProps: { showButton: false, rowKey: 'orgCode', primaryKey: 'orgCode' },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'depart3',
+    component: 'JEllipsis',
+    label: '选中部门',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'depart2',
+    component: 'JSelectDept',
+    label: '选择部门',
+    helpMessage: ['component模式'],
+    componentProps: { showButton: false },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'depart2',
+    component: 'JEllipsis',
+    label: '选中部门',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'user2',
+    component: 'JSelectUser',
+    label: '用户选择组件',
+    helpMessage: ['component模式'],
+    componentProps: {
+      labelKey: 'realname',
+      rowKey: 'id',
+      showSelectTable: false,
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'user2',
+    component: 'JEllipsis',
+    label: '选中用户',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'user3',
+    component: 'JSelectUserByDept',
+    label: '部门选择用户',
+    helpMessage: ['component模式'],
+    componentProps: {
+      labelKey: 'realname',
+      rowKey: 'username',
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'user3',
+    component: 'JEllipsis',
+    label: '选中用户',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'role2',
+    component: 'JSelectRole',
+    label: '角色选择组件',
+    helpMessage: ['component模式'],
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'role2',
+    component: 'JEllipsis',
+    label: '选中角色',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'position2',
+    component: 'JSelectPosition',
+    label: '职务选择组件',
+    helpMessage: ['component模式'],
+    colProps: { span: 12 },
+    componentProps: { async: true, showSelectTable: true },
+  },
+  {
+    field: 'position2',
+    component: 'JEllipsis',
+    label: '选中职务',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'checkbox1',
+    component: 'JCheckbox',
+    label: 'JCheckbox组件1',
+    helpMessage: ['component模式'],
+    defaultValue: '1,2',
+    componentProps: {
+      options: [
+        { label: '男', value: '1' },
+        { label: '女', value: '2' },
+      ],
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'checkbox1',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'checkbox2',
+    component: 'Input',
+    label: 'JCheckbox组件2',
+    defaultValue: '1',
+    helpMessage: ['插槽模式'],
+    slot: 'JCheckbox',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'checkbox2',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'data1',
+    label: '日期选择',
+    component: 'DatePicker',
+    componentProps: {
+      showTime: true,
+      valueFormat: 'YYYY-MM-DD HH:mm:ss',
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'data1',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'hk',
+    component: 'Input',
+    label: '滑块验证码',
+    helpMessage: ['插槽模式'],
+    slot: 'dargVerify',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'hk',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'JTreeDict',
+    component: 'JTreeDict',
+    label: '树字典',
+    helpMessage: ['component模式'],
+    colProps: { span: 12 },
+  },
+  {
+    field: 'JTreeDict',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'ts',
+    component: 'JTreeSelect',
+    label: '下拉树选择',
+    helpMessage: ['component模式'],
+    componentProps: {
+      dict: 'sys_permission,name,id',
+      pidField: 'parent_id',
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'ts',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'ts1',
+    component: 'JTreeSelect',
+    label: '下拉树多选',
+    helpMessage: ['component模式'],
+    componentProps: {
+      dict: 'sys_permission,name,id',
+      pidField: 'parent_id',
+      multiple: true,
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'ts1',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'category',
+    component: 'JCategorySelect',
+    label: '分类字典树',
+    helpMessage: ['component模式'],
+    defaultValue: '',
+    componentProps: {
+      pcode: 'B01',
+      multiple: true,
+    },
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'category',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'JEasyCron',
+    component: 'JEasyCron',
+    label: 'JEasyCron',
+    helpMessage: ['component模式'],
+    colProps: { span: 12 },
+    defaultValue: '* * * * * ? *',
+    rules: [{ validator: JCronValidator }],
+  },
+  {
+    field: 'JEasyCron',
+    component: 'JEllipsis',
+    label: '选择值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'JInput',
+    component: 'JInput',
+    label: '特殊查询组件',
+    helpMessage: ['插槽模式'],
+    slot: 'JInput',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'jinputtype',
+    component: 'Select',
+    label: '查询类型',
+    componentProps: {
+      options: [
+        { value: 'like', label: '模糊(like)' },
+        { value: 'ne', label: '不等于(ne)' },
+        { value: 'ge', label: '大于等于(ge)' },
+        { value: 'le', label: '小于等于(le)' },
+      ],
+    },
+    colProps: {
+      span: 6,
+    },
+  },
+  {
+    field: 'JInput',
+    component: 'JEllipsis',
+    label: '输入值',
+    colProps: { span: 6 },
+  },
+  {
+    field: 'field1',
+    component: 'Select',
+    label: '省市区选择',
+    helpMessage: ['插槽模式'],
+    slot: 'jAreaLinkage',
+    colProps: {
+      span: 12,
+    },
+    defaultValue: ['130000', '130200'],
+  },
+  {
+    field: 'field1',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'field0',
+    component: 'Select',
+    label: '禁用组件(方式一)',
+    helpMessage: ['插槽模式'],
+    slot: 'jAreaLinkage1',
+    colProps: {
+      span: 12,
+    },
+    defaultValue: ['130000', '130200'],
+  },
 
-    {
-        field: 'field0',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'field2',
-        component: 'JAreaLinkage',
-        label: '禁用组件(方式二)',
-        helpMessage: ['component模式'],
-        colProps: {
-            span: 12,
-        },
-        dynamicDisabled: ({values}) => {
-            return isDisabledAuth(['demo.dbarray']);
-        },
-        defaultValue: ['140000', '140300', '140302'],
-    },
-    {
-        field: 'field2',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'pca1',
-        component: 'JAreaSelect',
-        label: '省市区级联',
-        helpMessage: ['component模式'],
-        defaultValue: '140302',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'pca1',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'pop1',
-        component: 'Input',
-        label: 'JPopup示例',
-        helpMessage: ['插槽模式'],
-        slot: 'JPopup',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'pop1',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {
-            span: 12,
-        },
-    },
-    {
-        field: 'JInputPop',
-        component: 'JInputPop',
-        label: 'JInputPop',
-        helpMessage: ['component模式'],
-        colProps: {span: 12},
-    },
-    {
-        field: 'JInputPop',
-        component: 'JEllipsis',
-        label: '输入值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'JTreeDictAsync',
-        component: 'JTreeDict',
-        label: '异步JTreeDict',
-        helpMessage: ['component模式'],
-        colProps: {span: 12},
-        componentProps: {async: true},
-    },
-    {
-        field: 'JTreeDictAsync',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'JSwitch',
-        component: 'JSwitch',
-        label: 'JSwitch',
-        helpMessage: ['component模式'],
-        colProps: {span: 12},
-    },
-    {
-        field: 'JSwitch',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    },
-    {
-        field: 'JSwitchSelect',
-        component: 'JSwitch',
-        label: 'JSwitchSelect',
-        helpMessage: ['component模式'],
-        colProps: {span: 12},
-        componentProps: {query: true},
-    },
-    {
-        field: 'JSwitchSelect',
-        component: 'JEllipsis',
-        label: '选中值',
-        colProps: {span: 12},
-    }];
+  {
+    field: 'field0',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'field2',
+    component: 'JAreaLinkage',
+    label: '禁用组件(方式二)',
+    helpMessage: ['component模式'],
+    colProps: {
+      span: 12,
+    },
+    dynamicDisabled: ({ values }) => {
+      console.log(values);
+      return isDisabledAuth(['demo.dbarray']);
+    },
+    defaultValue: ['140000', '140300', '140302'],
+  },
+  {
+    field: 'field2',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'pca1',
+    component: 'JAreaSelect',
+    label: '省市区级联',
+    helpMessage: ['component模式'],
+    defaultValue: '140302',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'pca1',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'pop1',
+    component: 'Input',
+    label: 'JPopup示例',
+    helpMessage: ['插槽模式'],
+    slot: 'JPopup',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'pop1',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: {
+      span: 12,
+    },
+  },
+  {
+    field: 'JInputPop',
+    component: 'JInputPop',
+    label: 'JInputPop',
+    helpMessage: ['component模式'],
+    colProps: { span: 12 },
+  },
+  {
+    field: 'JInputPop',
+    component: 'JEllipsis',
+    label: '输入值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'JTreeDictAsync',
+    component: 'JTreeDict',
+    label: '异步JTreeDict',
+    helpMessage: ['component模式'],
+    colProps: { span: 12 },
+    componentProps: { async: true },
+  },
+  {
+    field: 'JTreeDictAsync',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'JSwitch',
+    component: 'JSwitch',
+    label: 'JSwitch',
+    helpMessage: ['component模式'],
+    colProps: { span: 12 },
+  },
+  {
+    field: 'JSwitch',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+  {
+    field: 'JSwitchSelect',
+    component: 'JSwitch',
+    label: 'JSwitchSelect',
+    helpMessage: ['component模式'],
+    colProps: { span: 12 },
+    componentProps: { query: true },
+  },
+  {
+    field: 'JSwitchSelect',
+    component: 'JEllipsis',
+    label: '选中值',
+    colProps: { span: 12 },
+  },
+];

+ 2 - 1
src/views/demo/page/account/setting/BaseSetting.vue

@@ -35,6 +35,7 @@
   import { baseSetschemas } from './data';
   import { useUserStore } from '/@/store/modules/user';
   import { uploadImg } from '/@/api/sys/upload';
+  import {getFileAccessHttpUrl} from '/@/utils/common/compUtils'
 
   export default defineComponent({
     components: {
@@ -63,7 +64,7 @@
 
       const avatar = computed(() => {
         const { avatar } = userStore.getUserInfo;
-        return avatar || headerImg;
+        return getFileAccessHttpUrl(avatar) || headerImg;
       });
 
       function updateAvatar(src: string, data:string) {

+ 120 - 33
src/views/monitor/route/RouteModal.vue

@@ -15,33 +15,57 @@
             </a-form-item>
 
             <a-form-item name="predicates" label="路由条件">
-                <div v-for="(item,index) in router.predicates">
+                <div v-for="(item,index) in router.predicates" >
+                  <!--当name在noKeyRouter时不需要指定key-->
+                   <template v-if="noKeyRouter.includes(item.name)">
+                     <a-divider>{{item.name}}
+                       <DeleteOutlined size="22" @click="removePredicate(router,index)"/>
+                     </a-divider>
+                     <div>
+                       <template v-for="(tag, tagIndx) in item.args">
+                         <a-input ref="inputRef2" v-if="tagIndx==currentTagIndex&&index==currentNameIndex"  type="text" size="small" :style="{ width: '190px' }" v-model:value="state.inputValue" @change="handleInputChange" @blur="handleInputEditConfirm(item,tag,tagIndx)" @keyup.enter="handleInputEditConfirm(item,tag,tagIndx)"/>
+                         <a-tag v-else :key="tag" style="margin-bottom:2px" :closable="true" @close="() => removeTag(item,tag)" @click="editTag(item,tag,tagIndx,index)">
+                           {{ tag }}
+                         </a-tag>
+                       </template>
+                       <a-input ref="inputRef" v-if="state.inputVisible&&index==currentNameIndex" type="text" size="small" :style="{ width: '100px' }" v-model:value="state.inputValue" @change="handleInputChange" @blur="handleInputConfirm(item)" @keyup.enter="handleInputConfirm(item)"/>
+                       <a-tag v-else style="background: #fff; borderStyle: dashed;margin-bottom:2px" @click="showInput(item,index)">
+                         <PlusOutlined size="22" />
+                         新建{{item.name}}
+                       </a-tag>
+                     </div>
+                   </template>
+                  <!--当name不在noKeyRouter时需要指定key-->
+                  <template v-if="!noKeyRouter.includes(item.name)">
                     <a-divider>{{item.name}}
-                        <Icon preIcon="mdi:access-point-remove" size="22" @click="removePredicate(router,index)"/>
+                      <DeleteOutlined size="22" @click="removePredicate(router,index)"/>
                     </a-divider>
                     <div>
-                        <template v-for="(tag, tagIndx) in item.args">
-                            <a-input ref="inputRef2" v-if="tagIndx==currentTagIndex"  type="text" size="small" :style="{ width: '190px' }" v-model:value="state.inputValue" @change="handleInputChange" @blur="handleInputEditConfirm(item,tag,tagIndx)" @keyup.enter="handleInputEditConfirm(item,tag,tagIndx)"/>
-                            <a-tag v-else :key="tag" style="margin-bottom:2px" :closable="true" @close="() => removeTag(item,tag)" @click="editTag(tag,tagIndx)">
-                                {{ tag }}
-                            </a-tag>
+                        <template v-for="(value, key) in item.args">
+                          <a-row>
+                            <a-col :span="5" style="margin-top: 8px">
+                              <span v-if="key=='header'" >Header名称</span>
+                              <span v-if="key=='regexp'">参数值</span>
+                              <span v-if="key=='param'">参数名</span>
+                              <span v-if="key=='name'">参数名</span>
+                            </a-col>
+                            <a-col :span="18">
+                              <a-input :defaultValue="value" placeholder="参数值" style="width: 70%; margin-right: 8px;margin-top: 3px" @change="(e)=>valueChange(e,item.args,key)"/>
+                            </a-col>
+                          </a-row>
                         </template>
-                        <a-input ref="inputRef" v-if="state.inputVisible&&index==currentNameIndex" type="text" size="small" :style="{ width: '100px' }" v-model:value="state.inputValue" @change="handleInputChange" @blur="handleInputConfirm(item)" @keyup.enter="handleInputConfirm(item)"/>
-                        <a-tag v-else style="background: #fff; borderStyle: dashed;margin-bottom:2px" @click="showInput(item,index)">
-                            <Icon type="plus"/>
-                            新建{{item.name}}
-                        </a-tag>
                     </div>
+                   </template>
                 </div>
                 <p class="btn" style="padding-top: 10px">
                     <a-dropdown>
                         <template #overlay>
-                            <a-menu @click="predicatesHandleMenuClick">
-                                <a-menu-item :key="item" v-for="item in tagArray">{{item}}</a-menu-item>
+                            <a-menu >
+                                <a-menu-item :key="item.name" v-for="item in tagArray" @click="predicatesHandleMenuClick(item)">{{item.name}}</a-menu-item>
                             </a-menu>
                         </template>
                         <a-button type="dashed" style="margin-left: 8px;width:100%"> 添加路由条件
-                            <Icon type="down"/>
+                          <DownOutlined :size="22"/>
                         </a-button>
                     </a-dropdown>
                 </p>
@@ -49,15 +73,15 @@
             <a-form-item name="predicates" label="过滤器">
                 <div v-for="(item,index) in router.filters">
                     <a-divider>{{item.name}}
-                        <Icon type="delete" size="22" @click="removeFilter(router,index)"/>
+                        <DeleteOutlined  size="22" @click="removeFilter(router,index)"/>
                     </a-divider>
                     <div v-for="(tag, index) in item.args" :key="tag.key">
                         <a-input v-model:value="tag.key" placeholder="参数键" style="width: 45%; margin-right: 8px"/>
                         <a-input v-model:value="tag.value" placeholder="参数值" style="width: 40%; margin-right: 8px;margin-top: 3px"/>
-                        <Icon class="dynamic-delete-button" type="minus-circle-o" @click="removeFilterParams(item,index)"/>
+                        <CloseOutlined :size="22" @click="removeFilterParams(item,index)"/>
                     </div>
                     <a-button type="dashed" style="margin-left:28%;width: 37%;margin-top:5px" size="small" @click="addFilterParams(item)">
-                        <Icon type="plus"/>
+                        <DownOutlined :size="22"/>
                         添加参数
                     </a-button>
                 </div>
@@ -69,7 +93,7 @@
                             </a-menu>
                         </template>
                         <a-button type="dashed" style="margin-left: 8px;width:100%"> 添加过滤器
-                            <Icon type="down"/>
+                            <DownOutlined />
                         </a-button>
                     </a-dropdown>
                 </p>
@@ -80,9 +104,12 @@
 </template>
 <script lang="ts" setup>
   import { ref, computed, unref, useAttrs, reactive,nextTick } from 'vue';
-  import { BasicDrawer, useDrawerInner } from '/src/components/Drawer';
+  import {BasicDrawer, useDrawerInner} from '/@/components/Drawer';
   import { saveOrUpdateRoute } from './route.api';
-  import { saveOrUpdateTenant } from '../../system/tenant/tenant.api';
+  import {DeleteOutlined} from '@ant-design/icons-vue';
+  import {PlusOutlined} from '@ant-design/icons-vue';
+  import {CloseOutlined} from '@ant-design/icons-vue';
+  import {DownOutlined} from '@ant-design/icons-vue';
   // 声明Emits
   const emit = defineEmits(['register', 'success']);
   const labelCol = reactive({
@@ -114,8 +141,55 @@
       { required: true, message: 'uri不能为空', trigger: 'blur' },
     ],
   };
-  const filterArray = [{ key: 0, name: '熔断器' }, { key: 1, name: '限流过滤器' }];
-  const tagArray = ref(['Path', 'Host', 'Cookie', 'Header', 'Method', 'Query', 'After', 'Before', 'Between', 'RemoteAddr']);
+  const noKeyRouter=["Path","Host","Method","After","Before","Between","RemoteAddr"];
+  const filterArray = [/*{ key: 0, name: '熔断器' },*/ { key: 1, name: '限流过滤器' }];
+  const tagArray = ref([
+    {
+      name:'Header',
+      args:{
+        header:'',
+        regexp:''
+      }
+    },
+    {
+      name:'Query',
+      args:{
+        param:'',
+        regexp:''
+      }
+    },
+    {
+      name:'Method',
+      args:[]
+    },
+    {
+      name:'Host',
+      args:[]
+    },
+    {
+      name:'Cookie',
+      args:{
+        name:'',
+        regexp:''
+      }
+    },
+    {
+      name:'After',
+      args:[]
+    },
+    {
+      name:'Before',
+      args:[]
+    },
+    {
+      name:'Between',
+      args:[]
+    },
+    {
+      name:'RemoteAddr',
+      args:[]
+    }
+  ]);
   const formRef = ref();
   let router = reactive({});
 
@@ -155,13 +229,24 @@
   //添加路由选项
   function predicatesHandleMenuClick(e) {
     router.predicates.push({
-      args: [],
-      name: e.key,
+      args: e.args,
+      name: e.name,
     });
   }
 
-  function editTag(tag, index) {
-    currentTagIndex.value = index;
+  /**
+   * 值修改事件
+   * @param e
+   * @param item
+   * @param key
+   */
+  function valueChange(e,item,key){
+    item[key]=e.target.value
+  }
+
+  function editTag(item,tag, tagIndex,index) {
+    currentNameIndex.value = index;
+    currentTagIndex.value = tagIndex;
     state.inputValue=tag;
     nextTick(() => {
       inputRef2.value.focus();
@@ -239,7 +324,7 @@
           value: 20,
         }],
         name: 'RequestRateLimiter',
-        title: filterArray[1].name,
+        title: filterArray[0].name,
       });
     }
   }
@@ -280,14 +365,16 @@
     await formRef.value.validate().then(() => {
       try {
         setDrawerProps({ confirmLoading: true });
-        console.log('formData', JSON.stringify(router));
-        router.predicates = JSON.stringify(router.predicates);
-        router.filters = JSON.stringify(router.filters);
+        //重新构造表单提交对象,切记不可修改router对象,数组修改为字符串容易造成界面混乱
+        let params=Object.assign({},router,{
+          predicates:JSON.stringify(router.predicates),
+          filters:JSON.stringify(router.filters),
+        })
         //提交表单
-        saveOrUpdateRoute({ router: router }).then(() => {
+        saveOrUpdateRoute({ router: params }).then(() => {
+          closeDrawer();
           //刷新列表
           emit('success');
-          closeDrawer();
         });
 
       } finally {

+ 10 - 1
src/views/system/depart/components/DepartRuleTab.vue

@@ -3,6 +3,7 @@
     <template v-if="treeData.length > 0">
       <BasicTree
           ref="basicTree"
+          class="depart-rule-tree"
           checkable
           :treeData="treeData"
           :checkedKeys="checkedKeys"
@@ -158,4 +159,12 @@ async function toggleCheckALL(flag) {
   await nextTick()
   checkedKeys.value = basicTree.value.getCheckedKeys()
 }
-</script>
+</script>
+
+<style lang="less" scoped>
+
+// 【VUEN-188】解决滚动条不灵敏的问题
+.depart-rule-tree ::v-deep(.scrollbar__bar) {
+  pointer-events: none;
+}
+</style>

+ 6 - 1
src/views/system/menu/menu.data.ts

@@ -6,6 +6,8 @@ import {duplicateCheck} from "../user/user.api";
 import {ajaxGetDictItems} from "./menu.api";
 import {render} from "/@/utils/common/renderUtils";
 import { Select } from 'ant-design-vue';
+import {rules} from '/@/utils/helper/validator'
+
 const isDir = (type) => type === 0;
 const isMenu = (type) => type === 1;
 const isButton = (type) => type === 2;
@@ -131,7 +133,10 @@ export const formSchema: FormSchema[] = [
     label: '访问路径',
     component: 'Input',
     required: true,
-    ifShow: ({ values }) => !(values.component === ComponentTypes.IFrame && values.internalOrExternal),
+    ifShow: ({ values }) => !(values.component === ComponentTypes.IFrame && values.internalOrExternal) && values.menuType !== 2,
+    dynamicRules: ({model, schema}) => {
+      return rules.duplicateCheckRule('sys_permission', 'url', model, schema, true)
+    },
   },
   {
     field: 'component',

+ 1 - 1
src/views/system/notice/DetailModal.vue

@@ -1,5 +1,5 @@
 <template>
-  <BasicModal v-bind="$attrs" @register="registerModal" title="查看详情" :showCancelBtn="false" :showOkBtn="false">
+  <BasicModal v-bind="$attrs" @register="registerModal" title="查看详情" :showCancelBtn="false" :showOkBtn="false" :height="500">
     <iframe :src="frameSrc" class="detail-iframe"/>
   </BasicModal>
 </template>

+ 4 - 1
src/views/system/notice/index.vue

@@ -153,7 +153,10 @@
         {
           label: '撤销',
           ifShow: record.sendStatus == 1,
-          onClick: handleReovke.bind(null, record.id),
+          popConfirm: {
+            title: '确定要撤销吗?',
+            confirm: handleReovke.bind(null, record.id),
+          },
         },
         {
           label: '查看',

+ 1 - 1
src/views/system/position/index.vue

@@ -32,7 +32,7 @@
     import {BasicTable, TableAction} from '/@/components/Table';
     import {useModal} from '/@/components/Modal';
     import {getPositionList, deletePosition, batchDeletePosition,customUpload,getExportUrl,getImportUrl} from './position.api';
-    import {columns, searchFormSchema} from './Position.data';
+    import {columns, searchFormSchema} from './position.data';
     import PositionModal from './PositionModal.vue';
     import {useMessage} from "/@/hooks/web/useMessage";
     import { useListPage } from '/@/hooks/system/useListPage'

+ 4 - 0
src/views/system/position/position.data.ts

@@ -1,4 +1,5 @@
 import { BasicColumn, FormSchema } from '/@/components/Table';
+import {rules} from '/@/utils/helper/validator'
 
 export const columns: BasicColumn[] = [
     {
@@ -61,5 +62,8 @@ export const formSchema: FormSchema[] = [
     dynamicDisabled: ({values}) => {
       return !!values.id;
     },
+    dynamicRules: ({model, schema}) => {
+      return rules.duplicateCheckRule('sys_position', 'code', model, schema, true)
+    },
   }
 ];

+ 57 - 0
src/views/system/role/components/RoleIndexModal.vue

@@ -0,0 +1,57 @@
+<template>
+    <BasicModal v-bind="$attrs" @register="registerModal" title="首页配置" @ok="handleSubmit" width="40%">
+        <BasicForm @register="registerForm"/>
+    </BasicModal>
+</template>
+
+<script lang="ts" setup>
+    import {ref, computed, unref} from 'vue';
+    import {BasicModal, useModalInner} from '/@/components/Modal';
+    import {BasicForm, useForm} from '/@/components/Form/index';
+    import {roleIndexFormSchema} from '../role.data';
+    import {saveOrUpdateRoleIndex, queryIndexByCode} from '../role.api';
+    // Emits声明
+    const emit = defineEmits(['register', 'success']);
+    const isUpdate = ref(true);
+    //表单配置
+    const [registerForm, {resetFields, setFieldsValue, validate}] = useForm({
+        labelWidth: 150,
+        schemas: roleIndexFormSchema,
+        showActionButtonGroup: false,
+    });
+    //表单赋值
+    const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
+        //重置表单
+        await resetFields();
+        setModalProps({confirmLoading: false});
+        setFieldsValue({roleCode: data.roleCode});
+        let res = await queryIndexByCode({roleCode: data.roleCode});
+        isUpdate.value = !!res.result?.id;
+        if (unref(isUpdate)) {
+            //表单赋值
+            await setFieldsValue({
+                ...res.result,
+            });
+        }
+    });
+
+    //表单提交事件
+    async function handleSubmit(v) {
+        try {
+            let values = await validate();
+            setModalProps({confirmLoading: true});
+            //提交表单
+            await saveOrUpdateRoleIndex(values, isUpdate.value);
+            //关闭弹窗
+            closeModal();
+            //刷新列表
+            emit('success', {isUpdate: isUpdate.value, values});
+        } finally {
+            setModalProps({confirmLoading: false});
+        }
+    }
+</script>
+
+<style lang="less" scoped>
+
+</style>

+ 5 - 5
src/views/system/role/components/RoleUserTable.vue

@@ -9,8 +9,8 @@
           <template #overlay>
             <a-menu>
               <a-menu-item key="1" @click="batchHandleDelete">
-                <Icon icon="ant-design:delete-outlined"></Icon>
-                删除
+                <Icon icon="bx:bx-unlink"></Icon>
+                取消关联
               </a-menu-item>
             </a-menu>
           </template>
@@ -72,7 +72,7 @@
     canResize: false,
     rowKey: 'id',
     actionColumn: {
-      width: 50,
+      width: 180,
       title: '操作',
       dataIndex: 'action',
       slots: {customRender: 'action'},
@@ -172,9 +172,9 @@
         onClick: handleEdit.bind(null, record),
       },
       {
-        label: '删除',
+        label: '取消关联',
         popConfirm: {
-          title: '是否确认删除',
+          title: '是否确认取消关联',
           confirm: handleDelete.bind(null, record),
         }
       }

+ 13 - 0
src/views/system/role/index.vue

@@ -31,6 +31,8 @@
     <RolePermissionDrawer @register="rolePermissionDrawer"/>
     <!--角色工单授权-->
     <RoleDesignModal @register="registerModal"/>
+    <!--角色工单授权-->
+    <RoleIndexModal @register="registerIndexModal"/>
 </template>
 <script lang="ts" name="system-role" setup>
   import {ref} from 'vue'
@@ -41,6 +43,7 @@
   import RoleDesc from './components/RoleDesc.vue';
   import RolePermissionDrawer from './components/RolePermissionDrawer.vue';
   import RoleDesignModal from './components/RoleDesignModal.vue';
+  import RoleIndexModal from './components/RoleIndexModal.vue';
   import RoleUserTable from './components/RoleUserTable.vue';
   import {columns, searchFormSchema} from './role.data';
   import {list, deleteRole, batchDeleteRole, getExportUrl,getImportUrl} from './role.api';
@@ -49,6 +52,7 @@
   const [roleUserDrawer, {openDrawer:openRoleUserDrawer}] = useDrawer();
   const [registerDrawer, {openDrawer}] = useDrawer();
   const [registerModal, {openModal}] = useModal();
+  const [registerIndexModal, {openModal:openIndexModal}] = useModal();
   const [rolePermissionDrawer, {openDrawer: openRolePermissionDrawer}] = useDrawer();
   const [registerDesc, {openDrawer: openRoleDesc}] = useDrawer();
 
@@ -131,6 +135,12 @@
     openModal(true, {roleId: id});
   }
   /**
+   * 首页配置弹窗
+   */
+  function handleIndexConfig(roleCode) {
+    openIndexModal(true, {roleCode});
+  }
+  /**
    * 角色用户
    */
   function handleUser(record) {
@@ -171,6 +181,9 @@
           title: '是否确认删除',
           confirm: handleDelete.bind(null, record),
         },
+      },{
+        label: '首页配置',
+        onClick: handleIndexConfig.bind(null, record.roleCode),
       }, {
         label: '工单?',
         onClick: handleDesign.bind(null, record.id),

+ 18 - 0
src/views/system/role/role.api.ts

@@ -21,6 +21,9 @@ enum Api {
   deleteUserRole = '/sys/user/deleteUserRole',
   batchDeleteUserRole = '/sys/user/deleteUserRoleBatch',
   addUserRole = '/sys/user/addSysUserRole',
+  saveRoleIndex='/sys/sysRoleIndex/add',
+  editRoleIndex='/sys/sysRoleIndex/edit',
+  queryIndexByCode='/sys/sysRoleIndex/queryByCode',
 }
 /**
  * 导出api
@@ -157,3 +160,18 @@ export const addUserRole = (params, handleSuccess) => {
     handleSuccess();
   });
 }
+/**
+ * 保存或者更新
+ * @param params
+ * @param isUpdate 是否是更新数据
+ */
+export const saveOrUpdateRoleIndex = (params, isUpdate) => {
+    let url = isUpdate ? Api.editRoleIndex : Api.saveRoleIndex;
+    return defHttp.post({url: url, params});
+}
+/**
+ * 根据code查询首页配置
+ * @param params
+ */
+export const queryIndexByCode = (params) =>
+    defHttp.get({url: Api.queryIndexByCode, params}, {isTransformResponse: false});

+ 33 - 3
src/views/system/role/role.data.ts

@@ -24,12 +24,10 @@ export const userColumns = [
   {
     title: '用户账号',
     dataIndex: 'username',
-    width: 100,
   },
   {
     title: '用户姓名',
     dataIndex: 'realname',
-    width: 100,
   },
   {
     title: '状态',
@@ -125,4 +123,36 @@ export const formDescSchema = [
   }
 ];
 
-
+export const roleIndexFormSchema: FormSchema[] = [
+    {
+        field: 'id',
+        label: '',
+        component: 'Input',
+        show:false
+    },
+    {
+        label: '角色编码',
+        field: 'roleCode',
+        component: 'Input',
+        dynamicDisabled: true
+    },
+    {
+        label: '首页路由',
+        field: 'url',
+        component: 'Input',
+        required:true
+    },
+    {
+        label: '优先级',
+        field: 'priority',
+        component: 'InputNumber'
+    },
+    {
+        label: '是否开启',
+        field: 'status',
+        component: 'JSwitch',
+        componentProps:{
+            options	: ['1', '0'], 
+       }
+    }
+];

+ 5 - 1
src/views/system/tenant/TenantModal.vue

@@ -13,7 +13,7 @@
     const emit = defineEmits(['register','success']);
     const isUpdate = ref(true);
     //表单配置
-    const [registerForm, {resetFields, setFieldsValue, validate}] = useForm({
+    const [registerForm, {resetFields, setFieldsValue, validate, updateSchema}] = useForm({
         labelWidth: 150,
         schemas: formSchema,
         showActionButtonGroup: false,
@@ -25,12 +25,16 @@
         setModalProps({confirmLoading: false});
         isUpdate.value = !!data?.isUpdate;
         if (unref(isUpdate)) {
+            // 编辑模式下禁用id字段
+            updateSchema({field: 'id', dynamicDisabled: true})
             //获取详情
             data.record = await getTenantById({id: data.record.id});
             //表单赋值
             await setFieldsValue({
                 ...data.record,
             });
+        } else {
+          updateSchema({field: 'id', dynamicDisabled: false})
         }
     });
     //设置标题

+ 1 - 0
src/views/system/tenant/index.vue

@@ -67,6 +67,7 @@
         label: '删除',
         popConfirm: {
           title: '是否确认删除',
+          placement: 'left',
           confirm: handleDelete.bind(null, record),
         },
       },

+ 2 - 5
src/views/system/tenant/tenant.data.ts

@@ -50,8 +50,8 @@ export const searchFormSchema: FormSchema[] = [
     component: 'Select',
     componentProps: {
       options: [
-        { label: '启用', value: 1 },
-        { label: '停用', value: 0 },
+        { label: '正常', value: 1 },
+        { label: '冻结', value: 0 },
       ],
     },
     colProps: { span: 8 },
@@ -77,9 +77,6 @@ export const formSchema: FormSchema[] = [
         field: 'id',
         label: '租户编号',
         component: 'InputNumber',
-        dynamicDisabled: ({values}) => {
-          return !!values.id;
-        },
         required: true
     },
     {

+ 7 - 3
src/views/system/user/UserDrawer.vue

@@ -1,5 +1,5 @@
 <template>
-  <BasicDrawer v-bind="$attrs" @register="registerDrawer" :title="getTitle" width="600" @ok="handleSubmit" destroyOnClose>
+  <BasicDrawer v-bind="$attrs" @register="registerDrawer" :title="getTitle" :width="adaptiveWidth" @ok="handleSubmit" :showFooter="showFooter" destroyOnClose>
     <BasicForm @register="registerForm" />
   </BasicDrawer>
 </template>
@@ -9,6 +9,7 @@
   import {formSchema} from './user.data';
   import {BasicDrawer, useDrawerInner} from '/@/components/Drawer';
   import {saveOrUpdateUser,getUserRoles,getUserDepartList} from './user.api';
+  import {useDrawerAdaptiveWidth} from '/@/hooks/jeecg/useAdaptiveWidth'
   // 声明Emits
   const emit = defineEmits(['success', 'register']);
   const attrs = useAttrs()
@@ -21,11 +22,13 @@
     schemas: formSchema,
     showActionButtonGroup: false,
   });
+  // TODO [VUEN-527] https://www.teambition.com/task/6239beb894b358003fe93626
+  const showFooter = ref(true)
   //表单赋值
   const [registerDrawer, {setDrawerProps, closeDrawer}] = useDrawerInner(async (data) => {
     await resetFields();
-    let showFooter = data?.showFooter ?? true
-    setDrawerProps({ confirmLoading: false, showFooter })
+    showFooter.value = data?.showFooter ?? true
+    setDrawerProps({ confirmLoading: false, showFooter: showFooter.value })
     isUpdate.value = !!data?.isUpdate;
     if (unref(isUpdate)) {
       rowId.value = data.record.id;
@@ -96,6 +99,7 @@
   });
   //获取标题
   const getTitle = computed(() => (!unref(isUpdate) ? '新增用户' : '编辑用户'));
+  const {adaptiveWidth} = useDrawerAdaptiveWidth()
 
   //提交事件
   async function handleSubmit() {

+ 11 - 1
src/views/system/user/UserRecycleBinModal.vue

@@ -34,6 +34,9 @@
   import {BasicTable, useTable, TableAction} from '/@/components/Table';
   import {recycleColumns} from './user.data';
   import {getRecycleBinList, putRecycleBin, deleteRecycleBin} from './user.api';
+  import {useMessage} from '/@/hooks/web/useMessage'
+
+  const {createConfirm} = useMessage()
   // 声明Emits
   const emit = defineEmits(['success', 'register']);
   const checkedKeys = ref<Array<string | number>>([]);
@@ -100,7 +103,14 @@
    * 批量删除事件
    */
   function batchHandleDelete() {
-    handleDelete({id:toRaw(unref(checkedKeys)).join(",")})
+    createConfirm({
+      iconType: 'warning',
+      title: '删除',
+      content: '确定要永久删除吗?删除后将不可恢复!',
+      onOk: () => handleDelete({id: toRaw(unref(checkedKeys)).join(',')}),
+      onCancel() {
+      },
+    })
   }
   //获取操作栏事件
   function getTableAction(record) {

+ 1 - 1
src/views/system/user/user.data.ts

@@ -176,7 +176,7 @@ export const formSchema: FormSchema[] = [
   {
     label: '职务',
     field: 'post',
-    required: true,
+    required: false,
     component: 'JSelectPosition',
     componentProps: {
         rowKey:'code',

+ 1 - 1
types/config.d.ts

@@ -169,7 +169,7 @@ export interface GlobEnvConfig {
   //是否开启微应用模式
   VITE_GLOB_APP_OPEN_QIANKUN: string;
   //单点服务端地址
-  VITE_GLOBE_APP_CAS_BASE_URL: string;
+  VITE_GLOB_APP_CAS_BASE_URL: string;
   VITE_GLOB_DOMAIN_URL: string;
   // Upload url
   VITE_GLOB_UPLOAD_URL?: string;

+ 1 - 0
types/store.d.ts

@@ -40,6 +40,7 @@ export interface UserInfo {
   homePath?: string;
   tenantid?: string | number;
   roles: RoleInfo[];
+  orgCode?: string;
 }
 
 export interface LoginInfo {

+ 302 - 48
yarn.lock

@@ -920,9 +920,9 @@
     regenerator-runtime "^0.13.4"
 
 "@babel/runtime@^7.7.2":
-  version "7.17.2"
-  resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941"
-  integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
+  version "7.17.9"
+  resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72"
+  integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==
   dependencies:
     regenerator-runtime "^0.13.4"
 
@@ -1206,53 +1206,53 @@
     minimatch "^3.0.4"
     strip-json-comments "^3.1.1"
 
-"@fullcalendar/common@~5.10.1":
-  version "5.10.1"
-  resolved "https://registry.npmmirror.com/@fullcalendar/common/download/@fullcalendar/common-5.10.1.tgz#a019951743852277a4095e536fd7716f6f85b9aa"
-  integrity sha1-oBmVF0OFInekCV5Tb9dxb2+Fuao=
+"@fullcalendar/common@~5.11.0":
+  version "5.11.0"
+  resolved "https://registry.npmmirror.com/@fullcalendar/common/-/common-5.11.0.tgz#d7e47b4272ba6824f95581fb7fb630c5880f4212"
+  integrity sha512-gWjbMAnN1u73Oqlgjbyky7i+3bY0hvFSnGT0YBPx44n874AkQa9e9OU12PMLTMOPy0tXPb8DEwRelFQ7CJNbcw==
   dependencies:
     tslib "^2.1.0"
 
-"@fullcalendar/core@^5.8.0", "@fullcalendar/core@~5.10.1":
-  version "5.10.1"
-  resolved "https://registry.npmmirror.com/@fullcalendar/core/download/@fullcalendar/core-5.10.1.tgz#05c7653e5004149a63bcb03e9dc8b049186ca50b"
-  integrity sha1-BcdlPlAEFJpjvLA+nciwSRhspQs=
+"@fullcalendar/core@^5.10.1", "@fullcalendar/core@~5.11.0":
+  version "5.11.0"
+  resolved "https://registry.npmmirror.com/@fullcalendar/core/-/core-5.11.0.tgz#336fcfb9799cf0c2d1ee15c110dce26a9e14881c"
+  integrity sha512-cF/d9LuJb/6xw14ms0urv1H4BiA70c4jrufe/EuVzLR6qTDpK92IZ3JK7GVtZtzptfTYZ/NQgDV9YCzIaO9Blw==
   dependencies:
-    "@fullcalendar/common" "~5.10.1"
+    "@fullcalendar/common" "~5.11.0"
     preact "^10.0.5"
     tslib "^2.1.0"
 
-"@fullcalendar/daygrid@^5.8.0", "@fullcalendar/daygrid@~5.10.1":
-  version "5.10.1"
-  resolved "https://registry.npmmirror.com/@fullcalendar/daygrid/download/@fullcalendar/daygrid-5.10.1.tgz#bdee4f58364fdab631b2abf8b56094ab5776f203"
-  integrity sha1-ve5PWDZP2rYxsqv4tWCUq1d28gM=
+"@fullcalendar/daygrid@^5.10.1", "@fullcalendar/daygrid@~5.11.0":
+  version "5.11.0"
+  resolved "https://registry.npmmirror.com/@fullcalendar/daygrid/-/daygrid-5.11.0.tgz#fd10391dfc2b6caa3538e0826f8d8f3e83e0e22a"
+  integrity sha512-Ybh/dfHn/VL0qOVIRVyJc9I8oYiqqHl6xQONk8xXCe456QbPzAQLsAxpLLJLH+3smWNCfoQgvDKzR9e9XTzLMQ==
   dependencies:
-    "@fullcalendar/common" "~5.10.1"
+    "@fullcalendar/common" "~5.11.0"
     tslib "^2.1.0"
 
-"@fullcalendar/interaction@^5.8.0":
-  version "5.10.1"
-  resolved "https://registry.npmmirror.com/@fullcalendar/interaction/download/@fullcalendar/interaction-5.10.1.tgz#dfa74b5c50bbd5608eb50aeab6e579c8d20cb367"
-  integrity sha1-36dLXFC71WCOtQrqtuV5yNIMs2c=
+"@fullcalendar/interaction@^5.10.1":
+  version "5.11.0"
+  resolved "https://registry.npmmirror.com/@fullcalendar/interaction/-/interaction-5.11.0.tgz#9cfb46cc5f75690394ef553d2c5cf4955782ff05"
+  integrity sha512-9XTI5+ydqrSX+IL3iWgKBURXfnPewn57Tmsv9mJZhiqrUEF7/+qtftLoKEAc8ZdWk/+01aBe67PFL16uPXj2Jg==
   dependencies:
-    "@fullcalendar/common" "~5.10.1"
+    "@fullcalendar/common" "~5.11.0"
     tslib "^2.1.0"
 
-"@fullcalendar/timegrid@^5.8.0":
-  version "5.10.1"
-  resolved "https://registry.npmmirror.com/@fullcalendar/timegrid/download/@fullcalendar/timegrid-5.10.1.tgz#fa7feb909bf599eac1466b9e70c0d56ce0d1aefc"
-  integrity sha1-+n/rkJv1merBRmuecMDVbODRrvw=
+"@fullcalendar/timegrid@^5.10.1":
+  version "5.11.0"
+  resolved "https://registry.npmmirror.com/@fullcalendar/timegrid/-/timegrid-5.11.0.tgz#b41153ef301f21f8c1e19a65023493c48c8bacf5"
+  integrity sha512-GNy+/PwAj510PS4Fu18Mf/CytNBVftFU7M8XwsUXOCJ6ouyroHZje0a7k5cH/nE5IQ6NJZfH2eAPBlxGath1MQ==
   dependencies:
-    "@fullcalendar/common" "~5.10.1"
-    "@fullcalendar/daygrid" "~5.10.1"
+    "@fullcalendar/common" "~5.11.0"
+    "@fullcalendar/daygrid" "~5.11.0"
     tslib "^2.1.0"
 
-"@fullcalendar/vue3@^5.8.0":
-  version "5.10.1"
-  resolved "https://registry.npmmirror.com/@fullcalendar/vue3/download/@fullcalendar/vue3-5.10.1.tgz#b34adcaf7837256a5aa9240592a0d97b55eb675d"
-  integrity sha1-s0rcr3g3JWpaqSQFkqDZe1XrZ10=
+"@fullcalendar/vue3@^5.10.1":
+  version "5.11.0"
+  resolved "https://registry.npmmirror.com/@fullcalendar/vue3/-/vue3-5.11.0.tgz#51b737e91aadab53a03162a8ade9f017429ba1cf"
+  integrity sha512-vqRJRItEdIdtO+6B3/jNppF9dU1zkZVuEL+YiXmuqH3bwqrOH0xK5ALm7YfPJvZJq9TnrVI2HPaVl9D49Mxr7g==
   dependencies:
-    "@fullcalendar/core" "~5.10.1"
+    "@fullcalendar/core" "~5.11.0"
     tslib "^2.1.0"
 
 "@humanwhocodes/config-array@^0.6.0":
@@ -1293,6 +1293,122 @@
   resolved "https://registry.npmmirror.com/@iconify/json/download/@iconify/json-1.1.423.tgz#3507db88e10a58b63f78e817200f07eacb1a2c1e"
   integrity sha1-NQfbiOEKWLY/eOgXIA8H6ssaLB4=
 
+"@interactjs/actions@1.10.11", "@interactjs/actions@^1.10.2":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/actions/-/actions-1.10.11.tgz#ec68fd60bee751f80c650964b5ba299eb6afe78c"
+  integrity sha512-P39zeefr4hkmKx+5nZ+mrH1s0l2YJ3gIHrthXmE81n6MlMa42m0WtHcTms4C5JTTNBP2EEDY+KGgGxSnmJKvUw==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.11"
+
+"@interactjs/auto-scroll@1.10.11":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/auto-scroll/-/auto-scroll-1.10.11.tgz#0c0ac7dbb55aa7d7df6c0a04c77ebb3148cbdf54"
+  integrity sha512-feHNjhi0EMNLV2nQcEgjYPz2mI54aeSW2RiaoNtFLyBvtXKp0b4DmluwDv6DvuXmUpDwD5g/Hk1gGM2rgl7iqQ==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.11"
+
+"@interactjs/auto-start@1.10.11", "@interactjs/auto-start@^1.10.2":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/auto-start/-/auto-start-1.10.11.tgz#5ce045740f35be36640ebad053db7b5652e18e70"
+  integrity sha512-cIg5CcalCPtC6AiGq6j/0hKUtL2MweEpvw12FuB19sz2Q9Dye0J4GliHKhOYvtumNinnvfVAZ4FZMqZEuX7YZA==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.11"
+
+"@interactjs/core@1.10.11":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/core/-/core-1.10.11.tgz#8b0203492c1ba6f8432f20b718ae53707fcfc724"
+  integrity sha512-aJ50ccVeszpJt7wPH7Yfqm7f1aG1SA94qd90P0NaESh5/QUXn4CESO6igobo4DFHQ5z+1Rfdl8aphP4JxlH4gw==
+
+"@interactjs/dev-tools@1.10.11", "@interactjs/dev-tools@^1.10.2":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/dev-tools/-/dev-tools-1.10.11.tgz#8d4b5b650cf74e800909f52962008700143a4304"
+  integrity sha512-BP2FNfMbF7zLuOAUGMkDhCo1e1B0fnqyb9ih/Y8yAIJuoLrZxP/9htbsS1vZOIVZ4UgtrId4cYOwfcAZBMQtmw==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.11"
+
+"@interactjs/inertia@1.10.11":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/inertia/-/inertia-1.10.11.tgz#58864173310985b8247d84e347148ea6cd7b88a8"
+  integrity sha512-h+sknCzRqBSyHy4ctPNsq56mxkAMMdwHWD6en7rDEw899gdGKYaXVDVdv1jMfiwNRw0eRFBNoCiol8r3a/a3Jw==
+  dependencies:
+    "@interactjs/offset" "1.10.11"
+  optionalDependencies:
+    "@interactjs/interact" "1.10.11"
+
+"@interactjs/interact@1.10.11":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/interact/-/interact-1.10.11.tgz#d96e3f949ee4001a6a34dc363a232646f9dd2b1b"
+  integrity sha512-0iZJ9l547JuBA/lKxK4ARGYVmMqRSsAdA8gXL1zWe51qEIQq8PyWmMipoi8JbDaL7exC2THKwkXu5uq5ndT+iA==
+  dependencies:
+    "@interactjs/core" "1.10.11"
+    "@interactjs/types" "1.10.11"
+    "@interactjs/utils" "1.10.11"
+
+"@interactjs/interactjs@^1.10.2":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/interactjs/-/interactjs-1.10.11.tgz#d0fdd6b03c1c855043b1f608a10b2f5ccac4b4b7"
+  integrity sha512-cGOxf6rp3Y8/sk88LhIT0XDn4gCiCzAnUG5Kkj9SAqiUO6BK/9+Wbp1IBkNaPgl/8uG8gNHh/dXBrlBBNcqJAg==
+  dependencies:
+    "@interactjs/actions" "1.10.11"
+    "@interactjs/auto-scroll" "1.10.11"
+    "@interactjs/auto-start" "1.10.11"
+    "@interactjs/core" "1.10.11"
+    "@interactjs/dev-tools" "1.10.11"
+    "@interactjs/inertia" "1.10.11"
+    "@interactjs/interact" "1.10.11"
+    "@interactjs/modifiers" "1.10.11"
+    "@interactjs/offset" "1.10.11"
+    "@interactjs/pointer-events" "1.10.11"
+    "@interactjs/reflow" "1.10.11"
+    "@interactjs/utils" "1.10.11"
+
+"@interactjs/modifiers@1.10.11", "@interactjs/modifiers@^1.10.2":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/modifiers/-/modifiers-1.10.11.tgz#f40962a97fd3e110e66b79664796c24f3c4e6cd6"
+  integrity sha512-ltqX1RSqeAIikixlQBlyEUdclT5+rbfIGi3sIdLLYaIZQnltYkWqL9MHKx/w5b+hV+Mc0p5MLUFWJbTdkSCZ9g==
+  dependencies:
+    "@interactjs/snappers" "1.10.11"
+  optionalDependencies:
+    "@interactjs/interact" "1.10.11"
+
+"@interactjs/offset@1.10.11":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/offset/-/offset-1.10.11.tgz#512242f330dc80cdbda4feda8fb34c0491f50496"
+  integrity sha512-mBT7eIfy5ivofECiv+VwtEwwIMLV54fT9ujSMWJPduxdSYIHepUWgEf/3zjJknFh6jQc7pqz9dtjvVvyzRCLlQ==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.11"
+
+"@interactjs/pointer-events@1.10.11":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/pointer-events/-/pointer-events-1.10.11.tgz#ff4c74a75d7711fc1006ebf32ea344e35bffe938"
+  integrity sha512-yBT8JJVMZ+MgBay5l1WAHnL8ch/mZsRfaFahti+QFYeQyRloDtsWmEMDSYI/Onyy9+hS3gN/ge77ArGciZZ0Ow==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.11"
+
+"@interactjs/reflow@1.10.11":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/reflow/-/reflow-1.10.11.tgz#43d2ad8ca002bf98091273d179fd70b1cabfb9e2"
+  integrity sha512-NSCtcCkjImOYSbxzzv2kFqR9t49J8KlhEr9UoePc7GyLbNXsiv3WQ3n0ehZd7CgZXQDiVXnP2UnmIOv5Zd4HQg==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.11"
+
+"@interactjs/snappers@1.10.11":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/snappers/-/snappers-1.10.11.tgz#3eb6e45ab8319c0dd4b60b284c55c87561aaadb1"
+  integrity sha512-yYtOMUZ7aFUZ1IYheq9Tj5hZ4J1r5dnaXhLF44WsI/awQ5L0DjZf07GPWof0B+7rZHEVudxyQNbPfFmb+1K94Q==
+  optionalDependencies:
+    "@interactjs/interact" "1.10.11"
+
+"@interactjs/types@1.10.11":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/types/-/types-1.10.11.tgz#29be25d503f9c7842df062fa3cda5b044a47cf2a"
+  integrity sha512-YRsVFWjL8Gkkvlx3qnjeaxW4fnibSJ9791g8BA7Pv5ANByI64WmtR1vU7A2rXcrOn8XvyCEfY0ss1s8NhZP+MA==
+
+"@interactjs/utils@1.10.11":
+  version "1.10.11"
+  resolved "https://registry.npmmirror.com/@interactjs/utils/-/utils-1.10.11.tgz#939d0f128dfa96c673276cca3eb7f313d92daabf"
+  integrity sha512-410ZoxKF+r1roeSelL+WHXfdryUMg5iykC1XwQ3l6XqNw43IMACzyvTH6k6Pwxj7w7x42nce0Qdn1GQ3Y8xyCw==
+
 "@intlify/core-base@9.1.9":
   version "9.1.9"
   resolved "https://registry.npmmirror.com/@intlify/core-base/download/@intlify/core-base-9.1.9.tgz#e4e8c951010728e4af3a0d13d74cf3f9e7add7f6"
@@ -1755,6 +1871,13 @@
   resolved "https://registry.nlark.com/@types/crypto-js/download/@types/crypto-js-4.0.2.tgz?cache=0&sync_timestamp=1629707090206&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fcrypto-js%2Fdownload%2F%40types%2Fcrypto-js-4.0.2.tgz#4524325a175bf819fec6e42560c389ce1fb92c97"
   integrity sha1-RSQyWhdb+Bn+xuQlYMOJzh+5LJc=
 
+"@types/echarts@^4.9.12":
+  version "4.9.14"
+  resolved "https://registry.npmmirror.com/@types/echarts/-/echarts-4.9.14.tgz#41fd4aa286078fa3346bbd92144a704aec5e2b78"
+  integrity sha512-hg2EFjTmiatztvxIFNIQ7tTIdSsbRZJXfBwJlaucvnQKqv4t2AKmpbPXU9rLtrgD/KCpyi1SX7T+WH6uVybmqA==
+  dependencies:
+    "@types/zrender" "*"
+
 "@types/estree@*":
   version "0.0.50"
   resolved "https://registry.nlark.com/@types/estree/download/@types/estree-0.0.50.tgz?cache=0&sync_timestamp=1629707592205&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Festree%2Fdownload%2F%40types%2Festree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
@@ -2045,6 +2168,11 @@
   dependencies:
     "@types/yargs-parser" "*"
 
+"@types/zrender@*":
+  version "4.0.1"
+  resolved "https://registry.npmmirror.com/@types/zrender/-/zrender-4.0.1.tgz#6280c40207927ce086be24b4391d668cd330e956"
+  integrity sha512-IyTRf30jPOXK1+1RChI/78U6aV9hyWYf/vhL96Vt66oDz9es/BDjeKpvbNZSOHVA7zAReOwJcmdZS5AGAqhygw==
+
 "@typescript-eslint/eslint-plugin@^5.1.0":
   version "5.3.0"
   resolved "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/download/@typescript-eslint/eslint-plugin-5.3.0.tgz#a55ae72d28ffeb6badd817fe4566c9cced1f5e29"
@@ -2579,7 +2707,7 @@ ansi-styles@^5.0.0:
   resolved "https://registry.nlark.com/ansi-styles/download/ansi-styles-5.2.0.tgz?cache=0&sync_timestamp=1618995588464&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fansi-styles%2Fdownload%2Fansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
   integrity sha1-B0SWkK1Fd30ZJKwquy/IiV26g2s=
 
-ant-design-vue@2.2.8:
+ant-design-vue@2.2.8, ant-design-vue@^2.2.8:
   version "2.2.8"
   resolved "https://registry.npmmirror.com/ant-design-vue/download/ant-design-vue-2.2.8.tgz#fa87cf6842d8ee9a0d8af393ff4099ecc4072f2b"
   integrity sha1-+ofPaELY7poNivOT/0CZ7MQHLys=
@@ -2784,6 +2912,13 @@ axios@^0.23.0:
   dependencies:
     follow-redirects "^1.14.4"
 
+axios@^0.24.0:
+  version "0.24.0"
+  resolved "https://registry.npmmirror.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
+  integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
+  dependencies:
+    follow-redirects "^1.14.4"
+
 babel-jest@^27.3.1:
   version "27.3.1"
   resolved "https://registry.npmmirror.com/babel-jest/download/babel-jest-27.3.1.tgz?cache=0&sync_timestamp=1634626713402&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fbabel-jest%2Fdownload%2Fbabel-jest-27.3.1.tgz#0636a3404c68e07001e434ac4956d82da8a80022"
@@ -2938,6 +3073,11 @@ basic-auth@^2.0.1:
   dependencies:
     safe-buffer "5.1.2"
 
+batch-processor@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8"
+  integrity sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==
+
 big.js@^5.2.2:
   version "5.2.2"
   resolved "https://registry.nlark.com/big.js/download/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@@ -3498,6 +3638,11 @@ codemirror@^5.63.3:
   resolved "https://registry.npmmirror.com/codemirror/download/codemirror-5.63.3.tgz#97042a242027fe0c87c09b36bc01931d37b76527"
   integrity sha1-lwQqJCAn/gyHwJs2vAGTHTe3ZSc=
 
+codemirror@^5.65.0:
+  version "5.65.3"
+  resolved "https://registry.npmmirror.com/codemirror/-/codemirror-5.65.3.tgz#2d029930d5a293bc5fb96ceea64654803c0d4ac7"
+  integrity sha512-kCC0iwGZOVZXHEKW3NDTObvM7pTIyowjty4BUqeREROc/3I6bWbgZDA3fGDwlA+rbgRjvnRnfqs9SfXynel1AQ==
+
 codepage@~1.15.0:
   version "1.15.0"
   resolved "https://registry.nlark.com/codepage/download/codepage-1.15.0.tgz#2e00519024b39424ec66eeb3ec07227e692618ab"
@@ -3879,6 +4024,11 @@ core-js@^3.15.1, core-js@^3.18.1:
   resolved "https://registry.npmmirror.com/core-js/download/core-js-3.19.1.tgz?cache=0&sync_timestamp=1635888694924&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fcore-js%2Fdownload%2Fcore-js-3.19.1.tgz#f6f173cae23e73a7d88fa23b6e9da329276c6641"
   integrity sha1-9vFzyuI+c6fYj6I7bp2jKSdsZkE=
 
+core-js@^3.6.5, core-js@^3.8.1:
+  version "3.22.2"
+  resolved "https://registry.npmmirror.com/core-js/-/core-js-3.22.2.tgz#3ea0a245b0895fa39d1faa15fe75d91ade504a01"
+  integrity sha512-Z5I2vzDnEIqO2YhELVMFcL1An2CIsFe9Q7byZhs8c/QxummxZlAHw33TUHbIte987LkisOgL0LwQ1P9D6VISnA==
+
 core-util-is@~1.0.0:
   version "1.0.3"
   resolved "https://registry.nlark.com/core-util-is/download/core-util-is-1.0.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcore-util-is%2Fdownload%2Fcore-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
@@ -4125,6 +4275,13 @@ debug@^3.1.1, debug@^3.2.6:
   dependencies:
     ms "^2.1.1"
 
+debug@^4.3.3:
+  version "4.3.4"
+  resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+  dependencies:
+    ms "2.1.2"
+
 decamelize-keys@^1.1.0:
   version "1.1.0"
   resolved "https://registry.nlark.com/decamelize-keys/download/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
@@ -4471,6 +4628,14 @@ echarts@^5.2.1:
     tslib "2.3.0"
     zrender "5.2.1"
 
+echarts@^5.2.2:
+  version "5.3.2"
+  resolved "https://registry.npmmirror.com/echarts/-/echarts-5.3.2.tgz#0a7b3be8c48a48b2e7cb1b82121df0c208d42d2c"
+  integrity sha512-LWCt7ohOKdJqyiBJ0OGBmE9szLdfA9sGcsMEi+GGoc6+Xo75C+BkcT/6NNGRHAWtnQl2fNow05AQjznpap28TQ==
+  dependencies:
+    tslib "2.3.0"
+    zrender "5.3.1"
+
 ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.nlark.com/ee-first/download/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -4493,6 +4658,13 @@ electron-to-chromium@^1.3.886:
   resolved "https://registry.npmmirror.com/electron-to-chromium/download/electron-to-chromium-1.3.887.tgz?cache=0&sync_timestamp=1635879962653&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Felectron-to-chromium%2Fdownload%2Felectron-to-chromium-1.3.887.tgz#b36aeed12a28aaa19460a467823f5bbe1f3c6f06"
   integrity sha1-s2ru0SooqqGUYKRngj9bvh88bwY=
 
+element-resize-detector@^1.2.1:
+  version "1.2.4"
+  resolved "https://registry.npmmirror.com/element-resize-detector/-/element-resize-detector-1.2.4.tgz#3e6c5982dd77508b5fa7e6d5c02170e26325c9b1"
+  integrity sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==
+  dependencies:
+    batch-processor "1.0.0"
+
 emittery@^0.8.1:
   version "0.8.1"
   resolved "https://registry.nlark.com/emittery/download/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860"
@@ -6258,9 +6430,9 @@ import-fresh@^3.0.0, import-fresh@^3.2.1:
     resolve-from "^4.0.0"
 
 import-html-entry@^1.9.0:
-  version "1.11.1"
-  resolved "https://registry.npmjs.org/import-html-entry/-/import-html-entry-1.11.1.tgz#3d8c5977926bdd122ab8e658965c102068b4af8d"
-  integrity sha512-O7mCUTwKdYU49/LH6nq1adWPnUlZQpKeGWIEcDq07KTcqP/v0jBLEIVc0oE0Mtlw3CEe0eeKGMyhl6LwfXCV7A==
+  version "1.12.0"
+  resolved "https://registry.npmmirror.com/import-html-entry/-/import-html-entry-1.12.0.tgz#460dff3cd86a9774f2ae9fc44bdce3577d30a235"
+  integrity sha512-wloMEMwupKJ8DWvKsEzJTXhHVieEH8ylu9ebeQg7T9JUsPTo0Zwa1EkuSKgKJvOiA2MxAFkeYYvd/E2pKiFtWQ==
   dependencies:
     "@babel/runtime" "^7.7.2"
 
@@ -8123,6 +8295,11 @@ minimist@1.2.5, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
   resolved "https://registry.npmmirror.com/minimist/download/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
   integrity sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=
 
+mitt@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/mitt/-/mitt-2.1.0.tgz#f740577c23176c6205b121b2973514eade1b2230"
+  integrity sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==
+
 mixin-deep@^1.2.0:
   version "1.3.2"
   resolved "https://registry.nlark.com/mixin-deep/download/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
@@ -9337,9 +9514,9 @@ q@^1.5.1:
   integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
 
 qiankun@^2.5.1:
-  version "2.6.3"
-  resolved "https://registry.npmjs.org/qiankun/-/qiankun-2.6.3.tgz#00c55a3d6655b2a78b6e0578d70c694cdb662073"
-  integrity sha512-h1NIokwjdt508HNPcWBdzoYFDJvhbpUUlFSa5dDkpJYVCl55iqqHgdyi1YayinmLmr/9s/zD+WQv+A2mbzkMQw==
+  version "2.7.0"
+  resolved "https://registry.npmmirror.com/qiankun/-/qiankun-2.7.0.tgz#2054e1be05ddfbf1eb7658125075bc9f04afd37e"
+  integrity sha512-MvsRNsu/2dql9un6qWRz/uNsyrFKa2b21Yxpb6UQpNu1pAl16xFozLDufBqiv/RbaGLI+f37VMwofi8n6WpP3w==
   dependencies:
     "@babel/runtime" "^7.10.5"
     import-html-entry "^1.9.0"
@@ -10005,7 +10182,7 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
 
 single-spa@^5.9.2:
   version "5.9.3"
-  resolved "https://registry.npmjs.org/single-spa/-/single-spa-5.9.3.tgz#2d151cbb3b273629a5b27b30a3b8ca847dcba4c5"
+  resolved "https://registry.npmmirror.com/single-spa/-/single-spa-5.9.3.tgz#2d151cbb3b273629a5b27b30a3b8ca847dcba4c5"
   integrity sha512-qMGraRzIBsodV6569Fob4cQ4/yQNrcZ5Achh3SAQDljmqUtjAZ7BAA7GAyO/l5eizb7GtTmVq9Di7ORyKw82CQ==
 
 sisteransi@^1.0.5:
@@ -10940,7 +11117,7 @@ tslib@2.3.0:
 
 tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0:
   version "1.14.1"
-  resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+  resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
   integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 
 tslib@^2, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0:
@@ -11351,6 +11528,21 @@ vite-plugin-mock@^2.9.6:
     fast-glob "^3.2.7"
     path-to-regexp "^6.2.0"
 
+vite-plugin-optimize-persist@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.npmmirror.com/vite-plugin-optimize-persist/-/vite-plugin-optimize-persist-0.1.2.tgz#c2aa2712afa74db55f580e3d1656e8cc0b783019"
+  integrity sha512-H/Ebn2kZO8PvwUF08SsT5K5xMJNCWKoGX71+e9/ER3yNj7GHiFjNQlvGg5ih/zEx09MZ9m7WCxOwmEKbeIVzww==
+  dependencies:
+    debug "^4.3.2"
+    fs-extra "^10.0.0"
+
+vite-plugin-package-config@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.npmmirror.com/vite-plugin-package-config/-/vite-plugin-package-config-0.1.1.tgz#6bd579f71db7582ef9dcc05e9f7920e689c498c9"
+  integrity sha512-w9B3I8ZnqoyhlbzimXjXNk85imrMZgvI9m8f6j3zonK5IVA5KXzpT+PZOHlDz8lqh1vqvoEI1uhy+ZDoLAiA/w==
+  dependencies:
+    debug "^4.3.3"
+
 vite-plugin-purge-icons@^0.7.0:
   version "0.7.0"
   resolved "https://registry.npm.taobao.org/vite-plugin-purge-icons/download/vite-plugin-purge-icons-0.7.0.tgz#c460037438fd71372153360ccb9e7d97b030fb58"
@@ -11575,6 +11767,19 @@ vscode-vue-languageservice@0.28.10:
     vscode-pug-languageservice "0.28.10"
     vscode-typescript-languageservice "0.28.10"
 
+vue-count-to2@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.npmmirror.com/vue-count-to2/-/vue-count-to2-1.0.6.tgz#4685ef8d7867efc793a375d31af0155e012441e8"
+  integrity sha512-Mm56aA2nxgw/qTfU2XaauUh1W4bdM6lIeihhDIahQOGNUxVPVARyvLUbUFbOrynOJ5Ab26fuTguspX+xBGhVKw==
+  dependencies:
+    core-js "^3.8.1"
+    vue-count-to "^1.0.13"
+
+vue-count-to@^1.0.13:
+  version "1.0.13"
+  resolved "https://registry.npmmirror.com/vue-count-to/-/vue-count-to-1.0.13.tgz#3e7573ea6e64c2b2972f64e0a2ab2e23c7590ff3"
+  integrity sha512-6R4OVBVNtQTlcbXu6SJ8ENR35M2/CdWt3Jmv57jOUM+1ojiFmjVGvZPH8DfHpMDSA+ITs+EW5V6qthADxeyYOQ==
+
 vue-cropper@^0.5.6:
   version "0.5.7"
   resolved "https://registry.npmmirror.com/vue-cropper/download/vue-cropper-0.5.7.tgz#56103ada05996ec5dcc9f25d24eb7701fda9fe6c"
@@ -11620,6 +11825,19 @@ vue-eslint-parser@^8.0.0:
     lodash "^4.17.21"
     semver "^7.3.5"
 
+vue-grid-layout@^3.0.0-beta1:
+  version "3.0.0-beta1"
+  resolved "https://registry.npmmirror.com/vue-grid-layout/-/vue-grid-layout-3.0.0-beta1.tgz#f8ce8eb7dbb1ff58f64820332afcc12c8cf873aa"
+  integrity sha512-MsW0yfYNtnAO/uDhfZvkP6effxSJxvhAFbIL37x6Rn3vW9xf0WHVefKaSbQMLpSq3mXnR6ut0pg2Cd5lqIIZzg==
+  dependencies:
+    "@interactjs/actions" "^1.10.2"
+    "@interactjs/auto-start" "^1.10.2"
+    "@interactjs/dev-tools" "^1.10.2"
+    "@interactjs/interactjs" "^1.10.2"
+    "@interactjs/modifiers" "^1.10.2"
+    element-resize-detector "^1.2.1"
+    mitt "^2.1.0"
+
 vue-i18n@^9.1.9:
   version "9.1.9"
   resolved "https://registry.npmmirror.com/vue-i18n/download/vue-i18n-9.1.9.tgz?cache=0&sync_timestamp=1635872482815&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fvue-i18n%2Fdownload%2Fvue-i18n-9.1.9.tgz#cb53e06ab5cc5b7eed59332f151caf48d47be9bb"
@@ -11641,9 +11859,9 @@ vue-json-pretty@^2.0.4:
   integrity sha1-L25/WahODh70HdM2Y8Ph0alzSik=
 
 vue-print-nb-jeecg@^1.0.10:
-  version "1.0.10"
-  resolved "https://registry.npmjs.org/vue-print-nb-jeecg/-/vue-print-nb-jeecg-1.0.10.tgz#d8d76342d053ac18c3b570ba6c4364ee9b93fa71"
-  integrity sha512-eP4lR7T8wNZ+/nZUOGG61+9UwNyTFQUYbcRkwgpOC8qEudepmGa+K9iGYzSrscx9G+E0YZDpIdlvAlIQoURCSA==
+  version "1.0.11"
+  resolved "https://packages.aliyun.com/60e053ce4690c27532d3dfd3/npm/npm-registry/vue-print-nb-jeecg/-/vue-print-nb-jeecg-1.0.11.tgz#63d1e0802facae02dd6482454debe998f6779d07"
+  integrity sha512-jp/7tnKm8A+vYsUqMY+8ODOi8WayjEYkf/r1Z7enUfMe3Rk9ytGIJonAhnwLBWMoH7mWndgMayOPMki1dFUlGA==
   dependencies:
     babel-plugin-transform-runtime "^6.23.0"
 
@@ -11676,6 +11894,30 @@ vue-types@^4.1.1:
   dependencies:
     is-plain-object "5.0.0"
 
+vue3-drag-test4@0.1.20:
+  version "0.1.20"
+  resolved "https://packages.aliyun.com/60e053ce4690c27532d3dfd3/npm/npm-registry/vue3-drag-test4/-/vue3-drag-test4-0.1.20.tgz#27bd632707a6428863facaed15bf9ca553fe18f5"
+  integrity sha512-RtAcE2jVZbsboe7sMVSmSK9PyYddiv+LeYSORnkUrAzOYxcZvzsxAKOJNkRhy5gxOKBqcKJ9K3t6Aqk6dEQxKQ==
+  dependencies:
+    "@fullcalendar/core" "^5.10.1"
+    "@fullcalendar/daygrid" "^5.10.1"
+    "@fullcalendar/interaction" "^5.10.1"
+    "@fullcalendar/timegrid" "^5.10.1"
+    "@fullcalendar/vue3" "^5.10.1"
+    "@types/echarts" "^4.9.12"
+    "@vueuse/core" "^6.6.2"
+    ant-design-vue "^2.2.8"
+    axios "^0.24.0"
+    codemirror "^5.65.0"
+    core-js "^3.6.5"
+    echarts "^5.2.2"
+    lodash-es "^4.17.21"
+    vue "^3.2.20"
+    vue-count-to2 "^1.0.6"
+    vue-grid-layout "^3.0.0-beta1"
+    xlsx "^0.17.3"
+    yarn "^1.22.17"
+
 vue@^3.2.20:
   version "3.2.21"
   resolved "https://registry.npmmirror.com/vue/download/vue-3.2.21.tgz?cache=0&sync_timestamp=1635835999987&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fvue%2Fdownload%2Fvue-3.2.21.tgz#55f5665172d95cf97e806b9aad0a375180be23a1"
@@ -11689,8 +11931,8 @@ vue@^3.2.20:
 
 vuedraggable@^4.1.0:
   version "4.1.0"
-  resolved "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz#edece68adb8a4d9e06accff9dfc9040e66852270"
-  integrity sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==
+  resolved "https://registry.nlark.com/vuedraggable/download/vuedraggable-4.1.0.tgz#edece68adb8a4d9e06accff9dfc9040e66852270"
+  integrity sha1-7ezmituKTZ4GrM/538kEDmaFInA=
   dependencies:
     sortablejs "1.14.0"
 
@@ -11701,7 +11943,7 @@ vxe-table-plugin-antd@^3.0.3:
 
 vxe-table@4.1.0:
   version "4.1.0"
-  resolved "https://registry.npmjs.org/vxe-table/-/vxe-table-4.1.0.tgz#6533ab768cada2e6743eaee801a5c7db54cc7581"
+  resolved "https://registry.npmmirror.com/vxe-table/-/vxe-table-4.1.0.tgz#6533ab768cada2e6743eaee801a5c7db54cc7581"
   integrity sha512-FX4nc4ckkQGWG/W94z97KBIGrfZsbcni3pm+4uMzNQUcEgxgchnY78/4PrRC4ZRsHJIMv4mtNrzdfJDCTfA65g==
 
 w3c-hr-time@^1.0.2:
@@ -12203,6 +12445,11 @@ yargs@^17.0.0:
     y18n "^5.0.5"
     yargs-parser "^20.2.2"
 
+yarn@^1.22.17:
+  version "1.22.18"
+  resolved "https://registry.npmmirror.com/yarn/-/yarn-1.22.18.tgz#05b822ade8c672987bab8858635145da0850f78a"
+  integrity sha512-oFffv6Jp2+BTUBItzx1Z0dpikTX+raRdqupfqzeMKnoh7WD6RuPAxcqDkMUy9vafJkrB0YaV708znpuMhEBKGQ==
+
 yauzl@^2.4.2:
   version "2.10.0"
   resolved "https://registry.nlark.com/yauzl/download/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
@@ -12228,6 +12475,13 @@ zrender@5.2.1:
   dependencies:
     tslib "2.3.0"
 
+zrender@5.3.1:
+  version "5.3.1"
+  resolved "https://registry.npmmirror.com/zrender/-/zrender-5.3.1.tgz#fa8e63ac7e719cfd563831fe8c42a9756c5af384"
+  integrity sha512-7olqIjy0gWfznKr6vgfnGBk7y4UtdMvdwFmK92vVQsQeDPyzkHW1OlrLEKg6GHz1W5ePf0FeN1q2vkl/HFqhXw==
+  dependencies:
+    tslib "2.3.0"
+
 zwitch@^1.0.0:
   version "1.0.5"
   resolved "https://registry.nlark.com/zwitch/download/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"