Prechádzať zdrojové kódy

Merge branch 'master' of http://182.92.126.35:3000/hrx/mky-vent-base

lxh 1 rok pred
rodič
commit
6f3886acd2
100 zmenil súbory, kde vykonal 4069 pridanie a 2180 odobranie
  1. 0 36
      .env.test
  2. 1 0
      .gitignore
  3. 14 0
      LICENSE
  4. 1 1
      build/script/buildConf.ts
  5. 0 21
      build/vite/optimizer.ts
  6. 4 0
      build/vite/plugin/compress.ts
  7. 0 25
      build/vite/plugin/hmr.ts
  8. 2 2
      build/vite/plugin/html.ts
  9. 1 0
      build/vite/plugin/imagemin.ts
  10. 12 22
      build/vite/plugin/index.ts
  11. 0 33
      build/vite/plugin/pwa.ts
  12. 1 0
      build/vite/plugin/styleImport.ts
  13. 0 16
      docs/切换到vue3前端路由.md
  14. 9 1
      index.html
  15. 1 1
      mock/demo/select-demo.ts
  16. 1 1
      mock/demo/tree-demo.ts
  17. 113 244
      package.json
  18. 582 1407
      pnpm-lock.yaml
  19. BIN
      public/favicon.ico
  20. BIN
      public/resource/img/pwa-192x192.png
  21. BIN
      public/resource/img/pwa-512x512.png
  22. 6 0
      src/App.vue
  23. 1 0
      src/api/sys/model/userModel.ts
  24. 19 3
      src/api/sys/user.ts
  25. 0 0
      src/assets/icons/lock.svg
  26. BIN
      src/assets/images/department.png
  27. BIN
      src/assets/images/drag_cover.png
  28. BIN
      src/assets/images/people.png
  29. BIN
      src/assets/images/process_no_form.png
  30. BIN
      src/assets/images/setting.png
  31. BIN
      src/assets/images/template_cover.jpg
  32. BIN
      src/assets/images/wallet.png
  33. BIN
      src/assets/svg/fileType/image.png
  34. 1 1
      src/components/CodeEditor/src/CodeEditor.vue
  35. 1 1
      src/components/CountDown/src/CountdownInput.vue
  36. 11 4
      src/components/Dropdown/src/Dropdown.vue
  37. 37 8
      src/components/Form/src/BasicForm.vue
  38. 9 1
      src/components/Form/src/componentMap.ts
  39. 42 3
      src/components/Form/src/components/ApiSelect.vue
  40. 6 1
      src/components/Form/src/components/FormAction.vue
  41. 42 9
      src/components/Form/src/components/FormItem.vue
  42. 23 18
      src/components/Form/src/hooks/useForm.ts
  43. 3 3
      src/components/Form/src/hooks/useFormEvents.ts
  44. 1 1
      src/components/Form/src/jeecg/components/JAreaLinkage.vue
  45. 4 3
      src/components/Form/src/jeecg/components/JCategorySelect.vue
  46. 28 12
      src/components/Form/src/jeecg/components/JCodeEditor.vue
  47. 35 20
      src/components/Form/src/jeecg/components/JDictSelectTag.vue
  48. 1 1
      src/components/Form/src/jeecg/components/JEllipsis.vue
  49. 8 6
      src/components/Form/src/jeecg/components/JImageUpload.vue
  50. 7 2
      src/components/Form/src/jeecg/components/JInput.vue
  51. 11 3
      src/components/Form/src/jeecg/components/JPopup.vue
  52. 54 56
      src/components/Form/src/jeecg/components/JRangeDate.vue
  53. 53 0
      src/components/Form/src/jeecg/components/JRangeTime.vue
  54. 12 2
      src/components/Form/src/jeecg/components/JSearchSelect.vue
  55. 14 2
      src/components/Form/src/jeecg/components/JSelectDept.vue
  56. 6 2
      src/components/Form/src/jeecg/components/JSelectPosition.vue
  57. 1 1
      src/components/Form/src/jeecg/components/JTreeDict.vue
  58. 23 11
      src/components/Form/src/jeecg/components/JTreeSelect.vue
  59. 44 13
      src/components/Form/src/jeecg/components/JUpload/JUpload.vue
  60. 8 3
      src/components/Form/src/jeecg/components/modal/DeptSelectModal.vue
  61. 16 4
      src/components/Form/src/jeecg/components/modal/PositionSelectModal.vue
  62. 13 3
      src/components/Form/src/jeecg/components/modal/RoleSelectModal.vue
  63. 14 4
      src/components/Form/src/jeecg/components/modal/UserSelectByDepModal.vue
  64. 49 8
      src/components/Form/src/jeecg/components/modal/UserSelectModal.vue
  65. 282 0
      src/components/Form/src/jeecg/components/positionSelect/PositionSelectModal.vue
  66. 232 0
      src/components/Form/src/jeecg/components/roleSelect/RoleSelectInput.vue
  67. 296 0
      src/components/Form/src/jeecg/components/roleSelect/RoleSelectModal.vue
  68. 150 0
      src/components/Form/src/jeecg/components/userSelect/SelectedUserItem.vue
  69. 187 0
      src/components/Form/src/jeecg/components/userSelect/UserList.vue
  70. 186 0
      src/components/Form/src/jeecg/components/userSelect/UserListAndDepart.vue
  71. 142 0
      src/components/Form/src/jeecg/components/userSelect/UserListAndRole.vue
  72. 368 0
      src/components/Form/src/jeecg/components/userSelect/UserSelectModal.vue
  73. 273 0
      src/components/Form/src/jeecg/components/userSelect/index.vue
  74. 11 0
      src/components/Form/src/jeecg/components/userSelect/useUserSelect.ts
  75. 69 0
      src/components/Form/src/jeecg/hooks/useCodeHinting.ts
  76. 18 12
      src/components/Form/src/jeecg/hooks/useSelectBiz.ts
  77. 30 4
      src/components/Form/src/jeecg/hooks/useTreeBiz.ts
  78. 3 0
      src/components/Form/src/props.ts
  79. 2 2
      src/components/Form/src/types/form.ts
  80. 9 0
      src/components/Form/src/types/index.ts
  81. 8 4
      src/components/Form/src/utils/Area.ts
  82. 54 15
      src/components/Icon/src/IconPicker.vue
  83. 2 1
      src/components/JVxeCustom/src/components/JVxeDepartSelectCell.vue
  84. 6 7
      src/components/JVxeCustom/src/components/JVxePcaCell.vue
  85. 15 7
      src/components/JVxeCustom/src/components/JVxePopupCell.vue
  86. 2 2
      src/components/JVxeCustom/src/components/JVxeSelectDictSearchCell.ts
  87. 2 1
      src/components/JVxeCustom/src/components/JVxeUserSelectCell.vue
  88. 4 0
      src/components/JVxeCustom/src/hooks/useFileCell.ts
  89. 4 1
      src/components/Markdown/src/Markdown.vue
  90. 97 67
      src/components/Modal/src/BasicModal.vue
  91. 3 3
      src/components/Modal/src/components/ModalClose.vue
  92. 6 1
      src/components/Modal/src/hooks/useModalDrag.ts
  93. 0 2
      src/components/Page/index.ts
  94. 1 0
      src/components/Page/injectionKey.ts
  95. 1 1
      src/components/Page/src/PageWrapper.vue
  96. 151 23
      src/components/Table/src/BasicTable.vue
  97. 56 0
      src/components/Table/src/components/CustomSelectHeader.vue
  98. 9 1
      src/components/Table/src/components/TableAction.vue
  99. 44 6
      src/components/Table/src/components/TableFooter.vue
  100. 1 1
      src/components/Table/src/components/editable/EditableCell.vue

+ 0 - 36
.env.test

@@ -1,36 +0,0 @@
-# 是否启用mock
-VITE_USE_MOCK = true
-
-# 发布路径
-VITE_PUBLIC_PATH = /
-
-# 控制台不输出
-VITE_DROP_CONSOLE = true
-
-# 是否启用gzip或brotli压缩
-# 选项值: gzip | brotli | none
-# 如果需要多个可以使用“,”分隔
-VITE_BUILD_COMPRESS = 'gzip'
-
-# 使用压缩时是否删除原始文件,默认为false
-VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
-
-#后台接口父地址(必填)
-VITE_GLOB_API_URL=/jeecgboot
-
-#后台接口全路径地址(必填)
-VITE_GLOB_DOMAIN_URL=http://localhost:8080/jeecg-boot
-
-# 接口父路径前缀
-VITE_GLOB_API_URL_PREFIX=
-
-# 是否启用图像压缩
-VITE_USE_IMAGEMIN= true
-
-# 使用pwa
-VITE_USE_PWA = false
-
-# 是否兼容旧浏览器
-VITE_LEGACY = false
-
-VITE_GLOB_IS_SIMULATE=true

+ 1 - 0
.gitignore

@@ -29,6 +29,7 @@ pnpm-debug.log*
 *.sln
 *.sw?
 /os_del.cmd
+os_del.cmd
 /.vscode/
 /.history/
 /svn clear.bat

+ 14 - 0
LICENSE

@@ -19,3 +19,17 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
+
+
+
+  开源协议补充
+    JeecgBoot 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京,地址:中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱:jeecgos@163.com
+    本软件受适用的国家软件著作权法(包括国际条约)和双重保护许可。
+  
+   1.允许基于本平台软件开展业务系统开发。
+   2.JeecgBoot底层依赖的非开源功能:online lib依赖、仪表盘lib依赖等,统一采用LGPL开源协议(不二次改造、不拆分出jeecgboot之外使用,就不产生侵权)
+   3.不得基于该平台软件的基础,修改包装成一个与JeecgBoot平台软件功能类似的产品进行发布、销售,或与JeecgBoot参与同类软件产品市场的竞争。
+	 违反此条款属于侵权行为,须赔偿侵权经济损失,同时立即停止著作权侵权行为。
+	 
+   总结:在遵循Apache开源协议和开源协议补充条款下,允许商用使用,不会造成侵权行为!
+	 解释权归:http://www.jeecg.com

+ 1 - 1
build/script/buildConf.ts

@@ -1,5 +1,5 @@
 /**
- * Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
+ * 生成外部配置文件,用于生产发布后配置,无需重新打包
  */
 import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
 import fs, { writeFileSync } from 'fs-extra';

+ 0 - 21
build/vite/optimizer.ts

@@ -1,21 +0,0 @@
-// TODO
-import type { GetManualChunk } from 'rollup';
-
-//
-const vendorLibs: { match: string[]; output: string }[] = [
-  // {
-  //   match: ['xlsx'],
-  //   output: 'xlsx',
-  // },
-];
-
-// @ts-ignore
-export const configManualChunk: GetManualChunk = (id: string) => {
-  if (/[\\/]node_modules[\\/]/.test(id)) {
-    const matchItem = vendorLibs.find((item) => {
-      const reg = new RegExp(`[\\/]node_modules[\\/]_?(${item.match.join('|')})(.*)`, 'ig');
-      return reg.test(id);
-    });
-    return matchItem ? matchItem.output : null;
-  }
-};

+ 4 - 0
build/vite/plugin/compress.ts

@@ -13,6 +13,10 @@ export function configCompressPlugin(compress: 'gzip' | 'brotli' | 'none', delet
   if (compressList.includes('gzip')) {
     plugins.push(
       compressPlugin({
+        verbose: true,
+        disable: false,
+        threshold: 10240,
+        algorithm: 'gzip',
         ext: '.gz',
         deleteOriginFile,
       })

+ 0 - 25
build/vite/plugin/hmr.ts

@@ -1,25 +0,0 @@
-import type { Plugin } from 'vite';
-
-/**
- * TODO
- * Temporarily solve the Vite circular dependency problem, and wait for a better solution to fix it later. I don't know what problems this writing will bring.
- * @returns
- */
-
-export function configHmrPlugin(): Plugin {
-  return {
-    name: 'singleHMR',
-    handleHotUpdate({ modules, file }) {
-      if (file.match(/xml$/)) return [];
-
-      modules.forEach((m) => {
-        if (!m.url.match(/\.(css|less)/)) {
-          m.importedModules = new Set();
-          m.importers = new Set();
-        }
-      });
-
-      return modules;
-    },
-  };
-}

+ 2 - 2
build/vite/plugin/html.ts

@@ -19,11 +19,11 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
   const htmlPlugin: PluginOption[] = createHtmlPlugin({
     minify: isBuild,
     inject: {
-      // Inject data into ejs template
+      // 修改模板html的标题
       data: {
         title: VITE_GLOB_APP_TITLE,
       },
-      // Embed the generated app.config.js file
+      // 将app.config.js文件注入到模板html中
       tags: isBuild
         ? [
             {

+ 1 - 0
build/vite/plugin/imagemin.ts

@@ -1,3 +1,4 @@
+// 【图片压缩插件】
 // Image resource files used to compress the output of the production environment
 // https://github.com/anncwb/vite-plugin-imagemin
 import viteImagemin from 'vite-plugin-imagemin';

+ 12 - 22
build/vite/plugin/index.ts

@@ -1,28 +1,28 @@
 import { PluginOption } from 'vite';
 import vue from '@vitejs/plugin-vue';
 import vueJsx from '@vitejs/plugin-vue-jsx';
-import legacy from '@vitejs/plugin-legacy';
 import purgeIcons from 'vite-plugin-purge-icons';
-import windiCSS from 'vite-plugin-windicss';
+import UnoCSS from 'unocss/vite';
+import { presetTypography, presetUno } from 'unocss';
+
 import VitePluginCertificate from 'vite-plugin-mkcert';
-import vueSetupExtend from 'vite-plugin-vue-setup-extend';
+//[issues/555]开发环境,vscode断点调试,文件或行数对不上
+import vueSetupExtend from 'vite-plugin-vue-setup-extend-plus';
 import { configHtmlPlugin } from './html';
-import { configPwaConfig } from './pwa';
 import { configMockPlugin } from './mock';
 import { configCompressPlugin } from './compress';
 import { configStyleImportPlugin } from './styleImport';
 import { configVisualizerConfig } from './visualizer';
 import { configThemePlugin } from './theme';
-import { configImageminPlugin } from './imagemin';
 import { configSvgIconsPlugin } from './svgSprite';
-import OptimizationPersist from 'vite-plugin-optimize-persist';
-import PkgConfig from 'vite-plugin-package-config';
+// //预编译加载插件(不支持vite3作废)
+// import OptimizationPersist from 'vite-plugin-optimize-persist';
 import { resolve } from 'path';
 import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
 import topLevelAwait from 'vite-plugin-top-level-await';
 
 export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
-  const { VITE_USE_IMAGEMIN, VITE_USE_MOCK, VITE_LEGACY, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
+  const { VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
 
   const vitePlugins: (PluginOption | PluginOption[])[] = [
     // have to
@@ -52,11 +52,7 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
     })
   ];
 
-  // vite-plugin-windicss
-  vitePlugins.push(windiCSS());
-
-  // @vitejs/plugin-legacy
-  VITE_LEGACY && isBuild && vitePlugins.push(legacy());
+  vitePlugins.push(UnoCSS({ presets: [presetUno(), presetTypography()] }));
 
   // vite-plugin-html
   vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));
@@ -81,18 +77,12 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
 
   // The following plugins only work in the production environment
   if (isBuild) {
-    // vite-plugin-imagemin
-    VITE_USE_IMAGEMIN && vitePlugins.push(configImageminPlugin());
-
     // rollup-plugin-gzip
     vitePlugins.push(configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE));
-
-    // vite-plugin-pwa
-    vitePlugins.push(configPwaConfig(viteEnv));
   }
 
-  //vite-plugin-theme【解决vite首次打开界面加载慢问题】
-  vitePlugins.push(PkgConfig());
-  vitePlugins.push(OptimizationPersist());
+  // //vite-plugin-theme【预编译加载插件,解决vite首次打开界面加载慢问题】
+  // vitePlugins.push(PkgConfig());
+  // vitePlugins.push(OptimizationPersist());
   return vitePlugins;
 }

+ 0 - 33
build/vite/plugin/pwa.ts

@@ -1,33 +0,0 @@
-/**
- * Zero-config PWA for Vite
- * https://github.com/antfu/vite-plugin-pwa
- */
-import { VitePWA } from 'vite-plugin-pwa';
-
-export function configPwaConfig(env: ViteEnv) {
-  const { VITE_USE_PWA, VITE_GLOB_APP_TITLE, VITE_GLOB_APP_SHORT_NAME } = env;
-
-  if (VITE_USE_PWA) {
-    // vite-plugin-pwa
-    const pwaPlugin = VitePWA({
-      manifest: {
-        name: VITE_GLOB_APP_TITLE,
-        short_name: VITE_GLOB_APP_SHORT_NAME,
-        icons: [
-          {
-            src: './resource/img/pwa-192x192.png',
-            sizes: '192x192',
-            type: 'image/png',
-          },
-          {
-            src: './resource/img/pwa-512x512.png',
-            sizes: '512x512',
-            type: 'image/png',
-          },
-        ],
-      },
-    });
-    return pwaPlugin;
-  }
-  return [];
-}

+ 1 - 0
build/vite/plugin/styleImport.ts

@@ -1,4 +1,5 @@
 /**
+ * 【样式按需加载插件 ——主要处理antd的样式】
  *  Introduces component library styles on demand.
  * https://github.com/anncwb/vite-plugin-style-import
  */

+ 0 - 16
docs/切换到vue3前端路由.md

@@ -1,16 +0,0 @@
-## 切换到 Vue3菜单路由
-
-- 第一步:执行SQL脚本
-```
-alter table sys_permission rename as sys_permission_v2;
-alter table sys_permission_v3 rename as sys_permission;
-```
-
-> 这个 sql 脚本做了什么?
-> - 1、把表名 sys_permission 备份改为 sys_permission_v2
-> - 2、把 sys_permission_v3 改为 sys_permission
-> 说明:因为 vue3 和 vue2 的菜单配置不一样,所以通过切换表来实现 vue3 和 vue2 的切换。
-
-- 第二步:登录进系统
-
-> 从 jeecgboot3.3.0 版本默认 vue3 和 vue2 的权限都已经分配好, 不需要再手工授权。

+ 9 - 1
index.html

@@ -323,6 +323,14 @@
     </div>
     
     <script type="module" src="/src/main.ts"></script>
-    <!-- <script type="module" src="/src/utils/threejs/main.worker.ts"></script> -->
+    <script>
+      var _hmt = _hmt || [];
+      (function() {
+        var hm = document.createElement("script");
+        hm.src = "https://hm.baidu.com/hm.js?0febd9e3cacb3f627ddac64d52caac39";
+        var s = document.getElementsByTagName("script")[0];
+        s.parentNode.insertBefore(hm, s);
+      })();
+    </script>
   </body>
 </html>

+ 1 - 1
mock/demo/select-demo.ts

@@ -21,7 +21,7 @@ export default [
     method: 'get',
     response: ({ query }) => {
       const { keyword,count} = query;
-      console.log(keyword);
+      console.log("查询条件:", keyword);
       return resultSuccess(demoList(keyword,count));
     },
   },

+ 1 - 1
mock/demo/tree-demo.ts

@@ -31,7 +31,7 @@ export default [
     method: 'get',
     response: ({ query }) => {
       const { keyword } = query;
-      console.log(keyword);
+      console.log("查询条件:", keyword);
       return resultSuccess(demoTreeList(keyword));
     },
   },

+ 113 - 244
package.json

@@ -1,68 +1,56 @@
 {
   "name": "jeecgboot-vue3",
-  "version": "3.4.4",
+  "version": "3.6.0",
   "author": {
     "name": "jeecg",
     "email": "jeecgos@163.com",
     "url": "https://github.com/jeecgboot/jeecgboot-vue3"
   },
   "scripts": {
-    "bootstrap": "pnpm install",
-    "serve": "npm run dev",
-    "dev": "vite",
+    "pinstall": "pnpm install",
     "clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
-    "clean:lib": "rimraf node_modules",
+    "dev": "vite",
     "build": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 vite build && esno ./build/script/postBuild.ts",
-    "build:test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode test && esno ./build/script/postBuild.ts",
-    "build:no-cache": "pnpm clean:cache && npm run build",
-    "report": "cross-env REPORT=true npm run build",
-    "type:check": "vue-tsc --noEmit --skipLibCheck",
+    "build:report": "pnpm clean:cache && cross-env REPORT=true npm run build",
     "preview": "npm run build && vite preview",
-    "preview:dist": "vite preview",
-    "log": "conventional-changelog -p angular -i CHANGELOG.md -s",
-    "lint:eslint": "eslint --cache --max-warnings 0  \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
-    "lint:prettier": "prettier --write  \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
-    "lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
-    "lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
-    "lint:pretty": "pretty-quick --staged",
-    "test:unit": "jest",
-    "test:unit-coverage": "jest --coverage",
-    "test:gzip": "http-server dist --cors --gzip -c-1",
-    "test:br": "http-server dist --cors --brotli -c-1",
-    "reinstall": "rimraf pnpm-lock.yaml && yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
-    "prepare": "husky install",
-    "gen:icon": "esno ./build/generate/icon/index.ts"
+    "reinstall": "rimraf pnpm-lock.yaml && rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run install",
+    "clean:lib": "rimraf node_modules",
+    "gen:icon": "esno ./build/generate/icon/index.ts",
+    "batch:prettier": "prettier --write  \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
+    "upgrade:log": "conventional-changelog -p angular -i CHANGELOG.md -s",
+    "husky:install": "husky install"
   },
   "dependencies": {
-    "@ant-design/colors": "^6.0.0",
+    "@ant-design/colors": "^7.0.0",
     "@ant-design/icons-vue": "^6.1.0",
-    "@iconify/iconify": "^2.2.1",
-    "@jeecg/online": "3.4.4-RC",
+    "@iconify/iconify": "^3.1.1",
+    "@jeecg/online": "3.5.3-vite4",
     "@kjgl77/datav-vue3": "^1.4.2",
     "@liveqing/liveplayer-v3": "^3.1.9",
-    "@logicflow/core": "^1.1.13",
-    "@logicflow/extension": "^1.1.13",
-    "@vue/runtime-core": "^3.2.33",
-    "@vue/shared": "^3.2.33",
-    "@vueuse/core": "^8.3.0",
-    "@vueuse/shared": "^8.3.0",
-    "@zxcvbn-ts/core": "^2.0.1",
-    "ant-design-vue": "^3.2.19",
-    "axios": "^0.26.1",
+    "@logicflow/core": "^1.2.12",
+    "@logicflow/extension": "^1.2.13",
+    "@qiaoqiaoyun/drag-free": "^1.1.4",
+    "@vue/runtime-core": "^3.3.4",
+    "@vue/shared": "^3.3.4",
+    "@vueuse/core": "^10.4.1",
+    "@vueuse/shared": "^10.4.1",
+    "@zxcvbn-ts/core": "^3.0.3",
+    "ant-design-vue": "^3.2.20",
+    "axios": "^1.5.0",
     "china-area-data": "^5.0.1",
-    "clipboard": "^2.0.8",
+    "clipboard": "^2.0.11",
     "codemirror": "^5.65.3",
-    "cron-parser": "^3.5.0",
-    "cropperjs": "^1.5.12",
+    "cron-parser": "^4.9.0",
+    "cropperjs": "^1.5.13",
     "crypto-js": "^4.1.1",
-    "dayjs": "^1.11.1",
+    "dayjs": "^1.11.9",
     "dexie": "^3.2.2",
-    "dom-align": "^1.12.2",
-    "echarts": "^5.3.2",
-    "emoji-mart-vue-fast": "^11.1.1",
+    "dom-align": "^1.12.4",
+    "echarts": "^5.4.3",
+    "emoji-mart-vue-fast": "^15.0.0",
     "enquire.js": "^2.1.6",
     "gsap": "^3.11.3",
-    "intro.js": "^5.1.0",
+    "intro.js": "^7.2.0",
     "lodash-es": "^4.17.21",
     "lodash.get": "^4.4.2",
     "md5": "^2.3.0",
@@ -70,129 +58,118 @@
     "mockjs": "^1.1.0",
     "moment": "^2.29.4",
     "nprogress": "^0.2.0",
-    "path-to-regexp": "^6.2.0",
-    "pinia": "2.0.12",
+    "path-to-regexp": "^6.2.1",
+    "pinia": "2.1.6",
     "plyr": "^3.7.8",
     "print-js": "^1.6.0",
     "qiankun": "^2.8.4",
-    "qrcode": "^1.5.0",
+    "qrcode": "^1.5.3",
     "qrcodejs2": "0.0.2",
-    "qs": "^6.10.3",
+    "qs": "^6.11.2",
     "resize-observer-polyfill": "^1.5.1",
     "showdown": "^2.1.0",
     "sortablejs": "^1.15.0",
     "three": "^0.155.0",
     "tinymce": "^5.10.3",
-    "vditor": "^3.8.13",
-    "vite-plugin-theme": "^0.8.6",
-    "vite-plugin-top-level-await": "^1.3.1",
-    "vue": "^3.2.33",
-    "vue-cropper": "^0.5.6",
+    "vditor": "^3.9.5",
+    "vue": "^3.3.4",
+    "vue-cropper": "^0.6.2",
     "vue-cropperjs": "^5.0.0",
-    "vue-i18n": "^9.1.9",
+    "vue-i18n": "9.2.2",
     "vue-infinite-scroll": "^2.0.2",
-    "vue-json-pretty": "^2.0.6",
-    "vue-print-nb-jeecg": "^1.0.10",
-    "vue-router": "^4.0.14",
-    "vue-tree-list": "^1.5.0",
-    "vue-types": "^4.1.1",
+    "vue-json-pretty": "^2.2.4",
+    "vue-print-nb-jeecg": "^1.0.12",
+    "vue-router": "^4.2.4",
+    "vue-types": "^5.1.1",
     "vuedraggable": "^4.1.0",
-    "vxe-table": "4.1.0",
-    "vxe-table-plugin-antd": "3.0.5",
-    "xe-utils": "^3.3.1",
-    "xss": "^1.0.13"
+    "vxe-table": "4.5.12",
+    "vxe-table-plugin-antd": "3.1.0",
+    "xe-utils": "3.5.13",
+    "xss": "^1.0.14"
   },
   "devDependencies": {
-    "@commitlint/cli": "^16.2.3",
-    "@commitlint/config-conventional": "^16.2.1",
-    "@iconify/json": "^2.1.30",
-    "@purge-icons/generated": "^0.8.1",
+    "@commitlint/cli": "^17.7.1",
+    "@commitlint/config-conventional": "^17.7.0",
+    "@iconify/json": "^2.2.107",
+    "@purge-icons/generated": "^0.9.0",
     "@rys-fe/vite-plugin-theme": "^0.8.6",
-    "@types/codemirror": "^5.60.5",
+    "@types/codemirror": "^5.60.9",
     "@types/crypto-js": "^4.1.1",
-    "@types/fs-extra": "^9.0.13",
-    "@types/inquirer": "^8.2.1",
-    "@types/intro.js": "^3.0.2",
-    "@types/jest": "^27.0.2",
-    "@types/lodash-es": "^4.17.6",
-    "@types/mockjs": "^1.0.6",
-    "@types/node": "^17.0.25",
+    "@types/fs-extra": "^11.0.1",
+    "@types/inquirer": "^9.0.3",
+    "@types/intro.js": "^5.1.1",
+    "@types/jest": "^29.5.4",
+    "@types/lodash-es": "^4.17.8",
+    "@types/mockjs": "^1.0.7",
+    "@types/node": "^20.5.6",
     "@types/nprogress": "^0.2.0",
-    "@types/qrcode": "^1.4.2",
+    "@types/qrcode": "^1.5.1",
     "@types/qs": "^6.9.7",
-    "@types/showdown": "^1.9.4",
-    "@types/sortablejs": "^1.10.7",
+    "@types/showdown": "^2.0.1",
+    "@types/sortablejs": "^1.15.1",
     "@types/three": "^0.155.0",
-    "@typescript-eslint/eslint-plugin": "^5.20.0",
-    "@typescript-eslint/parser": "^5.20.0",
+    "@typescript-eslint/eslint-plugin": "^6.4.1",
+    "@typescript-eslint/parser": "^6.5.0",
     "@vitejs/plugin-legacy": "^2.0.0",
-    "@vitejs/plugin-vue": "^3.0.1",
-    "@vitejs/plugin-vue-jsx": "^1.3.10",
-    "@vue/compiler-sfc": "^3.2.33",
-    "@vue/test-utils": "^2.0.0-rc.21",
-    "autoprefixer": "^10.4.4",
-    "commitizen": "^4.2.4",
-    "conventional-changelog-cli": "^2.2.2",
+    "@vitejs/plugin-vue": "^4.3.3",
+    "@vitejs/plugin-vue-jsx": "^3.0.2",
+    "@vue/compiler-sfc": "^3.3.4",
+    "@vue/test-utils": "^2.4.1",
+    "autoprefixer": "^10.4.15",
+    "commitizen": "^4.3.0",
+    "conventional-changelog-cli": "^3.0.0",
     "cross-env": "^7.0.3",
-    "cz-git": "^1.3.9",
-    "czg": "^1.3.9",
+    "cz-git": "^1.7.1",
+    "czg": "^1.7.1",
     "dat.gui": "^0.7.9",
-    "dotenv": "^16.0.0",
-    "eslint": "^8.13.0",
-    "eslint-config-prettier": "^8.5.0",
-    "eslint-define-config": "^1.1.1",
-    "eslint-plugin-jest": "^25.2.2",
-    "eslint-plugin-prettier": "^4.0.0",
-    "eslint-plugin-vue": "^8.6.0",
-    "esno": "^0.14.1",
-    "fs-extra": "^10.1.0",
-    "http-server": "^14.0.0",
-    "husky": "^7.0.4",
-    "inquirer": "^8.2.2",
-    "is-ci": "^3.0.0",
-    "jest": "^27.3.1",
-    "less": "^4.1.2",
-    "lint-staged": "12.3.7",
+    "dotenv": "^16.3.1",
+    "eslint": "^8.47.0",
+    "eslint-config-prettier": "^9.0.0",
+    "eslint-define-config": "^1.23.0",
+    "eslint-plugin-jest": "^27.2.3",
+    "eslint-plugin-prettier": "^5.0.0",
+    "eslint-plugin-vue": "^9.17.0",
+    "esno": "^0.17.0",
+    "fs-extra": "^11.1.1",
+    "http-server": "^14.1.1",
+    "husky": "^8.0.3",
+    "inquirer": "^9.2.10",
+    "is-ci": "^3.0.1",
+    "jest": "^29.6.4",
+    "less": "^4.2.0",
+    "lint-staged": "14.0.1",
     "npm-run-all": "^4.1.5",
     "picocolors": "^1.0.0",
-    "postcss": "^8.4.12",
-    "postcss-html": "^1.4.1",
+    "postcss": "^8.4.28",
+    "postcss-html": "^1.5.0",
     "postcss-less": "^6.0.0",
-    "prettier": "^2.6.2",
-    "pretty-quick": "^3.1.1",
-    "rimraf": "^3.0.2",
-    "rollup": "^2.70.2",
-    "rollup-plugin-visualizer": "^5.6.0",
-    "stylelint": "^14.7.1",
-    "stylelint-config-prettier": "^9.0.3",
-    "stylelint-config-recommended": "^7.0.0",
-    "stylelint-config-recommended-vue": "^1.4.0",
-    "stylelint-config-standard": "^25.0.0",
-    "stylelint-order": "^5.0.0",
-    "ts-jest": "^27.0.7",
-    "ts-node": "^10.7.0",
-    "typescript": "^4.6.3",
-    "vite": "^3.0.2",
+    "prettier": "^3.0.2",
+    "pretty-quick": "^3.1.3",
+    "rimraf": "^5.0.1",
+    "rollup": "^3.28.1",
+    "rollup-plugin-visualizer": "^5.9.2",
+    "stylelint": "^15.10.3",
+    "stylelint-config-prettier": "^9.0.5",
+    "stylelint-config-recommended": "^13.0.0",
+    "stylelint-config-recommended-vue": "^1.5.0",
+    "stylelint-config-standard": "^34.0.0",
+    "stylelint-order": "^6.0.3",
+    "ts-jest": "^29.1.1",
+    "ts-node": "^10.9.1",
+    "typescript": "^4.9.5",
+    "unocss": "^0.55.3",
+    "vite": "^4.4.9",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-html": "^3.2.0",
-    "vite-plugin-imagemin": "^0.6.1",
-    "vite-plugin-mkcert": "^1.10.1",
+    "vite-plugin-mkcert": "^1.16.0",
     "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.8.2",
-    "vite-plugin-pwa": "^0.12.3",
+    "vite-plugin-purge-icons": "^0.9.2",
     "vite-plugin-style-import": "^2.0.0",
     "vite-plugin-svg-icons": "^2.0.1",
     "vite-plugin-top-level-await": "^1.3.1",
-    "vite-plugin-vue-setup-extend": "^0.4.0",
-    "vite-plugin-windicss": "^1.8.7",
-    "vue-eslint-parser": "^8.3.0",
-    "vue-tsc": "^0.33.9"
-  },
-  "resolutions": {
-    "bin-wrapper": "npm:bin-wrapper-china",
-    "rollup": "^2.72.0"
+    "vite-plugin-vue-setup-extend-plus": "^0.1.0",
+    "vue-eslint-parser": "^9.3.1",
+    "vue-tsc": "^1.8.8"
   },
   "repository": {
     "type": "git",
@@ -209,119 +186,11 @@
   "vite": {
     "optimizeDeps": {
       "include": [
-        "@ant-design/colors",
-        "@liveqing/liveplayer-v3",
-        "@ant-design/icons-vue",
-        "@jeecg/online",
-        "@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",
-        "lodash-es",
-        "echarts/charts",
-        "echarts/components",
-        "echarts/core",
-        "echarts/renderers",
-        "gsap",
-        "intro.js",
-        "lodash-es",
-        "md5",
-        "moment",
-        "nprogress",
-        "path-to-regexp",
-        "pinia",
-        "print-js",
-        "qrcode",
-        "qs",
-        "resize-observer-polyfill",
-        "showdown",
-        "sortablejs",
-        "three",
         "three/examples/jsm/postprocessing/EffectComposer.js",
         "three/examples/jsm/postprocessing/OutputPass.js",
         "three/examples/jsm/postprocessing/RenderPass.js",
         "three/examples/jsm/postprocessing/ShaderPass.js",
-        "three/examples/jsm/postprocessing/UnrealBloomPass.js",
-        "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",
-        "vxe-table",
-        "vxe-table-plugin-antd",
-        "xe-utils",
-        "xss"
+        "three/examples/jsm/postprocessing/UnrealBloomPass.js"
       ]
     }
   }

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 582 - 1407
pnpm-lock.yaml


BIN
public/favicon.ico


BIN
public/resource/img/pwa-192x192.png


BIN
public/resource/img/pwa-512x512.png


+ 6 - 0
src/App.vue

@@ -50,6 +50,12 @@
     gl && gl.getExtension('WEBGL_lose_context').loseContext()
   })
 </script>
+
+<style lang="less">
+// update-begin--author:liaozhiyang---date:20230803---for:【QQYUN-5839】windi会影响到html2canvas绘制的图片样式
+img{display:inline-block;}
+// update-end--author:liaozhiyang---date:20230803---for:【QQYUN-5839】windi会影响到html2canvas绘制的图片样式
+</style>
 <style lang="less" scoped>
   #app {
     overflow: hidden;

+ 1 - 0
src/api/sys/model/userModel.ts

@@ -23,6 +23,7 @@ export interface LoginResultModel {
   userId: string | number;
   token: string;
   role: RoleInfo;
+  userInfo?: any
 }
 
 /**

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

@@ -78,14 +78,24 @@ export function phoneLoginApi(params: LoginParams, mode: ErrorMessageMode = 'mod
  * @description: getUserInfo
  */
 export function getUserInfo() {
-  return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' }).catch((e) => {
+  return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, {}).catch((e) => {
     // update-begin--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面
     if (e && (e.message.includes('timeout') || e.message.includes('401'))) {
       //接口不通时跳转到登录界面
       const userStore = useUserStoreWithOut();
       userStore.setToken('');
       setAuthCache(TOKEN_KEY, null);
-      router.push(PageEnum.BASE_LOGIN);
+
+      // update-begin-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
+      router.push({
+        path: PageEnum.BASE_LOGIN,
+        query: {
+          // 传入当前的路由,登录成功后跳转到当前路由
+          redirect: router.currentRoute.value.fullPath,
+        }
+      });
+      // update-end-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
+
     }
     // update-end--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面
   });
@@ -146,9 +156,15 @@ export const passwordChange = (params) => defHttp.get({ url: Api.passwordChange,
  * @description: 第三方登录
  */
 export function thirdLogin(params, mode: ErrorMessageMode = 'modal') {
+  //==========begin 第三方登录/auth2登录需要传递租户id===========
+  let tenantId = "0";
+  if(!params.tenantId){
+    tenantId = params.tenantId;
+  }
+  //==========end 第三方登录/auth2登录需要传递租户id===========
   return defHttp.get<LoginResultModel>(
     {
-      url: `${Api.thirdLogin}/${params.token}/${params.thirdType}`,
+      url: `${Api.thirdLogin}/${params.token}/${params.thirdType}/${tenantId}`,
     },
     {
       errorMessageMode: mode,

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/assets/icons/lock.svg


BIN
src/assets/images/department.png


BIN
src/assets/images/drag_cover.png


BIN
src/assets/images/people.png


BIN
src/assets/images/process_no_form.png


BIN
src/assets/images/setting.png


BIN
src/assets/images/template_cover.jpg


BIN
src/assets/images/wallet.png


BIN
src/assets/svg/fileType/image.png


+ 1 - 1
src/components/CodeEditor/src/CodeEditor.vue

@@ -15,7 +15,7 @@
   import { computed } from 'vue';
   import CodeMirrorEditor from './codemirror/CodeMirror.vue';
   import { isString } from '/@/utils/is';
-
+  
   const props = defineProps({
     value: { type: [Object, String] as PropType<Record<string, any> | string> },
     mode: { type: String, default: MODE.JSON },

+ 1 - 1
src/components/CountDown/src/CountdownInput.vue

@@ -12,7 +12,7 @@
   import { defineComponent, PropType } from 'vue';
   import CountButton from './CountButton.vue';
   import { useDesign } from '/@/hooks/web/useDesign';
-  import { useRuleFormItem } from '/@/hooks/component/useFormItem';
+  import { useRuleFormItem } from '/@/hooks/component/useFormItemSingle';
 
   const props = {
     value: { type: String },

+ 11 - 4
src/components/Dropdown/src/Dropdown.vue

@@ -10,19 +10,26 @@
             v-bind="getAttr(item.event)"
             @click="handleClickMenu(item)"
             :disabled="item.disabled"
-            :class="[{ 'is-pop-confirm': item.popConfirm }, (item.class ?? [])]"
+            :class="[{ 'is-pop-confirm': item.popConfirm }, item.class ?? []]"
           >
             <a-popconfirm v-if="popconfirm && item.popConfirm" v-bind="getPopConfirmAttrs(item.popConfirm)">
               <template #icon v-if="item.popConfirm.icon">
-                <Icon :icon="item.popConfirm.icon" />
+                <Icon v-if="item.iconColor" :icon="item.popConfirm.icon" :color="item.iconColor" />
+                <Icon v-else :icon="item.popConfirm.icon" />
               </template>
               <div class="dropdown-event-area">
-                <Icon :icon="item.icon" v-if="item.icon" />
+                <Icon :icon="item.icon" v-if="item.icon && item.iconColor" :color="item.iconColor" />
+                <Icon :icon="item.icon" v-else-if="item.icon" />
                 <span class="ml-1">{{ item.text }}</span>
               </div>
             </a-popconfirm>
+            <!--  设置动态插槽   -->
+            <template v-else-if="item.slot">
+              <slot :name="item.slot" :label="item.text"></slot>
+            </template>
             <template v-else>
-              <Icon :icon="item.icon" v-if="item.icon" />
+              <Icon :icon="item.icon" v-if="item.icon && item.iconColor" :color="item.iconColor" />
+              <Icon :icon="item.icon" v-else-if="item.icon" />
               <span class="ml-1">{{ item.text }}</span>
             </template>
           </a-menu-item>

+ 37 - 8
src/components/Form/src/BasicForm.vue

@@ -53,8 +53,11 @@
   import { useModalContext } from '/@/components/Modal';
 
   import { basicProps } from './props';
+  import componentSetting from '/@/settings/componentSetting';
+
   import { useDesign } from '/@/hooks/web/useDesign';
   import dayjs from 'dayjs';
+  import { useDebounceFn } from '@vueuse/core';
 
   export default defineComponent({
     name: 'BasicForm',
@@ -89,6 +92,16 @@
           mergeProps.labelCol = undefined;
         }
         //update-end-author:sunjianlei date:20220923 for: 如果用户设置了labelWidth,则使labelCol失效,解决labelWidth设置无效的问题
+        // update-begin--author:liaozhiyang---date:20231017---for:【QQYUN-6566】BasicForm支持一行显示(inline)
+        if (mergeProps.layout === 'inline') {
+          if (mergeProps.labelCol === componentSetting.form.labelCol) {
+            mergeProps.labelCol = undefined;
+          }
+          if (mergeProps.wrapperCol === componentSetting.form.wrapperCol) {
+            mergeProps.wrapperCol = undefined;
+          }
+        }
+        // update-end--author:liaozhiyang---date:20231017---for:【QQYUN-6566】BasicForm支持一行显示(inline)
         return mergeProps;
       });
 
@@ -118,12 +131,20 @@
           const { defaultValue, component, componentProps } = schema;
           // handle date type
           if (defaultValue && dateItemType.includes(component)) {
-            const { valueFormat } = componentProps;
+            //update-begin---author:wangshuai ---date:20230410  for:【issues/435】代码生成的日期控件赋默认值报错------------
+            let valueFormat:string = "";
+            if(componentProps){
+              valueFormat = componentProps?.valueFormat;
+            }
+            if(!valueFormat){
+              console.warn("未配置valueFormat,可能导致格式化错误!");
+            }
+            //update-end---author:wangshuai ---date:20230410  for:【issues/435】代码生成的日期控件赋默认值报错------------
             if (!Array.isArray(defaultValue)) {
               //update-begin---author:wangshuai ---date:20221124  for:[issues/215]列表页查询框(日期选择框)设置初始时间,一进入页面时,后台报日期转换类型错误的------------
-              if (valueFormat) {
+              if(valueFormat){
                 schema.defaultValue = dateUtil(defaultValue).format(valueFormat);
-              } else {
+              }else{
                 schema.defaultValue = dateUtil(defaultValue);
               }
               //update-end---author:wangshuai ---date:20221124  for:[issues/215]列表页查询框(日期选择框)设置初始时间,一进入页面时,后台报日期转换类型错误的------------
@@ -131,9 +152,9 @@
               const def: dayjs.Dayjs[] = [];
               defaultValue.forEach((item) => {
                 //update-begin---author:wangshuai ---date:20221124  for:[issues/215]列表页查询框(日期选择框)设置初始时间,一进入页面时,后台报日期转换类型错误的------------
-                if (valueFormat) {
+                if(valueFormat){
                   def.push(dateUtil(item).format(valueFormat));
-                } else {
+                }else{
                   def.push(dateUtil(item));
                 }
                 //update-end---author:wangshuai ---date:20221124  for:[issues/215]列表页查询框(日期选择框)设置初始时间,一进入页面时,后台报日期转换类型错误的------------
@@ -241,13 +262,21 @@
         propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
       }
 
+      //update-begin-author:taoyan date:2022-11-28 for: QQYUN-3121 【优化】表单视图问题#scott测试 8、此功能未实现
+      const onFormSubmitWhenChange = useDebounceFn(handleSubmit, 300);
       function setFormModel(key: string, value: any) {
         formModel[key] = value;
-        const { validateTrigger } = unref(getBindValue);
-        if (!validateTrigger || validateTrigger === 'change') {
-          validateFields([key]).catch((_) => {});
+        // update-begin--author:liaozhiyang---date:20230922---for:【issues/752】表单校验dynamicRules 无法 使用失去焦点后校验 trigger: 'blur'
+        // const { validateTrigger } = unref(getBindValue);
+        // if (!validateTrigger || validateTrigger === 'change') {
+        //   validateFields([key]).catch((_) => {});
+        // }
+        // update-end--author:liaozhiyang---date:20230922---for:【issues/752】表单校验dynamicRules 无法 使用失去焦点后校验 trigger: 'blur'
+        if(props.autoSearch === true){
+          onFormSubmitWhenChange();
         }
       }
+      //update-end-author:taoyan date:2022-11-28 for: QQYUN-3121 【优化】表单视图问题#scott测试 8、此功能未实现
 
       function handleEnterPress(e: KeyboardEvent) {
         const { autoSubmitOnEnter } = unref(getProps);

+ 9 - 1
src/components/Form/src/componentMap.ts

@@ -61,7 +61,10 @@ import JSearchSelect from './jeecg/components/JSearchSelect.vue';
 import JAddInput from './jeecg/components/JAddInput.vue';
 import { Time } from '/@/components/Time';
 import JRangeNumber from './jeecg/components/JRangeNumber.vue';
-import JRangeDate from './jeecg/components/JRangeDate.vue';
+import UserSelect from './jeecg/components/userSelect/index.vue';
+import JRangeDate from './jeecg/components/JRangeDate.vue'
+import JRangeTime from './jeecg/components/JRangeTime.vue'
+import RoleSelectInput from './jeecg/components/roleSelect/RoleSelectInput.vue';
 
 const componentMap = new Map<ComponentType, Component>();
 
@@ -131,7 +134,12 @@ componentMap.set('JUpload', JUpload);
 componentMap.set('JSearchSelect', JSearchSelect);
 componentMap.set('JAddInput', JAddInput);
 componentMap.set('JRangeNumber', JRangeNumber);
+componentMap.set('UserSelect', UserSelect);
 componentMap.set('RangeDate', JRangeDate);
+componentMap.set('RangeTime', JRangeTime);
+componentMap.set('RoleSelect', RoleSelectInput);
+
+
 
 export function add(compName: ComponentType, component: Component) {
   componentMap.set(compName, component);

+ 42 - 3
src/components/Form/src/components/ApiSelect.vue

@@ -1,5 +1,5 @@
 <template>
-  <Select @dropdownVisibleChange="handleFetch" v-bind="$attrs" @change="handleChange" :options="getOptions" v-model:value="state">
+  <Select @dropdownVisibleChange="handleFetch" v-bind="attrs_" @change="handleChange" :options="getOptions" v-model:value="state">
     <template #[item]="data" v-for="item in Object.keys($slots)">
       <slot :name="item" v-bind="data || {}"></slot>
     </template>
@@ -63,7 +63,30 @@
 
       // Embedded in the form, just use the hook binding to perform form verification
       const [state, setState] = useRuleFormItem(props, 'value', 'change', emitData);
-
+      // update-begin--author:liaozhiyang---date:20230830---for:【QQYUN-6308】解决警告
+      let vModalValue: any;
+      const attrs_ = computed(() => {
+        let obj: any = unref(attrs) || {};
+        if (obj && obj['onUpdate:value']) {
+          vModalValue = obj['onUpdate:value'];
+          delete obj['onUpdate:value'];
+        }
+        // update-begin--author:liaozhiyang---date:20231017---for:【issues/5467】ApiSelect修复覆盖了用户传递的方法
+        if (obj['filterOption'] === undefined) {
+          // update-begin--author:liaozhiyang---date:20230904---for:【issues/5305】无法按照预期进行搜索
+          obj['filterOption'] = (inputValue, option) => {
+            if (typeof option['label'] === 'string') {
+              return option['label'].toLowerCase().indexOf(inputValue.toLowerCase()) != -1;
+            } else {
+              return true;
+            }
+          };
+          // update-end--author:liaozhiyang---date:20230904---for:【issues/5305】无法按照预期进行搜索
+        }
+        // update-end--author:liaozhiyang---date:20231017---for:【issues/5467】ApiSelect修复覆盖了用户传递的方法
+        return obj;
+      });
+      // update-begin--author:liaozhiyang---date:20230830---for:【QQYUN-6308】解决警告
       const getOptions = computed(() => {
         const { labelField, valueField, numberToString } = props;
         return unref(options).reduce((prev, next: Recordable) => {
@@ -90,6 +113,10 @@
         },
         { deep: true }
       );
+     //监听数值修改,查询数据
+      watchEffect(() => {
+        props.value && handleFetch();
+      });
 
       async function fetch() {
         const api = props.api;
@@ -114,6 +141,17 @@
           //--@updateBy-begin----author:liusq---date:20210914------for:判断选择模式,multiple多选情况下的value值空的情况下需要设置为数组------
           unref(attrs).mode == 'multiple' && !Array.isArray(unref(state)) && setState([]);
           //--@updateBy-end----author:liusq---date:20210914------for:判断选择模式,multiple多选情况下的value值空的情况下需要设置为数组------
+
+          //update-begin---author:wangshuai ---date:20230505  for:初始化value值,如果是多选字符串的情况下显示不出来------------
+          initValue();
+          //update-end---author:wangshuai ---date:20230505  for:初始化value值,如果是多选字符串的情况下显示不出来------------
+        }
+      }
+
+      function initValue() {
+        let value = props.value;
+        if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
+          state.value = value.split(',');
         }
       }
 
@@ -129,10 +167,11 @@
       }
 
       function handleChange(_, ...args) {
+        vModalValue && vModalValue(_);
         emitData.value = args;
       }
 
-      return { state, attrs, getOptions, loading, t, handleFetch, handleChange };
+      return { state, attrs_, attrs, getOptions, loading, t, handleFetch, handleChange };
     },
   });
 </script>

+ 6 - 1
src/components/Form/src/components/FormAction.vue

@@ -65,6 +65,7 @@
       actionSpan: propTypes.number.def(6),
       isAdvanced: propTypes.bool,
       hideAdvanceBtn: propTypes.bool,
+      layout: propTypes.oneOf(['horizontal', 'vertical', 'inline']).def('horizontal'),
     },
     emits: ['toggle-advanced'],
     setup(props, { emit }) {
@@ -76,10 +77,14 @@
         const advancedSpanObj = showAdvancedButton ? { span: actionSpan < 6 ? 24 : actionSpan } : {};
         const actionColOpt: Partial<ColEx> = {
           style: { textAlign: 'right' },
-          span: showAdvancedButton ? 6 : 4,
           ...advancedSpanObj,
           ...actionColOptions,
         };
+        // update-begin--author:liaozhiyang---date:20231017---for:【QQYUN-6566】BasicForm支持一行显示(inline)
+        if (props.layout !== 'inline') {
+          actionColOpt['span'] = showAdvancedButton ? 6 : 4;
+        }
+        // update-end--author:liaozhiyang---date:20231017---for:【QQYUN-6566】BasicForm支持一行显示(inline)
         return actionColOpt;
       });
 

+ 42 - 9
src/components/Form/src/components/FormItem.vue

@@ -1,4 +1,5 @@
 <script lang="tsx">
+  import { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
   import type { PropType, Ref } from 'vue';
   import type { FormActionType, FormProps } from '../types/form';
   import type { FormSchema } from '../types/form';
@@ -14,6 +15,7 @@
   import { upperFirst, cloneDeep } from 'lodash-es';
   import { useItemLabelWidth } from '../hooks/useLabelWidth';
   import { useI18n } from '/@/hooks/web/useI18n';
+  import { useAppInject } from '/@/hooks/web/useAppInject';
 
   export default defineComponent({
     name: 'BasicFormItem',
@@ -39,6 +41,10 @@
         type: Function as PropType<(key: string, value: any) => void>,
         default: null,
       },
+      validateFields: {
+        type: Function as PropType<(nameList?: NamePath[] | undefined, options?: ValidateOptions) => Promise<any>>,
+        default: null,
+      },
       tableAction: {
         type: Object as PropType<TableActionType>,
       },
@@ -78,10 +84,9 @@
           componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {};
         }
         if (schema.component === 'Divider') {
-          componentProps = Object.assign({ type: 'horizontal' }, componentProps, {
-            orientation: 'left',
-            plain: true,
-          });
+          //update-begin---author:wangshuai---date:2023-09-22---for:【QQYUN-6603】分割线标题位置显示不正确---
+          componentProps = Object.assign({ type: 'horizontal',orientation:'left', plain: true, }, componentProps);
+          //update-end---author:wangshuai---date:2023-09-22---for:【QQYUN-6603】分割线标题位置显示不正确---
         }
         return componentProps as Recordable;
       });
@@ -201,12 +206,17 @@
       }
 
       function renderComponent() {
-        const { renderComponentContent, component, field, changeEvent = 'change', valueField } = props.schema;
+        const { renderComponentContent, component, field, changeEvent = 'change', valueField, componentProps } = props.schema;
 
         const isCheck = component && ['Switch', 'Checkbox'].includes(component);
-
+        // update-begin--author:liaozhiyang---date:20231013---for:【QQYUN-6679】input去空格
+        let isTrim = false;
+        if (component === 'Input' && componentProps && componentProps.trim) {
+          isTrim = true;
+        }
+        // update-end--author:liaozhiyang---date:20231013---for:【QQYUN-6679】input去空格
         const eventKey = `on${upperFirst(changeEvent)}`;
-
+        // update-begin--author:liaozhiyang---date:20230922---for:【issues/752】表单校验dynamicRules 无法 使用失去焦点后校验 trigger: 'blur'
         const on = {
           [eventKey]: (...args: Nullable<Recordable>[]) => {
             const [e] = args;
@@ -214,10 +224,26 @@
               propsData[eventKey](...args);
             }
             const target = e ? e.target : null;
-            const value = target ? (isCheck ? target.checked : target.value) : e;
+            // update-begin--author:liaozhiyang---date:20231013---for:【QQYUN-6679】input去空格
+            let value;
+            if (target) {
+              if (isCheck) {
+                value = target.checked;
+              } else {
+                value = isTrim ? target.value.trim() : target.value;
+              }
+            } else {
+              value = e;
+            }
+            // update-end--author:liaozhiyang---date:20231013---for:【QQYUN-6679】input去空格
             props.setFormModel(field, value);
+            //props.validateFields([field], { triggerName: 'change' }).catch((_) => {});
           },
+          // onBlur: () => {
+          //   props.validateFields([field], { triggerName: 'blur' }).catch((_) => {});
+          // },
         };
+        // update-end--author:liaozhiyang---date:20230922---for:【issues/752】表单校验dynamicRules 无法 使用失去焦点后校验 trigger: 'blur'
         const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;
 
         const { autoSetPlaceHolder, size } = props.formProps;
@@ -341,7 +367,14 @@
         }
 
         const { baseColProps = {} } = props.formProps;
-        const realColProps = { ...baseColProps, ...colProps };
+        // update-begin--author:liaozhiyang---date:20230803---for:【issues-641】调整表格搜索表单的span配置无效 
+        const { getIsMobile } = useAppInject();
+        let realColProps;
+        if (colProps['span'] && !unref(getIsMobile)) {
+          ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'].forEach((name) => delete baseColProps[name]);
+        }
+        realColProps = { ...baseColProps, ...colProps };
+        // update-end--author:liaozhiyang---date:20230803---for:【issues-641】调整表格搜索表单的span配置无效 
         const { isIfShow, isShow } = getShow();
         const values = unref(getValues);
 

+ 23 - 18
src/components/Form/src/hooks/useForm.ts

@@ -1,16 +1,16 @@
 import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form';
-import type { NamePath } from 'ant-design-vue/lib/form/interface';
+import type { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
 import type { DynamicProps } from '/#/utils';
 import { handleRangeValue } from '../utils/formUtils';
 import { ref, onUnmounted, unref, nextTick, watch } from 'vue';
 import { isProdMode } from '/@/utils/env';
 import { error } from '/@/utils/log';
 import { getDynamicProps, getValueType } from '/@/utils';
-import { add } from '/@/components/Form/src/componentMap';
+import { add } from "/@/components/Form/src/componentMap";
 //集成online专用控件
-import { OnlineSelectCascade, LinkTableCard, LinkTableSelect } from '@jeecg/online';
+import { OnlineSelectCascade, LinkTableCard, LinkTableSelect } from  '@jeecg/online';
 
-export declare type ValidateFields = (nameList?: NamePath[]) => Promise<Recordable>;
+export declare type ValidateFields = (nameList?: NamePath[], options?: ValidateOptions) => Promise<Recordable>;
 
 type Props = Partial<DynamicProps<FormProps>>;
 
@@ -19,10 +19,10 @@ export function useForm(props?: Props): UseFormReturnType {
   const loadedRef = ref<Nullable<boolean>>(false);
 
   //集成online专用控件
-  add('OnlineSelectCascade', OnlineSelectCascade);
-  add('LinkTableCard', LinkTableCard);
-  add('LinkTableSelect', LinkTableSelect);
-
+  add("OnlineSelectCascade", OnlineSelectCascade)
+  add("LinkTableCard", LinkTableCard)
+  add("LinkTableSelect", LinkTableSelect)
+  
   async function getForm() {
     const form = unref(formRef);
     if (!form) {
@@ -93,11 +93,16 @@ export function useForm(props?: Props): UseFormReturnType {
     // TODO promisify
     getFieldsValue: <T>() => {
       //update-begin-author:taoyan date:2022-7-5 for: VUEN-1341【流程】编码方式 流程节点编辑表单时,填写数据报错 包括用户组件、部门组件、省市区
-      const values = unref(formRef)?.getFieldsValue() as T;
-      if (values) {
-        Object.keys(values).map((key) => {
+      let values = unref(formRef)?.getFieldsValue() as T;
+      if(values){
+        Object.keys(values).map(key=>{
           if (values[key] instanceof Array) {
-            values[key] = values[key].join(',');
+            // update-begin-author:sunjianlei date:20221205 for: 【issues/4330】判断如果是对象数组,则不拼接
+            let isObject = typeof (values[key][0] || '') === 'object';
+            if (!isObject) {
+              values[key] = values[key].join(',');
+            }
+            // update-end-author:sunjianlei date:20221205 for: 【issues/4330】判断如果是对象数组,则不拼接
           }
         });
       }
@@ -128,11 +133,11 @@ export function useForm(props?: Props): UseFormReturnType {
      */
     validate: async (nameList?: NamePath[]): Promise<Recordable> => {
       const form = await getForm();
-      const getProps = props || form.getProps;
-      const values = form.validate(nameList).then((values) => {
-        for (const key in values) {
+      let getProps = props || form.getProps;
+      let values = form.validate(nameList).then((values) => {
+        for (let key in values) {
           if (values[key] instanceof Array) {
-            const valueType = getValueType(getProps, key);
+            let valueType = getValueType(getProps, key);
             if (valueType === 'string') {
               values[key] = values[key].join(',');
             }
@@ -144,9 +149,9 @@ export function useForm(props?: Props): UseFormReturnType {
       });
       return values;
     },
-    validateFields: async (nameList?: NamePath[]): Promise<Recordable> => {
+    validateFields: async (nameList?: NamePath[], options?: ValidateOptions): Promise<Recordable> => {
       const form = await getForm();
-      return form.validateFields(nameList);
+      return form.validateFields(nameList, options);
     },
   };
 

+ 3 - 3
src/components/Form/src/hooks/useFormEvents.ts

@@ -1,6 +1,6 @@
 import type { ComputedRef, Ref } from 'vue';
 import type { FormProps, FormSchema, FormActionType } from '../types/form';
-import type { NamePath } from 'ant-design-vue/lib/form/interface';
+import type { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
 import { unref, toRaw } from 'vue';
 import { isArray, isFunction, isObject, isString } from '/@/utils/is';
 import { deepMerge, getValueType } from '/@/utils';
@@ -207,8 +207,8 @@ export function useFormEvents({
     });
   }
 
-  async function validateFields(nameList?: NamePath[] | undefined) {
-    return unref(formElRef)?.validateFields(nameList);
+  async function validateFields(nameList?: NamePath[] | undefined, options?: ValidateOptions) {
+    return unref(formElRef)?.validateFields(nameList, options);
   }
 
   async function validate(nameList?: NamePath[] | undefined) {

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

@@ -15,7 +15,7 @@
     },
     inheritAttrs: false,
     props: {
-      value: propTypes.oneOfType([propTypes.object, propTypes.array]),
+      value: propTypes.oneOfType([propTypes.object, propTypes.array, propTypes.string]),
       //是否显示区县
       showArea: propTypes.bool.def(true),
       //是否是全部

+ 4 - 3
src/components/Form/src/jeecg/components/JCategorySelect.vue

@@ -71,13 +71,12 @@
         required: false,
       },
     },
-    emits: ['options-change', 'change'],
+    emits: ['options-change', 'change', 'update:value'],
     setup(props, { emit, refs }) {
       console.info(props);
       const emitData = ref<any[]>([]);
       const treeData = ref<any[]>([]);
       const treeValue = ref();
-      treeValue.value = '';
       const attrs = useAttrs();
       const [state] = useRuleFormItem(props, 'value', 'change', emitData);
       watch(
@@ -122,7 +121,7 @@
           if(props.multiple){
             treeValue.value = [];
           }else{
-            treeValue.value = '';
+            treeValue.value = { value: null, label: null };
           }
         } else {
           loadDictItem({ ids: props.value }).then((res) => {
@@ -153,6 +152,7 @@
           obj[props.back] = label;
         }
         emit('change', value, obj);
+        emit("update:value",value)
       }
 
       function asyncLoadTreeData(treeNode) {
@@ -205,6 +205,7 @@
         if (!value) {
           emit('change', '');
           treeValue.value = '';
+          emit("update:value",'')
         } else if (Array.isArray(value)) {
           let labels = [];
           let values = value.map((item) => {

+ 28 - 12
src/components/Form/src/jeecg/components/JCodeEditor.vue

@@ -1,5 +1,5 @@
 <template>
-  <div v-bind="boxBindProps">
+  <div ref="containerRef" v-bind="boxBindProps">
     <!-- 全屏按钮 -->
     <a-icon v-if="fullScreen" class="full-screen-icon" :type="fullScreenIcon" @click="onToggleFullScreen" />
     <textarea ref="textarea" v-bind="getBindValue"></textarea>
@@ -45,12 +45,14 @@
   import { useAttrs } from '/@/hooks/core/useAttrs';
   import { useDesign } from '/@/hooks/web/useDesign';
   import { isJsonObjectString } from '/@/utils/is.ts';
+  // 代码提示
+  import { useCodeHinting } from '../hooks/useCodeHinting';
 
   export default defineComponent({
     name: 'JCodeEditor',
-    components: {},
     // 不将 attrs 的属性绑定到 html 标签上
     inheritAttrs: false,
+    components: {},
     props: {
       value: propTypes.string.def(''),
       height: propTypes.string.def('auto'),
@@ -61,9 +63,12 @@
       zIndex: propTypes.any.def(999),
       theme: propTypes.string.def('idea'),
       language: propTypes.string.def(''),
+      // 代码提示
+      keywords: propTypes.array.def([]),
     },
     emits: ['change', 'update:value'],
     setup(props, { emit }) {
+      const containerRef = ref(null);
       const { prefixCls } = useDesign('code-editer');
       const CodeMirror = window.CodeMirror || _CodeMirror;
       const emitData = ref<object>();
@@ -121,6 +126,10 @@
         }
         return _props;
       });
+      // update-begin--author:liaozhiyang---date:20230904---for:【QQYUN-5955】online js增强,加入代码提示
+      const { codeHintingMount, codeHintingRegistry } = useCodeHinting(CodeMirror, props.keywords, props.language);
+      codeHintingRegistry();
+      // update-end--author:liaozhiyang---date:20230904---for:【QQYUN-5955】online js增强,加入代码提示
       /**
        * 监听组件值
        */
@@ -171,6 +180,9 @@
         coder.on('change', onChange);
         // 初始化成功时赋值一次
         setValue(innerValue, false);
+        // update-begin--author:liaozhiyang---date:20230904---for:【QQYUN-5955】online js增强,加入代码提示
+        codeHintingMount(coder);
+        // update-end--author:liaozhiyang---date:20230904---for:【QQYUN-5955】online js增强,加入代码提示
       }
 
       // 切换全屏状态
@@ -188,16 +200,13 @@
         }
       );
       //update-end-author:taoyan date:2022-5-9 for: codeEditor禁用功能
-
+      
       // 支持动态设置语言
-      watch(
-        () => props.language,
-        (val) => {
-          if (val && coder) {
-            coder.setOption('mode', val);
-          }
+      watch(()=>props.language, (val)=>{
+        if(val && coder){
+          coder.setOption('mode', val);
         }
-      );
+      });
 
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
 
@@ -208,7 +217,8 @@
         }
       }
       //update-end-author:taoyan date:2022-10-18 for: VUEN-2480【严重bug】online vue3测试的问题 8、online js增强样式问题
-      
+
+
       return {
         state,
         textarea,
@@ -218,7 +228,8 @@
         isFullScreen,
         fullScreenIcon,
         onToggleFullScreen,
-        refresh
+        refresh,
+        containerRef,
       };
     },
   });
@@ -298,4 +309,9 @@
       border: 1px solid #ddd;
     }
   }
+  .CodeMirror-hints.idea {
+    z-index: 1001;
+    max-width: 600px;
+    max-height: 300px;
+  }
 </style>

+ 35 - 20
src/components/Form/src/jeecg/components/JDictSelectTag.vue

@@ -35,6 +35,7 @@
       v-model:value="state"
       :filterOption="handleFilterOption"
       :getPopupContainer="getPopupContainer"
+      :style="style"
       @change="handleChange"
     >
       <a-select-option v-if="showChooseOption" :value="null">请选择…</a-select-option>
@@ -60,8 +61,8 @@
 
   export default defineComponent({
     name: 'JDictSelectTag',
-    components: { LoadingOutlined },
     inheritAttrs: false,
+    components: { LoadingOutlined },
     props: {
       value: propTypes.oneOfType([propTypes.string, propTypes.number, propTypes.array]),
       dictCode: propTypes.string,
@@ -80,8 +81,9 @@
         default: [],
         required: false,
       },
+      style: propTypes.any,
     },
-    emits: ['options-change', 'change'],
+    emits: ['options-change', 'change','update:value'],
     setup(props, { emit, refs }) {
       const dictOptions = ref<any[]>([]);
       const attrs = useAttrs();
@@ -146,39 +148,52 @@
 
       function handleChange(e) {
         const { mode } = unref<Recordable>(getBindValue);
+        let changeValue:any;
         // 兼容多选模式
+        
+        //update-begin---author:wangshuai ---date:20230216  for:[QQYUN-4290]公文发文:选择机关代字报错,是因为值改变触发了change事件三次,导致数据发生改变------------
+        //采用一个值,不然的话state值变换触发多个change
         if (mode === 'multiple') {
-          state.value = e?.target?.value ?? e;
+          changeValue = e?.target?.value ?? e;
+          // 过滤掉空值
+          if (changeValue == null || changeValue === '') {
+            changeValue = [];
+          }
+          if (Array.isArray(changeValue)) {
+            changeValue = changeValue.filter((item) => item != null && item !== '');
+          }
         } else {
-          state.value = [e?.target?.value ?? e];
-        }
-        // 过滤掉空值
-        if (state.value == null || state.value === '') {
-          state.value = [];
+          changeValue = e?.target?.value ?? e;
         }
-        if (Array.isArray(state.value)) {
-          state.value = state.value.filter((item) => item != null && item !== '');
-        }
-        //update-begin---author:wangshuai ---date:20221123  for:单选模式要改成字符串------------
-        if (mode !== 'multiple' && state.value && state.value.length > 0) {
-          state.value = state.value[0];
-        }
-        //update-end---author:wangshuai ---date:20221123  for:单选模式要改成字符串--------------
+        state.value = changeValue;
+
+        //update-begin---author:wangshuai ---date:20230403  for:【issues/4507】JDictSelectTag组件使用时,浏览器给出警告提示:Expected Function, got Array------------
+        emit('update:value',changeValue)
+        //update-end---author:wangshuai ---date:20230403  for:【issues/4507】JDictSelectTag组件使用时,浏览器给出警告提示:Expected Function, got Array述------------
+        //update-end---author:wangshuai ---date:20230216  for:[QQYUN-4290]公文发文:选择机关代字报错,是因为值改变触发了change事件三次,导致数据发生改变------------
+        
         // nextTick(() => formItemContext.onFieldChange());
       }
 
       /** 单选radio的值变化事件 */
       function handleChangeRadio(e) {
         state.value = e?.target?.value ?? e;
+        //update-begin---author:wangshuai ---date:20230504  for:【issues/506】JDictSelectTag 组件 type="radio" 没有返回值------------
+        emit('update:value',e?.target?.value ?? e)
+        //update-end---author:wangshuai ---date:20230504  for:【issues/506】JDictSelectTag 组件 type="radio" 没有返回值------------
       }
 
       /** 用于搜索下拉框中的内容 */
       function handleFilterOption(input, option) {
-        // 在 label 中搜索
-        let labelIf = option.children()[0]?.children.toLowerCase().indexOf(input.toLowerCase()) >= 0;
-        if (labelIf) {
-          return true;
+        // update-begin--author:liaozhiyang---date:20230914---for:【QQYUN-6514】 配置的时候,Y轴不能输入多个字段了,控制台报错
+        if (typeof option.children === 'function') {
+          // 在 label 中搜索
+          let labelIf = option.children()[0]?.children.toLowerCase().indexOf(input.toLowerCase()) >= 0;
+          if (labelIf) {
+            return true;
+          }
         }
+        // update-end--author:liaozhiyang---date:20230914---for:【QQYUN-6514】 配置的时候,Y轴不能输入多个字段了,控制台报错
         // 在 value 中搜索
         return (option.value || '').toString().toLowerCase().indexOf(input.toLowerCase()) >= 0;
       }

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

@@ -11,7 +11,7 @@
   import { propTypes } from '/@/utils/propTypes';
 
   const props = defineProps({
-    value: propTypes.oneOfType([propTypes.string, propTypes.array]),
+    value: propTypes.oneOfType([propTypes.string, propTypes.number, propTypes.array]),
     length: propTypes.number.def(25),
   });
   //显示的文本

+ 8 - 6
src/components/Form/src/jeecg/components/JImageUpload.vue

@@ -2,6 +2,7 @@
   <div class="clearfix">
     <a-upload
       :listType="listType"
+      accept="image/*"
       :multiple="multiple"
       :action="uploadUrl"
       :headers="headers"
@@ -36,7 +37,7 @@
   import { propTypes } from '/@/utils/propTypes';
   import { useAttrs } from '/@/hooks/core/useAttrs';
   import { useMessage } from '/@/hooks/web/useMessage';
-  import { getFileAccessHttpUrl, getRandom } from '/@/utils/common/compUtils';
+  import { getFileAccessHttpUrl, getHeaders, getRandom } from '/@/utils/common/compUtils';
   import { uploadUrl } from '/@/api/common/api';
   import { getToken } from '/@/utils/auth';
 
@@ -93,9 +94,7 @@
         return path.substring(path.lastIndexOf('/') + 1);
       };
       //token
-      const headers = ref<object>({
-        'X-Access-Token': getToken(),
-      });
+      const headers = getHeaders();
       //上传状态
       const loading = ref<boolean>(false);
       //是否是初始化加载
@@ -123,13 +122,16 @@
       watch(
         () => props.value,
         (val, prevCount) => {
-          if (val instanceof Array) {
+         //update-begin---author:liusq ---date:20230601  for:【issues/556】JImageUpload组件value赋初始值没显示图片------------
+            if (val && val instanceof Array) {
             val = val.join(',');
           }
           if (initTag.value == true) {
             initFileList(val);
           }
-        }
+        },
+        { immediate: true }
+        //update-end---author:liusq ---date:20230601  for:【issues/556】JImageUpload组件value赋初始值没显示图片------------
       );
 
       /**

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

@@ -3,10 +3,11 @@
 </template>
 
 <script lang="ts">
-  import { defineComponent, PropType, ref, watchEffect, unref, watch } from 'vue';
+  import { defineComponent, PropType, ref, watchEffect, unref, watch, computed } from 'vue';
   import { useAttrs } from '/@/hooks/core/useAttrs';
   import { propTypes } from '/@/utils/propTypes';
   import { JInputTypeEnum } from '/@/enums/jeecgEnum.ts';
+  import { omit } from 'lodash-es';
 
   export default defineComponent({
     name: 'JInput',
@@ -22,8 +23,12 @@
       const attrs = useAttrs();
       //表单值
       const showText = ref('');
+      // update-begin--author:liaozhiyang---date:20231026---for:【issues/803】JIput updateSchema不生效
       //绑定属性
-      const getBindValue = Object.assign({}, unref(props), unref(attrs));
+      const getBindValue = computed(() => {
+        return omit(Object.assign({}, unref(props), unref(attrs)), ['value']);
+      });
+      // update-end--author:liaozhiyang---date:20231026---for:【issues/803】JIput updateSchema不生效
       //监听类型变化
       watch(
         () => props.type,

+ 11 - 3
src/components/Form/src/jeecg/components/JPopup.vue

@@ -57,7 +57,7 @@
         default: () => [],
       },
     },
-    emits: ['update:value', 'register'],
+    emits: ['update:value', 'register', 'popUpChange', 'focus'],
     setup(props, { emit, refs }) {
       const { createMessage } = useMessage();
       const attrs = useAttrs();
@@ -67,9 +67,11 @@
       //注册model
       const [regModal, { openModal }] = useModal();
       //表单值
-      let { groupId, code, fieldConfig } = props;
+      let {code, fieldConfig } = props;
+      // update-begin--author:liaozhiyang---date:20230811---for:【issues/675】子表字段Popup弹框数据不更新
       //唯一分组groupId
-      const uniqGroupId = computed(() => (groupId ? `${groupId}_${code}_${fieldConfig[0]['source']}_${fieldConfig[0]['target']}` : ''));
+      const uniqGroupId = computed(() => (props.groupId ? `${props.groupId}_${code}_${fieldConfig[0]['source']}_${fieldConfig[0]['target']}` : ''));
+      // update-begin--author:liaozhiyang---date:20230811---for:【issues/675】子表字段Popup弹框数据不更新
       /**
        * 判断popup配置项是否正确
        */
@@ -94,6 +96,7 @@
        * 打开pop弹出框
        */
       function handleOpen() {
+        emit('focus');
         !props.disabled && openModal(true);
       }
 
@@ -121,6 +124,11 @@
         props.formElRef && props.formElRef.setFieldsValue(values);
         //传入赋值方法方式赋值
         props.setFieldsValue && props.setFieldsValue(values);
+        // update-begin--author:liaozhiyang---date:20230831---for:【issues/5288】popup弹框,无法将选择的数据填充到自身
+        // update-begin--author:liaozhiyang---date:20230811---for:【issues/5213】JPopup抛出change事件
+        emit('popUpChange', values);
+        // update-end--author:liaozhiyang---date:20230811---for:【issues/5213】JPopup抛出change事件
+        // update-begin--author:liaozhiyang---date:20230831---for:【issues/5288】popup弹框,无法将选择的数据填充到自身
       }
 
       return {

+ 54 - 56
src/components/Form/src/jeecg/components/JRangeDate.vue

@@ -1,67 +1,65 @@
 <template>
-  <a-range-picker v-model:value="rangeValue" @change="handleChange" :show-time="datetime" :placeholder="placeholder" :valueFormat="valueFormat" />
+    <a-range-picker v-model:value="rangeValue" @change="handleChange" :show-time="datetime" :placeholder="placeholder" :valueFormat="valueFormat"/>
 </template>
 
 <script>
-  import { defineComponent, ref, watch, computed } from 'vue';
-  import { propTypes } from '/@/utils/propTypes';
-  import { Form } from 'ant-design-vue';
+    import { defineComponent, ref, watch, computed } from 'vue';
+    import { propTypes } from '/@/utils/propTypes';
+    import { Form } from 'ant-design-vue';
 
-  const placeholder = ['最小值', '最大值'];
-  /**
-   * 用于范围查询
-   */
-  export default defineComponent({
-    name: 'JRangeDate',
-    props: {
-      value: propTypes.string.def(''),
-      datetime: propTypes.bool.def(false),
-      placeholder: propTypes.string.def(''),
-    },
-    emits: ['change', 'update:value'],
-    setup(props, { emit }) {
-      const rangeValue = ref([]);
-      const formItemContext = Form.useInjectFormItemContext();
-
-      watch(
-        () => props.value,
-        (val) => {
-          if (val) {
-            rangeValue.value = val.split(',');
-          } else {
-            rangeValue.value = [];
-          }
+    const placeholder = ['开始日期', '结束日期']
+    /**
+     * 用于范围查询
+     */
+    export default defineComponent({
+        name: "JRangeDate",
+        props:{
+            value: propTypes.string.def(''),
+            datetime: propTypes.bool.def(false),
+            placeholder: propTypes.string.def(''),
         },
-        { immediate: true }
-      );
+        emits:['change', 'update:value'],
+        setup(props, {emit}){
+            const rangeValue = ref([])
+            const formItemContext = Form.useInjectFormItemContext();
 
-      const valueFormat = computed(() => {
-        if (props.datetime === true) {
-          return 'YYYY-MM-DD HH:mm:ss';
-        } else {
-          return 'YYYY-MM-DD';
-        }
-      });
+            watch(()=>props.value, (val)=>{
+                if(val){
+                    rangeValue.value = val.split(',')
+                }else{
+                    rangeValue.value = []
+                }
+            }, {immediate: true});
 
-      function handleChange(arr) {
-        let str = '';
-        if (arr && arr.length > 0) {
-          if (arr[1] && arr[0]) {
-            str = arr.join(',');
-          }
+            const valueFormat = computed(()=>{
+                if(props.datetime === true){
+                    return 'YYYY-MM-DD HH:mm:ss'
+                }else{
+                    return 'YYYY-MM-DD'
+                }
+            });
+
+            function handleChange(arr){
+                let str = ''
+                if(arr && arr.length>0){
+                    if(arr[1] && arr[0]){
+                        str = arr.join(',')
+                    }
+                }
+                emit('change', str);
+                emit('update:value', str);
+                formItemContext.onFieldChange();
+            }
+            return {
+                rangeValue,
+                placeholder,
+                valueFormat,
+                handleChange
+            }
         }
-        emit('change', str);
-        emit('update:value', str);
-        formItemContext.onFieldChange();
-      }
-      return {
-        rangeValue,
-        placeholder,
-        valueFormat,
-        handleChange,
-      };
-    },
-  });
+    });
 </script>
 
-<style scoped></style>
+<style scoped>
+
+</style>

+ 53 - 0
src/components/Form/src/jeecg/components/JRangeTime.vue

@@ -0,0 +1,53 @@
+<template>
+    <a-time-range-picker v-model:value="rangeValue" @change="handleChange" :placeholder="placeholder" :valueFormat="format" :format="format"/>
+</template>
+
+<script>
+    import { defineComponent, ref, watch } from 'vue';
+    import { propTypes } from '/@/utils/propTypes';
+    import { Form } from 'ant-design-vue';
+
+    const placeholder = ['开始时间', '结束时间']
+    /**
+     * 用于时间-time组件的范围查询
+     */
+    export default defineComponent({
+        name: "JRangeTime",
+        props:{
+            value: propTypes.string.def(''),
+            format: propTypes.string.def('HH:mm:ss'),
+            placeholder: propTypes.string.def(''),
+        },
+        emits:['change', 'update:value'],
+        setup(props, {emit}){
+            const rangeValue = ref([])
+            const formItemContext = Form.useInjectFormItemContext();
+
+            watch(()=>props.value, (val)=>{
+                if(val){
+                    rangeValue.value = val.split(',')
+                }else{
+                    rangeValue.value = []
+                }
+            }, {immediate: true});
+
+
+            function handleChange(arr){
+                let str = ''
+                if(arr && arr.length>0){
+                    if(arr[1] && arr[0]){
+                        str = arr.join(',')
+                    }
+                }
+                emit('change', str);
+                emit('update:value', str);
+                formItemContext.onFieldChange();
+            }
+            return {
+                rangeValue,
+                placeholder,
+                handleChange
+            }
+        }
+    });
+</script>

+ 12 - 2
src/components/Form/src/jeecg/components/JSearchSelect.vue

@@ -29,6 +29,7 @@
     :placeholder="placeholder"
     :filterOption="filterOption"
     :notFoundContent="loading ? undefined : null"
+    :dropdownAlign="{overflow: {adjustY: adjustY }}"
     @change="handleChange"
   >
     <template #notFoundContent>
@@ -64,6 +65,10 @@
         type: Function,
         default: (node) => node.parentNode,
       },
+      //默认开启Y轴溢出位置调整,因此在可视空间不足时下拉框位置会自动上移,导致Select的输入框被遮挡。需要注意的是,默认情况是是可视空间,而不是所拥有的空间
+      //update-begin-author:liusq date:2023-04-04 for:[issue/286]下拉搜索框遮挡问题
+      adjustY:propTypes.bool.def(true),
+      //update-end-author:liusq date:2023-04-04 for:[issue/286]下拉搜索框遮挡问题
       //是否在有值后立即触发change
       immediateChange: propTypes.bool.def(false),
       //update-begin-author:taoyan date:2022-8-15 for: VUEN-1971 【online 专项测试】关联记录和他表字段 1
@@ -111,7 +116,7 @@
       watch(
         () => props.dictOptions,
         (val) => {
-          if (val && val.length > 0) {
+          if (val && val.length >= 0) {
             options.value = [...val];
           }
         },
@@ -293,7 +298,12 @@
         // 如果设定了排序信息,需要写入排序信息,在关键词后加 [orderby:create_time,desc]
         if(props.params && props.params.column && props.params.order){
           let temp = text||''
-          return temp+'[orderby:'+props.params.column+','+props.params.order+']'
+          
+          //update-begin-author:taoyan date:2023-5-22 for: /issues/4905 表单生成器字段配置时,选择关联字段,在进行高级配置时,无法加载数据库列表,提示 Sgin签名校验错误! #4905
+          temp = temp+'[orderby:'+props.params.column+','+props.params.order+']'
+          return encodeURI(temp);
+          //update-end-author:taoyan date:2023-5-22 for: /issues/4905 表单生成器字段配置时,选择关联字段,在进行高级配置时,无法加载数据库列表,提示 Sgin签名校验错误! #4905
+          
         }else{
           return text;
         }

+ 14 - 2
src/components/Form/src/jeecg/components/JSelectDept.vue

@@ -1,8 +1,8 @@
 <!--部门选择组件-->
 <template>
   <div>
-    <JSelectBiz @handleOpen="handleOpen" :loading="loadingEcho" v-bind="attrs" />
-    <DeptSelectModal @register="regModal" @getSelectResult="setValue" v-bind="getBindValue" />
+    <JSelectBiz @handleOpen="handleOpen" :loading="loadingEcho" v-bind="attrs" @change="handleChange"/>
+    <DeptSelectModal @register="regModal" @getSelectResult="setValue" v-bind="getBindValue" :multiple="multiple" />
   </div>
 </template>
 <script lang="ts">
@@ -119,6 +119,17 @@
         emit('update:value', values.join(','));
       }
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
+
+      //update-begin---author:wangshuai ---date:20230406  for:【issues/397】在表单中使用v-model:value绑定JSelectDept组件时无法清除已选择的数据------------
+      /**
+       * 值改变事件更新value值
+       * @param values
+       */
+      function handleChange(values) {
+        emit('update:value', values);
+      }
+      //update-end---author:wangshuai ---date:20230406  for:【issues/397】在表单中使用v-model:value绑定JSelectDept组件时无法清除已选择的数据------------
+      
       return {
         state,
         attrs,
@@ -130,6 +141,7 @@
         regModal,
         setValue,
         handleOpen,
+        handleChange
       };
     },
   });

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

@@ -30,14 +30,14 @@
       },
       rowKey: {
         type: String,
-        default: 'code',
+        default: 'id',
       },
       params: {
         type: Object,
         default: () => {},
       },
     },
-    emits: ['options-change', 'change'],
+    emits: ['options-change', 'change', 'update:value'],
     setup(props, { emit, refs }) {
       const emitData = ref<any[]>();
       //注册model
@@ -108,6 +108,10 @@
         //emitData.value = values.join(",");
         state.value = values;
         selectValues.value = values;
+        //update-begin-author:liusq date:20230517 for:选择职务组件v-model方式绑定值不生效
+        emit('update:value', values.join(','));
+        //update-begin-author:liusq date:20230517 for:选择职务组件v-model方式绑定值不生效
+
       }
 
       const getBindValue = Object.assign({}, unref(props), unref(attrs));

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

@@ -55,7 +55,7 @@
 
   async function loadViewInfo() {
     if (!props.value || props.value == '0') {
-      treeValue.value = null;
+      treeValue.value = { value: null, label: null };
     } else {
       let params = { field: props.field, val: props.value };
       let result = await defHttp.get({ url: Api.view, params });

+ 23 - 11
src/components/Form/src/jeecg/components/JTreeSelect.vue

@@ -13,7 +13,9 @@
     v-bind="attrs"
     @change="onChange"
     @search="onSearch"
-  />
+    :tree-checkable="treeCheckAble"
+  >
+  </a-tree-select>
 </template>
 <script lang="ts" setup>
   /*
@@ -49,8 +51,12 @@
     reload: propTypes.number.def(1),
     //update-begin-author:taoyan date:2022-11-8 for: issues/4173 Online JTreeSelect控件changeOptions方法未生效
     url: propTypes.string.def(''),
-    params: propTypes.object.def({})
+    params: propTypes.object.def({}),
     //update-end-author:taoyan date:2022-11-8 for: issues/4173 Online JTreeSelect控件changeOptions方法未生效
+    //update-begin---author:wangshuai date: 20230202 for: 新增是否有复选框
+    //默认没有选择框
+    treeCheckAble: propTypes.bool.def(false),
+    //update-end---author:wangshuai date: 20230202 for: 新增是否有复选框
   });
   const attrs = useAttrs();
   const emit = defineEmits(['change', 'update:value']);
@@ -100,10 +106,10 @@
    */
   async function loadItemByCode() {
     if (!props.value || props.value == '0') {
-      if (props.multiple) {
+      if(props.multiple){
         treeValue.value = [];
-      } else {
-        treeValue.value = null;
+      }else{
+        treeValue.value = { label: null, value: null };
       }
     } else {
       //update-begin-author:taoyan date:2022-11-8 for: issues/4173 Online JTreeSelect控件changeOptions方法未生效
@@ -113,12 +119,18 @@
         let params = { key: props.value };
         let result = await defHttp.get({ url: `${Api.view}${props.dict}`, params }, { isTransformResponse: false });
         if (result.success) {
-          let values = props.value.split(',');
-          treeValue.value = result.result.map((item, index) => ({
-            key: values[index],
-            value: values[index],
-            label: item,
-          }));
+          //update-start-author:liaozhiyang date:2023-7-17 for:【issues/5141】使用JtreeSelect 组件 控制台报错
+          if(props.multiple){
+            let values = props.value.split(',');
+            treeValue.value = result.result.map((item, index) => ({
+              key: values[index],
+              value: values[index],
+              label: item,
+            }));
+          }else{
+            treeValue.value = { key: props.value, value: props.value, label: result.result[0] };
+          }
+          //update-end-author:liaozhiyang date:2023-7-17 for:【issues/5141】使用JtreeSelect 组件 控制台报错
           onLoadTriggleChange(result.result[0]);
         }
       }

+ 44 - 13
src/components/Form/src/jeecg/components/JUpload/JUpload.vue

@@ -17,7 +17,7 @@
           <div class="ant-upload-text">{{ text }}</div>
         </div>
       </template>
-      <a-button v-else-if="buttonVisible" :disabled="isMaxCount || disabled">
+      <a-button v-else-if="buttonVisible" :disabled="buttonDisabled">
         <Icon icon="ant-design:upload-outlined" />
         <span>{{ text }}</span>
       </a-button>
@@ -26,7 +26,7 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref, reactive, computed, watch, nextTick, createApp } from 'vue';
+  import { ref, reactive, computed, watch, nextTick, createApp,unref } from 'vue';
   import { Icon } from '/@/components/Icon';
   import { getToken } from '/@/utils/auth';
   import { uploadUrl } from '/@/api/common/api';
@@ -36,9 +36,8 @@
   import { useAttrs } from '/@/hooks/core/useAttrs';
   import { useDesign } from '/@/hooks/web/useDesign';
   import { UploadTypeEnum } from './upload.data';
-  import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
+  import { getFileAccessHttpUrl, getHeaders } from '/@/utils/common/compUtils';
   import UploadItemActions from './components/UploadItemActions.vue';
-  import { antPrefixCls } from '/@/settings/designSetting'
 
   const { createMessage, createConfirm } = useMessage();
   const { prefixCls } = useDesign('j-upload');
@@ -68,11 +67,11 @@
     removeConfirm: propTypes.bool.def(false),
     beforeUpload: propTypes.func,
     disabled: propTypes.bool.def(false),
+    // 替换前一个文件,用于超出最大数量依然允许上传
+    replaceLastOne: propTypes.bool.def(false),
   });
 
-  const headers = reactive({
-    'X-Access-Token': getToken(),
-  });
+  const headers = getHeaders();
   const fileList = ref<any[]>([]);
   const uploadGoOn = ref<boolean>(true);
   // refs
@@ -81,9 +80,26 @@
   const isMaxCount = computed(() => props.maxCount > 0 && fileList.value.length >= props.maxCount);
   // 当前是否是上传图片模式
   const isImageMode = computed(() => props.fileType === UploadTypeEnum.image);
+  // 上传按钮是否禁用
+  const buttonDisabled = computed(()=>{
+    if(props.disabled === true){
+      return true;
+    }
+    if(isMaxCount.value === true){
+      if(props.replaceLastOne === true){
+        return false
+      }else{
+        return true;
+      }
+    }
+    return false
+  });
   // 合并 props 和 attrs
   const bindProps = computed(() => {
-    const bind: any = Object.assign({}, props, attrs);
+    //update-begin-author:liusq date:20220411 for: [issue/455]上传组件传入accept限制上传文件类型无效
+    const bind: any = Object.assign({}, props, unref(attrs));
+    //update-end-author:liusq date:20220411 for: [issue/455]上传组件传入accept限制上传文件类型无效
+
     bind.name = 'file';
     bind.listType = isImageMode.value ? 'picture-card' : 'text';
     bind.class = [bind.class, { 'upload-disabled': props.disabled }];
@@ -110,7 +126,13 @@
           parseArrayValue(val);
         }
       } else {
-        parsePathsValue(val);
+        //update-begin---author:liusq ---date:20230914  for:[issues/5327]Upload组件returnUrl为false时上传的字段值返回了一个'[object Object]' ------------
+        if (props.returnUrl) {
+          parsePathsValue(val);
+        } else {
+          val && parseArrayValue(JSON.parse(val));
+        }
+        //update-end---author:liusq ---date:20230914  for:[issues/5327]Upload组件returnUrl为false时上传的字段值返回了一个'[object Object]' ------------
       }
     },
     { immediate: true }
@@ -118,7 +140,7 @@
 
   watch(fileList, () => nextTick(() => addActionsListener()), { immediate: true });
 
-  const antUploadItemCls = `${antPrefixCls}-upload-list-item`;
+  const antUploadItemCls = 'ant-upload-list-item';
 
   // Listener
   function addActionsListener() {
@@ -264,15 +286,22 @@
       }
     }
     if (info.file.status === 'done') {
+      let successFileList = [];
       if (info.file.response.success) {
-        fileListTemp = fileListTemp.map((file) => {
+        successFileList = fileListTemp.map((file) => {
           if (file.response) {
             let reUrl = file.response.message;
             file.url = getFileAccessHttpUrl(reUrl);
           }
           return file;
         });
+      }else{
+        successFileList = fileListTemp.filter(item=>{
+          return item.uid!=info.file.uid;
+        });
+        createMessage.error(`${info.file.name} 上传失败.`);
       }
+      fileListTemp = successFileList;
     } else if (info.file.status === 'error') {
       createMessage.error(`${info.file.name} 上传失败.`);
     }
@@ -292,11 +321,13 @@
               fileSize: item.size,
             };
             newFileList.push(fileJson);
-          } else {
+          }else{
             return;
           }
         }
-        emitValue(newFileList);
+        //update-begin---author:liusq ---date:20230914  for:[issues/5327]Upload组件returnUrl为false时上传的字段值返回了一个'[object Object]' ------------
+        emitValue(JSON.stringify(newFileList));
+        //update-end---author:liusq ---date:20230914  for:[issues/5327]Upload组件returnUrl为false时上传的字段值返回了一个'[object Object]' ------------
       }
     }
   }

+ 8 - 3
src/components/Form/src/jeecg/components/modal/DeptSelectModal.vue

@@ -11,6 +11,7 @@
         @check="onCheck"
         :fieldNames="fieldNames"
         :checkedKeys="checkedKeys"
+        :multiple="multiple"
         :checkStrictly="getCheckStrictly"
       />
       <!--树操作部分-->
@@ -39,6 +40,7 @@
   import { BasicTree, TreeActionType } from '/@/components/Tree';
   import { useTreeBiz } from '/@/components/Form/src/jeecg/hooks/useTreeBiz';
   import {propTypes} from "/@/utils/propTypes";
+  import { omit } from 'lodash-es';
 
   export default defineComponent({
     name: 'DeptSelectModal',
@@ -64,12 +66,15 @@
       
       //update-begin-author:taoyan date:2022-10-28 for: 部门选择警告类型不匹配
       let propValue = props.value === ''?[]:props.value;
-      const getBindValue = Object.assign({}, unref(props), unref(attrs), {value: propValue});
-      //update-end-author:taoyan date:2022-10-28 for: 部门选择警告类型不匹配
+      //update-begin-author:liusq date:2023-05-26 for:  [issues/538]JSelectDept组件受 dynamicDisabled 影响
+      let temp = Object.assign({}, unref(props), unref(attrs), {value: propValue},{disabled: false});
+      const getBindValue = omit(temp, 'multiple');
+      //update-end-author:liusq date:2023-05-26 for:  [issues/538]JSelectDept组件受 dynamicDisabled 影响
+     //update-end-author:taoyan date:2022-10-28 for: 部门选择警告类型不匹配
       
       const queryUrl = getQueryUrl();
       const [{ visibleChange, checkedKeys, getCheckStrictly, getSelectTreeData, onCheck, onLoadData, treeData, checkALL, expandAll, onSelect }] =
-        useTreeBiz(treeRef, queryUrl, getBindValue);
+        useTreeBiz(treeRef, queryUrl, getBindValue, props);
       const searchInfo = ref(props.params);
       const tree = ref([]);
       //替换treeNode中key字段为treeData中对应的字段

+ 16 - 4
src/components/Form/src/jeecg/components/modal/PositionSelectModal.vue

@@ -78,7 +78,8 @@
         canResize: false,
         bordered: true,
         size: 'small',
-        rowKey: 'code',
+        //改成读取rowKey,自定义传递参数
+        rowKey: props.rowKey,
       };
       const getBindValue = Object.assign({}, unref(props), unref(attrs), config);
       const [{ rowSelection, visibleChange, indexColumnProps, getSelectResult, handleDeleteSelected, selectRows }] = useSelectBiz(
@@ -99,6 +100,16 @@
           xl: 10,
           xxl: 10,
         },
+        //update-begin-author:liusq date:2023-10-30 for: [issues/5514]组件页面显示错位
+        actionColOptions: {
+            xs: 24,
+            sm: 8,
+            md: 8,
+            lg: 8,
+            xl: 8,
+            xxl: 8,
+        },
+        //update-end-author:liusq date:2023-10-30 for: [issues/5514]组件页面显示错位
         schemas: [
           {
             label: '职务名称',
@@ -113,18 +124,18 @@
         {
           title: '职务编码',
           dataIndex: 'code',
-          width: 40,
+          width: 180,
           align: 'left',
         },
         {
           title: '职务名称',
           dataIndex: 'name',
-          width: 40,
+          // width: 180,
         },
         {
           title: '职务等级',
           dataIndex: 'postRank_dictText',
-          width: 40,
+          width: 180,
         },
       ];
       //已选择的table信息
@@ -176,6 +187,7 @@
         selectedTable,
         selectRows,
         handleDeleteSelected,
+        searchInfo,
       };
     },
   });

+ 13 - 3
src/components/Form/src/jeecg/components/modal/RoleSelectModal.vue

@@ -67,11 +67,21 @@
           xl: 14,
           xxl: 14,
         },
+        //update-begin-author:liusq date:2023-10-30 for: [issues/5514]组件页面显示错位
+        actionColOptions: {
+            xs: 24,
+            sm: 8,
+            md: 8,
+            lg: 8,
+            xl: 8,
+            xxl: 8,
+        },
+        //update-end-author:liusq date:2023-10-30 for: [issues/5514]组件页面显示错位
         schemas: [
           {
             label: '角色名称',
             field: 'roleName',
-            component: 'JInput',
+            component: 'Input',
           },
         ],
       };
@@ -80,13 +90,13 @@
         {
           title: '角色名称',
           dataIndex: 'roleName',
-          width: 40,
+          width: 240,
           align: 'left',
         },
         {
           title: '角色编码',
           dataIndex: 'roleCode',
-          width: 40,
+          // width: 40,
         },
       ];
 

+ 14 - 4
src/components/Form/src/jeecg/components/modal/UserSelectByDepModal.vue

@@ -76,22 +76,22 @@
           {
             title: '用户账号',
             dataIndex: 'username',
-            width: 50,
+            width: 180,
           },
           {
             title: '用户姓名',
             dataIndex: 'realname',
-            width: 50,
+            width: 180,
           },
           {
             title: '性别',
             dataIndex: 'sex_dictText',
-            width: 50,
+            width: 80,
           },
           {
             title: '手机号码',
             dataIndex: 'phone',
-            width: 50,
+            // width: 50,
           },
         ],
         useSearchForm: true,
@@ -110,6 +110,16 @@
             xl: 6,
             xxl: 10,
           },
+          //update-begin-author:liusq date:2023-10-30 for: [issues/5514]组件页面显示错位
+          actionColOptions: {
+              xs: 24,
+              sm: 12,
+              md: 12,
+              lg: 12,
+              xl: 8,
+              xxl: 8,
+          },
+          //update-end-author:liusq date:2023-10-30 for: [issues/5514]组件页面显示错位
           schemas: [
             {
               label: '账号',

+ 49 - 8
src/components/Form/src/jeecg/components/modal/UserSelectModal.vue

@@ -5,7 +5,7 @@
       v-bind="$attrs"
       @register="register"
       :title="modalTitle"
-      width="900px"
+      :width="showSelected ? '1200px' : '900px'"
       wrapClassName="j-user-select-modal"
       @ok="handleOk"
       destroyOnClose
@@ -24,6 +24,7 @@
             :searchInfo="searchInfo"
             :rowSelection="rowSelection"
             :indexColumnProps="indexColumnProps"
+            :afterFetch="afterFetch"
           >
             <!-- update-begin-author:taoyan date:2022-5-25 for: VUEN-1112一对多 用户选择 未显示选择条数,及清空 -->
             <template #tableTitle></template>
@@ -48,7 +49,7 @@
   </div>
 </template>
 <script lang="ts">
-  import { defineComponent, unref, ref } from 'vue';
+  import { defineComponent, unref, ref, watch } from 'vue';
   import { BasicModal, useModalInner } from '/@/components/Modal';
   import { getUserList } from '/@/api/common/api';
   import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
@@ -72,6 +73,13 @@
         type: String,
         default: '选择用户',
       },
+      //update-begin---author:wangshuai ---date:20230703  for:【QQYUN-5685】5、离职人员可以选自己------------
+      //排除用户id的集合
+      excludeUserIdList: {
+        type: Array,
+        default: [],
+      },
+      //update-end---author:wangshuai ---date:20230703  for:【QQYUN-5685】5、离职人员可以选自己------------
     },
     emits: ['register', 'getSelectResult'],
     setup(props, { emit, refs }) {
@@ -107,6 +115,15 @@
         getBindValue
       );
       const searchInfo = ref(props.params);
+      // update-begin--author:liaozhiyang---date:20230811---for:【issues/657】右侧选中列表删除无效
+      watch(rowSelection.selectedRowKeys, (newVal) => {
+        //update-begin---author:wangshuai ---date: 20230829  for:null指针异常导致控制台报错页面不显示------------
+        if(tableRef.value){
+          tableRef.value.setSelectedRowKeys(newVal);
+        }
+        //update-end---author:wangshuai ---date: 20230829 for:null指针异常导致控制台报错页面不显示------------
+      });
+      // update-end--author:liaozhiyang---date:20230811---for:【issues/657】右侧选中列表删除无效
       //查询form
       const formConfig = {
         baseColProps: {
@@ -145,33 +162,33 @@
         {
           title: '用户账号',
           dataIndex: 'username',
-          width: 40,
+          width: 120,
           align: 'left',
         },
         {
           title: '用户姓名',
           dataIndex: 'realname',
-          width: 40,
+          width: 120,
         },
         {
           title: '性别',
           dataIndex: 'sex_dictText',
-          width: 20,
+          width: 50,
         },
         {
           title: '手机号码',
           dataIndex: 'phone',
-          width: 30,
+          width: 120,
         },
         {
           title: '邮箱',
           dataIndex: 'email',
-          width: 40,
+          // width: 40,
         },
         {
           title: '状态',
           dataIndex: 'status_dictText',
-          width: 20,
+          width: 80,
         },
       ];
       //已选择的table信息
@@ -209,6 +226,29 @@
           closeModal();
         });
       }
+      
+      //update-begin---author:wangshuai ---date:20230703  for:【QQYUN-5685】5、离职人员可以选自己------------
+      /**
+       * 用户返回结果逻辑查询
+       */
+      function afterFetch(record) {
+        let excludeList = props.excludeUserIdList;
+        if(!excludeList){
+          return record;
+        }
+        let arr:any[] = [];
+        //如果存在过滤用户id集合,并且后台返回的数据不为空
+        if(excludeList.length>0 && record && record.length>0){
+          for(let item of record){
+            if(excludeList.indexOf(item.id)<0){
+              arr.push({...item})
+            }
+          }
+          return arr;
+        }
+        return record;
+      }
+      //update-end---author:wangshuai ---date:20230703  for:【QQYUN-5685】5、离职人员可以选自己------------
 
       return {
         //config,
@@ -227,6 +267,7 @@
         handleDeleteSelected,
         tableScroll,
         tableRef,
+        afterFetch,
       };
     },
   });

+ 282 - 0
src/components/Form/src/jeecg/components/positionSelect/PositionSelectModal.vue

@@ -0,0 +1,282 @@
+<template>
+    <BasicModal
+        @register="register"
+        :getContainer="getContainer"
+        :canFullscreen="false"
+        :title="title"
+        :width="500"
+        destroyOnClose
+        @ok="handleOk"
+        wrapClassName="j-user-select-modal2" >
+
+        <div style="position: relative; min-height: 350px">
+            <div style="width: 100%">
+                <a-input v-model:value="searchText" allowClear style="width: 100%" placeholder="搜索">
+                    <template #prefix>
+                        <SearchOutlined style="color: #c0c0c0" />
+                    </template>
+                </a-input>
+            </div>
+
+            <!-- tabs -->
+            <div class="modal-select-list-container">
+                <div class="scroll">
+                    <div class="content" style="right: -10px">
+                        
+                        <label class="item" v-for="item in showDataList" @click="(e)=>onSelect(e, item)">
+                            <a-checkbox v-model:checked="item.checked">
+                                <span>{{ item.name }}</span>
+                            </a-checkbox>
+                        </label>
+                    </div>
+                    
+                </div>
+            </div>
+
+            <!-- 选中用户 -->
+            <div class="selected-users" style="width: 100%; overflow-x: hidden">
+                <SelectedUserItem v-for="item in selectedList" :info="item" @unSelect="unSelect" />
+            </div>
+        </div>
+    </BasicModal>
+</template>
+
+<script lang="ts">
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { SearchOutlined, CloseOutlined } from '@ant-design/icons-vue';
+  import SelectedUserItem from '../userSelect/SelectedUserItem.vue';
+  import { defHttp } from '/@/utils/http/axios';
+  import { computed, ref, toRaw, watch } from 'vue';
+  
+  export default {
+    name: 'PositionSelectModal',
+    components: {
+      BasicModal,
+      SearchOutlined,
+      CloseOutlined,
+      SelectedUserItem,
+    },
+    props: {
+      multi: {
+        type: Boolean,
+        default: true,
+      },
+      getContainer: {
+        type: Function,
+        default: null,
+      },
+      title:{
+        type: String,
+        default: '',
+      },
+      type: {
+        type: String,
+        default: 'sys_position',
+      },
+      appId: {
+        type: String,
+        default: '',
+      }
+    },
+    emits: ['selected', 'register'],
+    setup(props, { emit }) {
+
+      const searchText = ref('');
+      const selectedIdList = computed(() => {
+        let arr = selectedList.value;
+        if (!arr || arr.length == 0) {
+          return [];
+        } else {
+          return arr.map((k) => k.id);
+        }
+      });
+
+      watch(()=>props.appId, async (val)=>{
+        if(val){
+          await loadDataList();
+        }
+      }, {immediate: true});
+      
+      
+      // 弹窗事件
+      const [register] = useModalInner(() => {
+        let list = dataList.value;
+        if(!list || list.length ==0 ){
+        }
+        for(let item of list){
+          item.checked = false
+        }
+      });
+
+      // 确定事件
+      function handleOk() {
+        let arr = toRaw(selectedIdList.value);
+        emit('selected', arr);
+      }
+      
+      const dataList = ref<any[]>([]);
+      const showDataList = computed(()=>{
+        let list = dataList.value;
+        if(!list || list.length ==0 ){
+          return []
+        }
+        let text = searchText.value;
+        if(!text){
+          return list
+        }
+        return list.filter(item=>item.name.indexOf(text)>=0)
+      });
+  
+      const selectedList = computed(()=>{
+        let list = dataList.value;
+        if(!list || list.length ==0 ){
+          return []
+        }
+        return list.filter(item=>item.checked)
+      });
+
+      function unSelect(id) {
+        let list = dataList.value;
+        if(!list || list.length ==0 ){
+          return;
+        }
+        let arr = list.filter(item=>item.id == id);
+        arr[0].checked = false;
+      }
+      
+      async function loadDataList() {
+        let params = {
+          pageNo: 1,
+          pageSize: 200,
+          column: 'createTime',
+          order: 'desc'
+        };
+        const url = '/sys/position/list'
+        const data = await defHttp.get({ url, params }, { isTransformResponse: false });
+        if (data.success) {
+          const { records } = data.result;
+          let arr:any[] = [];
+          if(records && records.length>0){
+            for(let item of records){
+              arr.push({
+                id: item.id,
+                name: item.name || item.roleName,
+                selectType: props.type,
+                checked: false
+              })
+            }
+          }
+          dataList.value = arr;
+        } else {
+          console.error(data.message);
+        }
+        console.log('loadDataList', data);
+      }
+
+
+      function onSelect(e, item) {
+        prevent(e);
+        console.log('onselect');
+        item.checked = !item.checked;
+      }
+
+      function prevent(e) {
+        e.preventDefault();
+        e.stopPropagation();
+      }
+
+      return {
+        register,
+        showDataList,
+        searchText,
+        handleOk,
+        selectedList,
+        selectedIdList,
+        unSelect,
+        onSelect
+      
+      };
+    },
+  };
+</script>
+<style scoped lang="less">
+    .modal-select-list-container{
+        height: 352px;
+        margin-top: 12px;
+        overflow: auto;
+        .scroll{
+            height: 100%;
+            position: relative;
+            width: 100%;
+            overflow: hidden;
+            .content{
+                bottom: 0;
+                left: 0;
+                overflow: scroll;
+                overflow-x: hidden;
+                position: absolute;
+                right: 0;
+                top: 0;
+                .item{
+                    padding: 7px 5px;
+                    cursor: pointer;
+                    display: block;
+                    &:hover{
+                        background-color: #f5f5f5;
+                    }
+                }
+               
+            }
+        }
+
+       
+    }
+</style>
+
+<style lang="less">
+    .j-user-select-modal2 {
+        .depart-select {
+            .ant-select-selector {
+                color: #fff !important;
+                background-color: #409eff !important;
+                border-radius: 5px !important;
+            }
+            .ant-select-selection-item,
+            .ant-select-arrow {
+                color: #fff !important;
+            }
+        }
+        .my-search {
+            position: absolute;
+            top: 14px;
+            z-index: 1;
+            &.all-width {
+                width: 100%;
+            }
+
+            .anticon {
+                cursor: pointer;
+                &:hover {
+                    color: #0a8fe9 !important;
+                }
+            }
+            .hidden {
+                display: none;
+            }
+        }
+
+        .my-tabs {
+        }
+
+        .selected-users {
+            display: flex;
+            flex-wrap: wrap;
+            flex-direction: row;
+            padding-top: 15px;
+        }
+
+        .scroll-container {
+            padding-bottom: 0 !important;
+        }
+    }
+</style>

+ 232 - 0
src/components/Form/src/jeecg/components/roleSelect/RoleSelectInput.vue

@@ -0,0 +1,232 @@
+<template>
+  <div>
+    <div @click="showModal" :class="disabled ? 'select-input disabled-select' : 'select-input'">
+      <template v-if="selectedList.length > 0">
+        <template v-for="(item, index) in selectedList">
+          <SelectedUserItem v-if="index < maxCount" :info="item" @unSelect="unSelect" query />
+        </template>
+      </template>
+      <span v-else style="height: 30px; line-height: 30px; display: inline-block; margin-left: 7px; color: #bfbfbf">请选择</span>
+      <div v-if="ellipsisInfo.status" class="user-selected-item">
+        <div class="user-select-ellipsis">
+          <span style="color: red">+{{ ellipsisInfo.count }}...</span>
+        </div>
+      </div>
+    </div>
+    <RoleSelectModal :appId="currentAppId" :multi="multi" :getContainer="getContainer" title="选择组织角色" @register="registerRoleModal" @selected="onSelected" />
+  </div>
+</template>
+
+<script lang="ts">
+  import { useModal } from '/@/components/Modal';
+  import { defHttp } from '/@/utils/http/axios';
+  import { computed, ref, watch, watchEffect, defineComponent } from 'vue';
+  import RoleSelectModal from './RoleSelectModal.vue';
+  import SelectedUserItem from '../userSelect/SelectedUserItem.vue';
+  import { Form } from 'ant-design-vue';
+  import { useUserStore } from '/@/store/modules/user';
+
+  const maxCount = 2;
+
+  export default defineComponent({
+    name: 'RoleSelectInput',
+    components: {
+      RoleSelectModal,
+      SelectedUserItem,
+    },
+    props: {
+      disabled: {
+        type: Boolean,
+        default: false,
+      },
+      store: {
+        type: String,
+        default: 'id',
+      },
+      value: {
+        type: String,
+        default: '',
+      },
+      multi: {
+        type: Boolean,
+        default: false,
+      },
+      getContainer: {
+        type: Function,
+        default: null,
+      },
+      appId: {
+        type: String,
+        default: '',
+      },
+    },
+    emits: ['update:value', 'change'],
+    setup(props, { emit }) {
+      const formItemContext = Form.useInjectFormItemContext();
+      const selectedList = ref<any[]>([]);
+      const loading = ref(true);
+
+      const [registerRoleModal, { openModal: openRoleModal, closeModal: closeRoleModal }] = useModal();
+      function showModal(e) {
+        e.preventDefault();
+        e.stopPropagation();
+        let list = selectedList.value.map((item) => item.id);
+        openRoleModal(true, {
+          list,
+        });
+      }
+
+      const ellipsisInfo = computed(() => {
+        let max = maxCount;
+        let len = selectedList.value.length;
+        if (len > max) {
+          return { status: true, count: len - max };
+        } else {
+          return { status: false };
+        }
+      });
+
+      function unSelect(id) {
+        console.log('unSelectUser', id);
+        loading.value = false;
+        let arr = selectedList.value;
+        let index = -1;
+        for (let i = 0; i < arr.length; i++) {
+          if (arr[i].id == id) {
+            index = i;
+            break;
+          }
+        }
+        if (index >= 0) {
+          arr.splice(index, 1);
+          selectedList.value = arr;
+          onSelectedChange();
+        }
+      }
+
+      function onSelectedChange() {
+        let temp: any[] = [];
+        let arr = selectedList.value;
+        if (arr && arr.length > 0) {
+          temp = arr.map((k) => {
+            return k[props.store];
+          });
+        }
+        let str = temp.join(',');
+        emit('update:value', str);
+        emit('change', str);
+        formItemContext.onFieldChange();
+        console.log('选中数据', str);
+      }
+
+      function onSelected(_v, values) {
+        console.log('角色选择完毕:', values);
+        loading.value = false;
+        if (values && values.length > 0) {
+          selectedList.value = values;
+        } else {
+          selectedList.value = [];
+        }
+        onSelectedChange();
+        closeRoleModal();
+      }
+
+      // 目前仅用于数据重新加载的一个状态
+      const currentAppId = ref('');
+      const userStore = useUserStore();
+      watchEffect(() => {
+        let tenantId = userStore.getTenant;
+        let appId = props.appId;
+        if (appId) {
+          currentAppId.value = appId;
+        } else {
+          currentAppId.value = new Date().getTime() + '-' + tenantId;
+        }
+      });
+
+      watch(
+        () => props.value,
+        async (val) => {
+          if (val) {
+            if (loading.value === true) {
+              await getRoleList(val);
+            }
+          } else {
+            selectedList.value = [];
+          }
+          loading.value = true;
+        },
+        { immediate: true }
+      );
+
+      /**
+       * 获取角色列表
+       * @param ids
+       */
+      async function getRoleList(ids) {
+        const url = '/sys/role/listByTenant';
+        let params = {
+          [props.store]: ids,
+          pageSize: 200
+        };
+        // 特殊条件处理(因为后台实体是roleCode,所以折中一下,不能直接改,会出问题)
+        if (props.store === 'code') {
+          params.roleCode = ids;
+        }
+        selectedList.value = [];
+        const data = await defHttp.get({ url, params }, { isTransformResponse: false });
+        console.log('getRoleList>>', data);
+        if (data.success) {
+          const { records } = data.result;
+          let arr: any[] = [];
+          if (records && records.length > 0) {
+            for (let item of records) {
+              arr.push({
+                id: item.id,
+                name: item.name || item.roleName,
+                code: item.roleCode,
+                checked: true,
+                selectType: 'sys_role',
+              });
+            }
+          }
+          selectedList.value = arr;
+        } else {
+          console.error(data.message);
+        }
+      }
+
+      return {
+        selectedList,
+        ellipsisInfo,
+        maxCount,
+        registerRoleModal,
+        closeRoleModal,
+        showModal,
+        onSelected,
+        unSelect,
+        currentAppId,
+      };
+    },
+  });
+</script>
+
+<style scoped lang="less">
+  .select-input {
+    padding: 0 5px;
+    background-color: #fff;
+    border: 1px solid #ccc;
+    border-radius: 3px;
+    box-sizing: border-box;
+    display: flex;
+    color: #9e9e9e;
+    font-size: 14px;
+    flex-wrap: nowrap;
+    min-height: 32px;
+    overflow-x: hidden;
+    &.disabled-select {
+      cursor: not-allowed;
+      background-color: #f5f5f5 !important;
+    }
+  }
+</style>

+ 296 - 0
src/components/Form/src/jeecg/components/roleSelect/RoleSelectModal.vue

@@ -0,0 +1,296 @@
+<template>
+    <BasicModal
+        @register="register"
+        :getContainer="getContainer"
+        :canFullscreen="false"
+        :title="title"
+        :width="500"
+        destroyOnClose
+        @ok="handleOk"
+        wrapClassName="j-user-select-modal2" >
+
+        <div style="position: relative; min-height: 350px">
+            <div style="width: 100%">
+                <a-input v-model:value="searchText" allowClear style="width: 100%" placeholder="搜索">
+                    <template #prefix>
+                        <SearchOutlined style="color: #c0c0c0" />
+                    </template>
+                </a-input>
+            </div>
+
+            <!-- tabs -->
+            <div class="modal-select-list-container">
+                <div class="scroll">
+                    <div class="content" style="right: -10px">
+                        
+                        <label class="item" v-for="item in showDataList" @click="(e)=>onSelect(e, item)">
+                            <a-checkbox v-model:checked="item.checked">
+                                <span class="text">{{ item.name }}</span>
+                            </a-checkbox>
+                        </label>
+                    </div>
+                    
+                </div>
+            </div>
+
+            <!-- 选中用户 -->
+            <div class="selected-users" style="width: 100%; overflow-x: hidden">
+                <SelectedUserItem v-for="item in selectedList" :info="item" @unSelect="unSelect" />
+            </div>
+        </div>
+    </BasicModal>
+</template>
+
+<script lang="ts">
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { SearchOutlined, CloseOutlined } from '@ant-design/icons-vue';
+  import SelectedUserItem from '../userSelect/SelectedUserItem.vue';
+  import { defHttp } from '/@/utils/http/axios';
+
+  import { computed, ref, toRaw, watch } from 'vue';
+  export default {
+    name: 'RoleSelectModal',
+    components: {
+      BasicModal,
+      SearchOutlined,
+      CloseOutlined,
+      SelectedUserItem,
+    },
+    props: {
+      multi: {
+        type: Boolean,
+        default: true,
+      },
+      getContainer: {
+        type: Function,
+        default: null,
+      },
+      title:{
+        type: String,
+        default: '',
+      },
+      type: {
+        type: String,
+        default: 'sys_role',
+      },
+      appId: {
+        type: String,
+        default: '',
+      }
+    },
+    emits: ['selected', 'register'],
+    setup(props, { emit }) {
+
+      const searchText = ref('');
+      const selectedIdList = computed(() => {
+        let arr = selectedList.value;
+        if (!arr || arr.length == 0) {
+          return [];
+        } else {
+          return arr.map((k) => k.id);
+        }
+      });
+
+      watch(()=>props.appId, async (val)=>{
+        if(val){
+          await loadDataList();
+        }
+      }, {immediate: true});
+      
+      
+      // 弹窗事件
+      const [register] = useModalInner((data) => {
+        let list = dataList.value;
+        if(!list || list.length ==0 ){
+        }else{
+          let selectedIdList = data.list || [];
+          for(let item of list){
+            if(selectedIdList.indexOf(item.id)>=0){
+              item.checked = true;
+            }else{
+              item.checked = false;
+            }
+          }
+        }
+      });
+
+      // 确定事件
+      function handleOk() {
+        let arr = toRaw(selectedIdList.value);
+        emit('selected', arr, toRaw(selectedList.value));
+      }
+      
+      const dataList = ref<any[]>([]);
+      const showDataList = computed(()=>{
+        let list = dataList.value;
+        if(!list || list.length ==0 ){
+          return []
+        }
+        let text = searchText.value;
+        if(!text){
+          return list
+        }
+        return list.filter(item=>item.name.indexOf(text)>=0)
+      });
+  
+      const selectedList = computed(()=>{
+        let list = dataList.value;
+        if(!list || list.length ==0 ){
+          return []
+        }
+        return list.filter(item=>item.checked)
+      });
+
+      function unSelect(id) {
+        let list = dataList.value;
+        if(!list || list.length ==0 ){
+          return;
+        }
+        let arr = list.filter(item=>item.id == id);
+        arr[0].checked = false;
+      }
+      
+      async function loadDataList() {
+        let params = {
+          pageNo: 1,
+          pageSize: 200,
+          column: 'createTime',
+          order: 'desc'
+        };
+        const url = '/sys/role/listByTenant';
+        const data = await defHttp.get({ url, params }, { isTransformResponse: false });
+        if (data.success) {
+          const { records } = data.result;
+          let arr:any[] = [];
+          if(records && records.length>0){
+            for(let item of records){
+              arr.push({
+                id: item.id,
+                name: item.name || item.roleName,
+                code: item.roleCode,
+                selectType: props.type,
+                checked: false
+              })
+            }
+          }
+          dataList.value = arr;
+        } else {
+          console.error(data.message);
+        }
+        console.log('loadDataList', data);
+      }
+
+
+      function onSelect(e, item) {
+        prevent(e);
+        console.log('onselect');
+        // 单选判断 只能选中一条数据 其余数据置false
+        if(props.multi === false){
+          let list = dataList.value;
+          for(let item of list){
+            item.checked = false;
+          }
+        }
+        item.checked = !item.checked;
+      }
+
+      function prevent(e) {
+        e.preventDefault();
+        e.stopPropagation();
+      }
+
+      return {
+        register,
+        showDataList,
+        searchText,
+        handleOk,
+        selectedList,
+        selectedIdList,
+        unSelect,
+        onSelect
+      
+      };
+    },
+  };
+</script>
+<style scoped lang="less">
+    .modal-select-list-container{
+        height: 352px;
+        margin-top: 12px;
+        overflow: auto;
+        .scroll{
+            height: 100%;
+            position: relative;
+            width: 100%;
+            overflow: hidden;
+            .content{
+                bottom: 0;
+                left: 0;
+                overflow: scroll;
+                overflow-x: hidden;
+                position: absolute;
+                right: 0;
+                top: 0;
+                .item{
+                    padding: 7px 5px;
+                    cursor: pointer;
+                    display: block;
+                    &:hover{
+                        background-color: #f5f5f5;
+                    }
+                }
+               
+            }
+        }
+
+       
+    }
+</style>
+
+<style lang="less">
+    .j-user-select-modal2 {
+        .depart-select {
+            .ant-select-selector {
+                color: #fff !important;
+                background-color: #409eff !important;
+                border-radius: 5px !important;
+            }
+            .ant-select-selection-item,
+            .ant-select-arrow {
+                color: #fff !important;
+            }
+        }
+        .my-search {
+            position: absolute;
+            top: 14px;
+            z-index: 1;
+            &.all-width {
+                width: 100%;
+            }
+
+            .anticon {
+                cursor: pointer;
+                &:hover {
+                    color: #0a8fe9 !important;
+                }
+            }
+            .hidden {
+                display: none;
+            }
+        }
+
+        .my-tabs {
+        }
+
+        .selected-users {
+            display: flex;
+            flex-wrap: wrap;
+            flex-direction: row;
+            padding-top: 15px;
+        }
+
+        .scroll-container {
+            padding-bottom: 0 !important;
+        }
+    }
+</style>

+ 150 - 0
src/components/Form/src/jeecg/components/userSelect/SelectedUserItem.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="user-selected-item">
+    <div
+      style="
+        display: flex;
+        flex-direction: row;
+        height: 24px;
+        border-radius: 12px;
+        padding-right: 10px;
+        vertical-align: middle;
+        background-color: #f5f5f5;
+      "
+    >
+      <span style="width: 24px; height: 24px; line-height: 20px; margin-right: 3px; display: inline-block">
+        <a-avatar v-if="info.avatar" :src="getFileAccessHttpUrl(info.avatar)" :size="24"></a-avatar>
+        
+        <a-avatar v-else-if="info.avatarIcon" class="ant-btn-primary" :size="24" >
+          <template #icon>
+            <Icon :icon=" 'ant-design:'+info.avatarIcon " style="font-size: 16px;margin-top: 4px"/>
+          </template>
+        </a-avatar>
+        
+        <a-avatar v-else-if="info.selectType == 'sys_role'" :size="24" style="background-color: rgb(255, 173, 0);">
+          <template #icon>
+            <team-outlined style="font-size: 16px"/>
+          </template>
+        </a-avatar>
+        <a-avatar v-else-if="info.selectType == 'sys_position'" :size="24" style="background-color: rgb(245, 34, 45);">
+          <template #icon>
+            <TagsOutlined style="font-size: 16px"/>
+          </template>
+        </a-avatar>
+        
+        <a-avatar :size="24" v-else>
+          <template #icon><UserOutlined /></template>
+        </a-avatar>
+      </span>
+
+      <div style="height: 24px; line-height: 24px" class="ellipsis">
+        {{ info.realname || info.name }}
+      </div>
+
+      <div v-if="showClose" class="icon-close">
+        <CloseOutlined @click="removeSelect"/>
+      </div>
+    </div>
+
+    <div v-if="!showClose" class="icon-remove">
+      <MinusCircleFilled @click="removeSelect" />
+    </div>
+  </div>
+</template>
+
+<script>
+  import { UserOutlined, CloseOutlined, MinusCircleFilled, TagsOutlined, TeamOutlined } from '@ant-design/icons-vue';
+  import {computed} from 'vue'
+  import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
+  
+  export default {
+    name: 'SelectedUserItem',
+    components: {
+      UserOutlined,
+      MinusCircleFilled,
+      CloseOutlined,
+      TagsOutlined,
+      TeamOutlined
+    },
+    props: {
+      info: {
+        type: Object,
+        default: () => {},
+      },
+      // 是否作为查询条件
+      query:{
+        type: Boolean,
+        default: false,
+      }
+    },
+    emits: ['unSelect'],
+    setup(props, { emit }) {
+      function removeSelect(e) {
+        e.preventDefault();
+        e.stopPropagation();
+        emit('unSelect', props.info.id);
+      }
+
+      const showClose = computed(()=>{
+        if(props.query===true){
+          return true;
+        }else{
+          return false;
+        }
+      });
+      
+      return {
+        showClose,
+        removeSelect,
+        getFileAccessHttpUrl
+      };
+    },
+  };
+</script>
+
+<style lang="less">
+  .user-selected-item {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    margin-right: 8px;
+    height: 30px;
+    border-radius: 12px;
+    line-height: 30px;
+    vertical-align: middle;
+
+    .ellipsis {
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+    .icon-remove {
+      position: absolute;
+      top: -10px;
+      right: -4px;
+      font-size: 18px;
+      width: 15px;
+      height: 15px;
+      cursor: pointer;
+      display: none;
+    }
+    
+    .icon-close{
+      height: 22px;
+      line-height: 24px;
+      font-size: 10px;
+      font-weight: bold;
+      margin-left: 7px;
+      &:hover{
+        color: #0a8fe9;
+      }
+    }
+
+    &:hover {
+      .icon-remove {
+        display: block;
+      }
+    }
+  }
+</style>

+ 187 - 0
src/components/Form/src/jeecg/components/userSelect/UserList.vue

@@ -0,0 +1,187 @@
+<template>
+  <a-list item-layout="horizontal" :data-source="showDataList">
+    <template #renderItem="{ item }">
+      <a-list-item style="padding: 3px 0">
+        <div class="user-select-user-info" @click="(e) => onClickUser(e, item)">
+          <div>
+            <a-checkbox v-model:checked="checkStatus[item.id]" />
+          </div>
+          <div>
+            <a-avatar v-if="item.avatar" :src="getFileAccessHttpUrl(item.avatar)"></a-avatar>
+            <a-avatar v-else-if="item.avatarIcon" class="ant-btn-primary">
+              <template #icon>
+                <Icon :icon=" 'ant-design:'+item.avatarIcon " style="margin-top: 4px;font-size: 24px;"/>
+              </template>
+            </a-avatar>
+            <a-avatar v-else>
+              <template #icon><UserOutlined /></template>
+            </a-avatar>
+          </div>
+          <div :style="nameStyle">
+            {{ item.realname }}
+          </div>
+          <div :style="departStyle" class="ellipsis" :title="item.orgCodeTxt">
+            {{ item.orgCodeTxt }}
+          </div>
+          <div style="width: 1px"></div>
+        </div>
+      </a-list-item>
+    </template>
+  </a-list>
+</template>
+
+<script lang="ts">
+  import { UserOutlined } from '@ant-design/icons-vue';
+  import { computed, toRaw, reactive, watchEffect, ref } from 'vue';
+  import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
+  
+  export default {
+    name: 'UserList',
+    props: {
+      dataList: {
+        type: Array,
+        default: () => [],
+      },
+      // 是否显示部门文本
+      depart: {
+        type: Boolean,
+        default: false,
+      },
+      selectedIdList: {
+        type: Array,
+        default: () => [],
+      },
+      excludeUserIdList:{
+        type: Array,
+        default: () => [],
+      }
+    },
+    components: {
+      UserOutlined,
+    },
+    emits: ['selected', 'unSelect'],
+    setup(props, { emit }) {
+      function onClickUser(e, user) {
+        e && prevent(e);
+        let status = checkStatus[user.id];
+        if (status === true) {
+          emit('unSelect', user.id);
+        } else {
+          emit('selected', toRaw(user));
+        }
+      }
+
+      function getTwoText(text) {
+        if (!text) {
+          return '';
+        } else {
+          return text.substr(0, 2);
+        }
+      }
+
+      const departStyle = computed(() => {
+        if (props.depart === true) {
+          // 如果显示部门信息
+          return {
+            flex: 1,
+          };
+        } else {
+          return {
+            display: 'none',
+          };
+        }
+      });
+
+      const nameStyle = computed(() => {
+        if (props.depart === true) {
+          // 如果显示部门信息
+          return {
+            width: '200px',
+          };
+        } else {
+          return {
+            flex: 1,
+          };
+        }
+      });
+
+      function onChangeChecked(e) {
+        console.error('onChangeChecked', e);
+      }
+
+     // const showDataList = ref<any[]>([])
+      const checkStatus = reactive<any>({});
+      watchEffect(() => {
+        let arr1 = props.dataList;
+        if (!arr1 || arr1.length === 0) {
+          return;
+        }
+        let idList = props.selectedIdList;
+        for (let item of arr1) {
+          if (idList.indexOf(item.id) >= 0) {
+            checkStatus[item.id] = true;
+          } else {
+            checkStatus[item.id] = false;
+          }
+        }
+        
+      
+      });
+
+      function prevent(e) {
+        e.preventDefault();
+        e.stopPropagation();
+      }
+
+      function records2DataList() {
+        let arr:any[] = [];
+        let excludeList = props.excludeUserIdList;
+        let records = props.dataList;
+        if(records && records.length>0){
+          for(let item of records){
+            if(excludeList.indexOf(item.id)<0){
+              arr.push({...item})
+            }
+          }
+        }
+        return arr;
+      }
+      
+      const showDataList = computed(()=>{
+        let excludeList = props.excludeUserIdList;
+        if(excludeList && excludeList.length>0){
+          return records2DataList();
+        }
+        return props.dataList;
+      });
+
+      return {
+        onClickUser,
+        getTwoText,
+        departStyle,
+        nameStyle,
+        onChangeChecked,
+        checkStatus,
+        showDataList,
+        getFileAccessHttpUrl
+      };
+    },
+  };
+</script>
+
+<style lang="less">
+  .user-select-user-info {
+    display: flex;
+    width: 100%;
+    > div {
+      height: 36px;
+      line-height: 36px;
+      margin-right: 10px;
+    }
+    .ellipsis {
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      overflow: hidden;
+    }
+  }
+</style>

+ 186 - 0
src/components/Form/src/jeecg/components/userSelect/UserListAndDepart.vue

@@ -0,0 +1,186 @@
+<template>
+  <a-row>
+    <a-col :span="12">
+      <div :style="containerStyle">
+        <a-tree
+          v-if="treeData.length > 0"
+          :load-data="loadChildren"
+          showIcon
+          autoExpandParent
+          :treeData="treeData"
+          :selectedKeys="selectedKeys"
+          v-model:expandedKeys="expandedKeys"
+          @select="onSelect"
+        >
+          <template #title="{ title, key }">
+            <FolderFilled style="color: #9e9e9e"/><span style="margin-left: 5px">{{ title }}</span>
+          </template>
+        </a-tree>
+      </div>
+    </a-col>
+    <a-col :span="12" style="padding-left: 10px">
+      <div :style="containerStyle">
+        <user-list :excludeUserIdList="excludeUserIdList" :dataList="userDataList" :selectedIdList="selectedIdList" @selected="onSelectUser" @unSelect="unSelectUser" />
+      </div>
+    </a-col>
+  </a-row>
+</template>
+
+<script lang="ts">
+  import { defHttp } from '/@/utils/http/axios';
+  import { computed, ref, watch } from 'vue';
+  import UserList from './UserList.vue';
+  import { FolderFilled } from '@ant-design/icons-vue';
+
+  export default {
+    name: 'DepartUserList',
+    components: {
+      UserList,
+      FolderFilled
+    },
+    props: {
+      searchText: {
+        type: String,
+        default: '',
+      },
+      selectedIdList: {
+        type: Array,
+        default: () => [],
+      },
+      excludeUserIdList:{
+        type: Array,
+        default: () => [],
+      }
+    },
+    emits: ['loaded', 'selected', 'unSelect'],
+    setup(props, { emit }) {
+      //export const queryById = (params) => defHttp.get({ url: Api.queryById, params }, { isTransformResponse: false });
+
+      async function loadDepartTree(pid?) {
+        const url = '/sys/sysDepart/queryDepartTreeSync';
+        let params = {};
+        if (pid) {
+          params['pid'] = pid;
+        }
+        const data = await defHttp.get({ url, params }, { isTransformResponse: false });
+        console.log('loadDepartTree', data);
+        return data;
+      }
+
+      async function initRoot() {
+        console.log('initRoot');
+        const data = await loadDepartTree();
+        if (data.success) {
+          let arr = data.result;
+          treeData.value = arr;
+          emitDepartOptions(arr);
+        } else {
+          console.error(data.message);
+        }
+        clear();
+      }
+
+      function emitDepartOptions(arr) {
+        let options = [];
+        if (arr && arr.length > 0) {
+          options = arr.map((k) => {
+            return {
+              value: k.id,
+              label: k.departName,
+            };
+          });
+        }
+        emit('loaded', options);
+      }
+
+      initRoot();
+
+      const treeData = ref<any[]>([]);
+      const selectedKeys = ref<string[]>([]);
+      const expandedKeys = ref<string[]>([]);
+      const selectedDepartId = ref('');
+      function onSelect(ids, e) {
+        let record = e.node.dataRef;
+        selectedKeys.value = [record.key];
+
+        let id = ids[0];
+        selectedDepartId.value = id;
+        loadUserList();
+      }
+
+      function clear() {
+        selectedDepartId.value = '';
+      }
+      async function loadChildren(treeNode) {
+        console.log('loadChildren', treeNode);
+        const data = await loadDepartTree(treeNode.eventKey);
+        if (data.success) {
+          let arr = data.result;
+          treeNode.dataRef.children = [...arr];
+          treeData.value = [...treeData.value];
+        } else {
+          console.error(data.message);
+        }
+      }
+
+      const maxHeight = ref(300);
+      maxHeight.value = window.innerHeight - 300;
+      const containerStyle = computed(() => {
+        return {
+          'overflow-y': 'auto',
+          'max-height': maxHeight.value + 'px',
+        };
+      });
+
+      const userDataList = ref<any[]>([]);
+      async function loadUserList() {
+        const url = '/sys/user/selectUserList';
+        let params = {
+          pageNo: 1,
+          pageSize: 10,
+        };
+        if (props.searchText) {
+          params['keyword'] = props.searchText;
+        }
+        if (selectedDepartId.value) {
+          params['departId'] = selectedDepartId.value;
+        }
+        const data = await defHttp.get({ url, params }, { isTransformResponse: false });
+        if (data.success) {
+          const { records } = data.result;
+          userDataList.value = records;
+        } else {
+          console.error(data.message);
+        }
+        console.log('depart-loadUserList', data);
+      }
+      watch(
+        () => props.searchText,
+        () => {
+          loadUserList();
+        }
+      );
+
+      function onSelectUser(info) {
+        emit('selected', info);
+      }
+      function unSelectUser(id) {
+        emit('unSelect', id);
+      }
+
+      return {
+        containerStyle,
+        treeData,
+        selectedKeys,
+        expandedKeys,
+        onSelect,
+        loadChildren,
+        onSelectUser,
+        unSelectUser,
+        userDataList,
+      };
+    },
+  };
+</script>
+
+<style scoped></style>

+ 142 - 0
src/components/Form/src/jeecg/components/userSelect/UserListAndRole.vue

@@ -0,0 +1,142 @@
+<template>
+  <a-row>
+    <a-col :span="12">
+      <div :style="containerStyle">
+        <a-tree v-if="treeData.length > 0" showIcon :treeData="treeData" :selectedKeys="selectedKeys" @select="onSelect">
+          <template #title="{ title, key }">
+            <UserOutlined style="color: #9e9e9e"/><span style="margin-left: 5px">{{ title }}</span>
+          </template>
+        </a-tree>
+      </div>
+    </a-col>
+    <a-col :span="12" style="padding-left: 10px">
+      <div :style="containerStyle">
+        <user-list :excludeUserIdList="excludeUserIdList" :dataList="userDataList" :selectedIdList="selectedIdList" @selected="onSelectUser" @unSelect="unSelectUser" />
+      </div>
+    </a-col>
+  </a-row>
+</template>
+
+<script lang="ts">
+  import { computed, ref, watch } from 'vue';
+  import { defHttp } from '/@/utils/http/axios';
+  import UserList from './UserList.vue';
+  import { UserOutlined } from '@ant-design/icons-vue';
+
+  export default {
+    name: 'RoleUserList',
+    components: {
+      UserList,
+      UserOutlined
+    },
+    props: {
+      searchText: {
+        type: String,
+        default: '',
+      },
+      selectedIdList: {
+        type: Array,
+        default: () => [],
+      },
+      excludeUserIdList:{
+        type: Array,
+        default: () => [],
+      }
+    },
+    emits: ['selected', 'unSelect'],
+    setup(props, { emit }) {
+      const treeData = ref<any[]>([]);
+      async function loadRoleList() {
+        const url = '/sys/role/listByTenant';
+        let params = {
+          order: 'desc',
+          column: 'createTime',
+          pageSize: 200,
+        };
+        let arr = [];
+        const data = await defHttp.get({ url, params }, { isTransformResponse: false });
+        if (data.success) {
+          const { records } = data.result;
+          arr = records.map((k) => {
+            return {
+              title: k.roleName,
+              id: k.id,
+              key: k.id,
+            };
+          });
+        }
+        console.log('loadRoleList', data);
+        treeData.value = arr;
+      }
+      loadRoleList();
+
+      const selectedKeys = ref<any[]>([]);
+      const selectedRoleId = ref('');
+      function onSelect(ids, e) {
+        let record = e.node.dataRef;
+        selectedKeys.value = [record.key];
+
+        let id = ids[0];
+        selectedRoleId.value = id;
+        loadUserList();
+      }
+
+      const userDataList = ref<any[]>([]);
+      async function loadUserList() {
+        const url = '/sys/user/selectUserList';
+        let params = {
+          pageNo: 1,
+          pageSize: 10,
+        };
+        if (props.searchText) {
+          params['keyword'] = props.searchText;
+        }
+        if (selectedRoleId.value) {
+          params['roleId'] = selectedRoleId.value;
+        }
+        const data = await defHttp.get({ url, params }, { isTransformResponse: false });
+        if (data.success) {
+          const { records } = data.result;
+          userDataList.value = records;
+        } else {
+          console.error(data.message);
+        }
+        console.log('role-loadUserList', data);
+      }
+      watch(
+        () => props.searchText,
+        () => {
+          loadUserList();
+        }
+      );
+
+      function onSelectUser(info) {
+        emit('selected', info);
+      }
+      function unSelectUser(id) {
+        emit('unSelect', id);
+      }
+
+      const maxHeight = ref(300);
+      maxHeight.value = window.innerHeight - 300;
+      const containerStyle = computed(() => {
+        return {
+          'overflow-y': 'auto',
+          'max-height': maxHeight.value + 'px',
+        };
+      });
+
+      return {
+        containerStyle,
+        treeData,
+        selectedKeys,
+        onSelect,
+        onSelectUser,
+        unSelectUser,
+        userDataList,
+      };
+    },
+  };
+</script>
+
+<style scoped></style>

+ 368 - 0
src/components/Form/src/jeecg/components/userSelect/UserSelectModal.vue

@@ -0,0 +1,368 @@
+<template>
+  <BasicModal
+    @register="register"
+    :getContainer="getContainer"
+    :canFullscreen="false"
+    title="选择用户"
+    :width="600"
+    wrapClassName="j-user-select-modal2"
+  >
+    <!-- 部门下拉框 -->
+    <a-select v-model:value="selectedDepart" style="width: 100%" class="depart-select" @change="onDepartChange">
+      <a-select-option v-for="item in departOptions" :value="item.value">{{ item.label }}</a-select-option>
+    </a-select>
+
+    <div style="position: relative; min-height: 350px">
+      <!-- 用户搜索框 -->
+      <div :class="searchInputStatus ? 'my-search all-width' : 'my-search'">
+        <span :class="searchInputStatus ? 'hidden' : ''" style="margin-left: 10px"
+          ><SearchOutlined style="color: #c0c0c0" @click="showSearchInput"
+        /></span>
+        <div style="width: 100%" :class="searchInputStatus ? '' : 'hidden'">
+          <a-input v-model:value="searchText" @pressEnter="onSearchUser" style="width: 100%" placeholder="请输入用户名按回车搜索">
+            <template #prefix>
+              <SearchOutlined style="color: #c0c0c0" />
+            </template>
+            <template #suffix>
+              <CloseOutlined title="退出搜索" @click="clearSearch" />
+            </template>
+          </a-input>
+        </div>
+      </div>
+
+      <!-- tabs -->
+      <div class="my-tabs">
+        <a-tabs v-model:activeKey="myActiveKey" :centered="true" @change="onChangeTab">
+          <!-- 所有用户 -->
+          <a-tab-pane key="1" tab="全部" forceRender>
+            <user-list :excludeUserIdList="excludeUserIdList" :dataList="userDataList" :selectedIdList="selectedIdList" depart @selected="onSelectUser" @unSelect="unSelectUser" />
+          </a-tab-pane>
+
+          <!-- 部门用户 -->
+          <a-tab-pane key="2" tab="按部门" forceRender>
+            <depart-user-list
+              :searchText="searchText"
+              :selectedIdList="selectedIdList"
+              :excludeUserIdList="excludeUserIdList"
+              @loaded="initDepartOptions"
+              @selected="onSelectUser"
+              @unSelect="unSelectUser"
+            />
+          </a-tab-pane>
+
+          <!-- 角色用户 -->
+          <a-tab-pane key="3" tab="按角色" forceRender>
+            <role-user-list :excludeUserIdList="excludeUserIdList" :searchText="searchText" :selectedIdList="selectedIdList" @selected="onSelectUser" @unSelect="unSelectUser" />
+          </a-tab-pane>
+        </a-tabs>
+      </div>
+
+      <!-- 选中用户 -->
+      <div class="selected-users" style="width: 100%; overflow-x: hidden">
+        <SelectedUserItem v-for="item in selectedUserList" :info="item" @unSelect="unSelectUser" />
+      </div>
+    </div>
+
+    <template #footer>
+      <div style="display: flex; justify-content: space-between; width: 100%">
+        <div class="select-user-page-info">
+          <a-pagination
+            v-if="myActiveKey == '1'"
+            v-model:current="pageNo"
+            size="small"
+            :total="totalRecord"
+            show-quick-jumper
+            @change="onPageChange"
+          />
+        </div>
+        <a-button type="primary" @click="handleOk">确 定</a-button>
+      </div>
+    </template>
+  </BasicModal>
+</template>
+
+<script lang="ts">
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { SearchOutlined, CloseOutlined } from '@ant-design/icons-vue';
+  import UserList from './UserList.vue';
+  import SelectedUserItem from './SelectedUserItem.vue';
+  import DepartUserList from './UserListAndDepart.vue';
+  import RoleUserList from './UserListAndRole.vue';
+  import { Pagination } from 'ant-design-vue';
+  const APagination = Pagination;
+  import { defHttp } from '/@/utils/http/axios';
+
+  import {computed, ref, toRaw, unref} from 'vue';
+  import { useUserStore } from '/@/store/modules/user';
+  import { mySelfData } from './useUserSelect'
+  
+  export default {
+    name: 'UserSelectModal',
+    components: {
+      BasicModal,
+      SearchOutlined,
+      CloseOutlined,
+      SelectedUserItem,
+      UserList,
+      DepartUserList,
+      RoleUserList,
+      APagination,
+    },
+    props: {
+      multi: {
+        type: Boolean,
+        default: false,
+      },
+      getContainer: {
+        type: Function,
+        default: null,
+      },
+      //是否排除我自己
+      izExcludeMy: {
+        type: Boolean,
+        default: false,
+      },
+      //是否在高级查询中作为条件 可以选择当前用户表达式
+      inSuperQuery:{
+        type: Boolean,
+        default: false,
+      }
+    },
+    emits: ['selected', 'register'],
+    setup(props, { emit }) {
+      const myActiveKey = ref('1');
+      const selectedUserList = ref<any[]>([]);
+      const userStore = useUserStore();
+      const selectedIdList = computed(() => {
+        let arr = selectedUserList.value;
+        if (!arr || arr.length == 0) {
+          return [];
+        } else {
+          return arr.map((k) => k.id);
+        }
+      });
+      // QQYUN-4152【应用】已经存在的用户,添加的时候还可以重复选择
+      const excludeUserIdList = ref<any[]>([]);
+
+      // 弹窗事件
+      const [register] = useModalInner((data) => {
+        let list = data.list;
+        if (list && list.length > 0) {
+          selectedUserList.value = [...list];
+        } else {
+          selectedUserList.value = [];
+        }
+        if(data.excludeUserIdList){
+          excludeUserIdList.value = data.excludeUserIdList;
+        }else{
+          excludeUserIdList.value = [];
+        }
+        //如果排除我自己,直接excludeUserIdList.push排除即可
+        if (props.izExcludeMy) {
+          excludeUserIdList.value.push(userStore.getUserInfo.id);
+        }
+        //加载用户列表
+        loadUserList();
+      });
+
+      // 确定事件
+      function handleOk() {
+        let arr = toRaw(selectedUserList.value);
+        emit('selected', arr);
+      }
+
+      /*--------------部门下拉框,用于筛选用户---------------*/
+      const selectedDepart = ref('');
+      const departOptions = ref<any[]>([]);
+      function initDepartOptions(options) {
+        departOptions.value = [{ value: '', label: '全部用户' }, ...options];
+        selectedDepart.value = '';
+      }
+      function onDepartChange() {
+        loadUserList();
+      }
+      /*--------------部门下拉框,用于筛选用户---------------*/
+
+      /*--------------第一页 搜索框---------------*/
+      const searchInputStatus = ref(false);
+      const searchText = ref('');
+      function showSearchInput(e) {
+        e && prevent(e);
+        searchInputStatus.value = true;
+      }
+      function onSearchUser() {
+        console.log('onSearchUser');
+        loadUserList();
+      }
+      function clearSearch(e) {
+        e && prevent(e);
+        searchText.value = '';
+        searchInputStatus.value = false;
+        loadUserList();
+      }
+      /*--------------第一页 搜索框---------------*/
+
+      /*--------------加载数据---------------*/
+      const pageNo = ref(1);
+      const totalRecord = ref(0);
+      const userDataList = ref<any[]>([]);
+      async function onPageChange() {
+        console.log('onPageChange', pageNo.value);
+        await loadUserList();
+      }
+      async function loadUserList() {
+        const url = '/sys/user/selectUserList';
+        let params = {
+          pageNo: pageNo.value,
+          pageSize: 10,
+        };
+        if (searchText.value) {
+          params['keyword'] = searchText.value;
+        }
+        if (selectedDepart.value) {
+          params['departId'] = selectedDepart.value;
+        }
+        const data = await defHttp.get({ url, params }, { isTransformResponse: false });
+        if (data.success) {
+          let { records, total } = data.result;
+          //如果排除的用户id的长度不为0,那么需要改变页数
+          if(unref(excludeUserIdList) && unref(excludeUserIdList).length>0){
+             total = total - unref(excludeUserIdList).length;
+          }
+          totalRecord.value = total;
+          initCurrentUserData(records);
+          userDataList.value = records;
+        } else {
+          console.error(data.message);
+        }
+        console.log('loadUserList', data);
+      }
+      
+      // 往用户列表中添加一个 当前用户选项
+      function initCurrentUserData(records) {
+        if(pageNo.value==1 && props.inSuperQuery === true){
+          records.unshift({...mySelfData})
+        }
+      }
+      /*--------------加载数据---------------*/
+
+      /*--------------选中/取消选中---------------*/
+      function onSelectUser(info) {
+        if (props.multi === true) {
+          let arr = selectedUserList.value;
+          let idList = selectedIdList.value;
+          if (idList.indexOf(info.id) < 0) {
+            arr.push({ ...info });
+            selectedUserList.value = arr;
+          }
+        } else {
+          selectedUserList.value = [{ ...info }];
+        }
+      }
+      function unSelectUser(id) {
+        let arr = selectedUserList.value;
+        let index = -1;
+        for (let i = 0; i < arr.length; i++) {
+          if (arr[i].id === id) {
+            index = i;
+            break;
+          }
+        }
+        if (index >= 0) {
+          arr.splice(index, 1);
+          selectedUserList.value = arr;
+        }
+      }
+      /*--------------选中/取消选中---------------*/
+
+      function onChangeTab(tab) {
+        myActiveKey.value = tab;
+      }
+
+      function prevent(e) {
+        e.preventDefault();
+        e.stopPropagation();
+      }
+
+      //加载第一页数据
+      loadUserList();
+
+      return {
+        selectedDepart,
+        departOptions,
+        initDepartOptions,
+        onDepartChange,
+
+        register,
+        handleOk,
+
+        searchText,
+        searchInputStatus,
+        showSearchInput,
+        onSearchUser,
+        clearSearch,
+
+        myActiveKey,
+        onChangeTab,
+
+        pageNo,
+        totalRecord,
+        onPageChange,
+        userDataList,
+        selectedUserList,
+        selectedIdList,
+        onSelectUser,
+        unSelectUser,
+        excludeUserIdList
+      };
+    },
+  };
+</script>
+
+<style lang="less">
+  .j-user-select-modal2 {
+    .depart-select {
+      .ant-select-selector {
+        color: #fff !important;
+        background-color: #409eff !important;
+        border-radius: 5px !important;
+      }
+      .ant-select-selection-item,
+      .ant-select-arrow {
+        color: #fff !important;
+      }
+    }
+    .my-search {
+      position: absolute;
+      top: 14px;
+      z-index: 1;
+      &.all-width {
+        width: 100%;
+      }
+
+      .anticon {
+        cursor: pointer;
+        &:hover {
+          color: #0a8fe9 !important;
+        }
+      }
+      .hidden {
+        display: none;
+      }
+    }
+
+    .my-tabs {
+    }
+
+    .selected-users {
+      display: flex;
+      flex-wrap: wrap;
+      flex-direction: row;
+      padding-top: 15px;
+    }
+
+    .scroll-container {
+      padding-bottom: 0 !important;
+    }
+  }
+</style>

+ 273 - 0
src/components/Form/src/jeecg/components/userSelect/index.vue

@@ -0,0 +1,273 @@
+<template>
+  <div>
+    <div v-if="isSearchFormComp" @click="click2Add" :class="disabled?'disabled-user-select':''" style="padding:0 5px;background-color: #fff;border: 1px solid #ccc;border-radius: 3px;box-sizing: border-box;display:flex;color: #9e9e9e;font-size: 14px;flex-wrap: wrap;min-height: 32px;">
+      <template v-if="selectedUserList.length > 0">
+        <SelectedUserItem v-for="item in showUserList" :info="item" @unSelect="unSelectUser" query />
+      </template>
+      <span v-else style="height: 30px;line-height: 30px;display: inline-block;margin-left: 7px;color: #bfbfbf;">请选择用户</span>
+      <div v-if="ellipsisInfo.status" class="user-selected-item">
+        <div class="user-select-ellipsis">
+          <span style="color: red">+{{ellipsisInfo.count}}...</span>
+        </div>
+      </div>
+    </div>    
+    
+    <div v-else style="display: flex; flex-wrap: wrap; flex-direction: row" >
+      <template v-if="selectedUserList.length > 0">
+        <SelectedUserItem v-for="item in selectedUserList" :info="item" @unSelect="unSelectUser" />
+      </template>
+      <a-button v-if="showAddButton" shape="circle" @click="onShowModal"><PlusOutlined /></a-button>
+    </div>
+
+    <user-select-modal :inSuperQuery="inSuperQuery" :multi="multi" :getContainer="getContainer" @register="registerModal" @selected="onSelected" :izExcludeMy="izExcludeMy"></user-select-modal>
+  </div>
+</template>
+
+<script lang="ts">
+  import { defineComponent, watch, ref, computed, toRaw } from 'vue';
+  import { Form } from 'ant-design-vue';
+  import { PlusOutlined } from '@ant-design/icons-vue';
+  import { useModal } from '/@/components/Modal';
+  import UserSelectModal from './UserSelectModal.vue';
+  import { defHttp } from '/@/utils/http/axios';
+  import SelectedUserItem from './SelectedUserItem.vue';
+  import { mySelfExpress, mySelfData } from './useUserSelect'
+
+  export default defineComponent({
+    name: 'UserSelect',
+    components: {
+      PlusOutlined,
+      UserSelectModal,
+      SelectedUserItem,
+    },
+    props: {
+      store: {
+        type: String,
+        default: 'id',
+      },
+      value: {
+        type: String,
+        default: '',
+      },
+      multi: {
+        type: Boolean,
+        default: false,
+      },
+      getContainer: {
+        type: Function,
+        default: null,
+      },
+      // 是否作为查询条件
+      query:{
+        type: Boolean,
+        default: false,
+      },
+      //最多显示几个人员-query为true有效
+      maxCount:{
+        type: Number,
+        default: 2
+      },
+      disabled:{
+        type: Boolean,
+        default: false,
+      },
+      //是否排除我自己
+      izExcludeMy:{
+        type: Boolean,
+        default: false,
+      },
+      //是否在高级查询中作为条件 可以选择当前用户
+      inSuperQuery:{
+        type: Boolean,
+        default: false,
+      }
+    },
+    emits: ['update:value', 'change'],
+    setup(props, { emit }) {
+      const formItemContext = Form.useInjectFormItemContext();
+      const loading = ref(true);
+      const selectedUserList = ref<any[]>([]);
+      const showUserList = computed(()=>{
+        let list = selectedUserList.value
+        let max = props.maxCount;
+        if(list.length<=max){
+          return list;
+        }
+        return list.filter((_item, index)=>index<max);
+      });
+      const ellipsisInfo = computed(()=>{
+        let max = props.maxCount;
+        let len = selectedUserList.value.length
+        if(len > max){
+          return {status: true, count: len-max};
+        }else{
+          return {status: false}
+        }
+      });
+
+      // 注册弹窗
+      const [registerModal, { openModal, closeModal }] = useModal();
+      function onShowModal() {
+        if(props.disabled===true){
+          return ;
+        }
+        let list = toRaw(selectedUserList.value);
+        openModal(true, {
+          list,
+        });
+      }
+
+      function onSelected(arr) {
+        console.log('onSelected', arr);
+        selectedUserList.value = arr;
+        onSelectedChange();
+        closeModal();
+      }
+
+      function onSelectedChange() {
+        loading.value = false;
+        let temp: any[] = [];
+        let arr = selectedUserList.value;
+        if (arr && arr.length > 0) {
+          temp = arr.map((k) => {
+            return k[props.store];
+          });
+        }
+        let str = temp.join(',');
+        emit('update:value', str);
+        emit('change', str);
+        formItemContext.onFieldChange();
+        console.log('选中数据', str);
+      }
+
+      watch(
+        () => props.value,
+        async (val) => {
+          if (val) {
+            if (loading.value === true) {
+              await getUserList(val);
+            }
+          } else {
+            selectedUserList.value = [];
+          }
+          loading.value = true;
+        },
+        { immediate: true }
+      );
+
+      async function getUserList(ids) {
+        let hasUserExpress = false;
+        let paramIds = ids;
+        let idList = [];
+        selectedUserList.value = [];
+        if(ids){
+          // update-begin-author:sunjianlei date:20230330 for: 修复用户选择器逗号分割回显不生效的问题
+          let tempArray = ids.split(',').map(s => s.trim()).filter(s => s != '');
+          if (tempArray.includes(mySelfExpress)) {
+            hasUserExpress = true;
+            idList = tempArray.filter(item => item != mySelfExpress);
+          } else {
+            idList = tempArray;
+          }
+          // update-end-author:sunjianlei date:20230330 for: 修复用户选择器逗号分割回显不生效的问题
+        }
+
+        if(idList.length>0){
+          paramIds = idList.join(',')
+          const url = '/sys/user/list';
+          let params = {
+            [props.store]: paramIds,
+          };
+          const data = await defHttp.get({ url, params }, { isTransformResponse: false });
+          console.log('getUserList', data);
+          if (data.success) {
+            const { records } = data.result;
+            selectedUserList.value = records;
+          } else {
+            console.error(data.message);
+          }
+        }
+        if(hasUserExpress){
+          let temp = selectedUserList.value;
+          temp.push({...mySelfData})
+        }
+      }
+
+      const showAddButton = computed(() => {
+        if(props.disabled === true){
+          return false;
+        }
+        if (props.multi === true) {
+          return true;
+        } else {
+          if (selectedUserList.value.length > 0) {
+            return false;
+          } else {
+            return true;
+          }
+        }
+      });
+
+      function unSelectUser(id) {
+        console.log('unSelectUser', id);
+        let arr = selectedUserList.value;
+        let index = -1;
+        for (let i = 0; i < arr.length; i++) {
+          if (arr[i].id == id) {
+            index = i;
+            break;
+          }
+        }
+        if (index >= 0) {
+          arr.splice(index, 1);
+          selectedUserList.value = arr;
+
+          onSelectedChange();
+        }
+      }
+      
+      function click2Add(e) {
+        e.preventDefault();
+        e.stopPropagation();
+        onShowModal();
+      }
+      
+      const isSearchFormComp = computed(()=>{
+        if(props.query===true){
+          return true;
+        }else{
+          return false
+        }
+      });
+      
+      return {
+        registerModal,
+        onShowModal,
+        isSearchFormComp,
+        onSelected,
+        showAddButton,
+        unSelectUser,
+        selectedUserList,
+        showUserList,
+        ellipsisInfo,
+        click2Add
+      };
+    },
+  });
+</script>
+
+<style lang="less" scoped>
+  .user-select-ellipsis{
+    width: 40px;
+    height: 24px;
+    text-align: center;
+    line-height: 22px;
+    border-radius: 8px;
+    background: #f5f5f5;
+    border: 1px solid #f0f0f0;
+  }
+  .disabled-user-select{
+    cursor: not-allowed;
+    background-color: #f5f5f5 !important;
+  }
+</style>

+ 11 - 0
src/components/Form/src/jeecg/components/userSelect/useUserSelect.ts

@@ -0,0 +1,11 @@
+/**
+ * 用户选择组件支持选择 我自己,以表达式的形式传值
+ */
+export const mySelfExpress = '#{sys_user_code}';
+
+/**
+ * 用户列表 我自己的数据
+ */
+export const mySelfData = {
+  id: mySelfExpress, username: mySelfExpress, realname: '当前用户', avatarIcon: 'idcard-outlined', avatarColor: 'rgb(75 176 79)'
+}

+ 69 - 0
src/components/Form/src/jeecg/hooks/useCodeHinting.ts

@@ -0,0 +1,69 @@
+export const useCodeHinting = (CodeMirror, keywords, language) => {
+  const currentKeywords: any = [...keywords];
+  const codeHintingMount = (coder) => {
+    if (keywords.length) {
+      coder.setOption('mode', language);
+      setTimeout(() => {
+        coder!.on('cursorActivity', function () {
+          coder?.showHint({
+            completeSingle: false,
+            // container: containerRef.value
+          });
+        });
+      }, 1e3);
+    }
+  };
+  const codeHintingRegistry = () => {
+    const funcsHint = (cm, callback) => {
+      // 获取光标位置
+      const cur = cm.getCursor();
+      // 获取当前单词的信息
+      const token = cm.getTokenAt(cur);
+      const start = token.start;
+      const end = cur.ch;
+      const str = token.string;
+
+      if (str.length) {
+        const findIdx = (a, b) => a.toLowerCase().indexOf(b.toLowerCase());
+        let list = currentKeywords
+          .filter((item) => {
+            const index = findIdx(item, str);
+            return (index === 0 || index === 1) && (item.length != str.length || item.length - 1 != str.length);
+          })
+          .sort((a, b) => {
+            if (findIdx(a, str) < findIdx(b, str)) {
+              return -1;
+            } else {
+              return 1;
+            }
+          });
+
+        // 有点去掉点
+        // list = list.map(item => {
+        //   if(item.indexOf(".") === 0){
+        //     return item.substring(1);
+        //   }
+        //   return item;
+        // });
+        if (list.length === 1 && (list[0] === str || list[0].substring(1) === str)) {
+          list = [];
+        }
+        if (list.length) {
+          callback({
+            list: list,
+            from: CodeMirror.Pos(cur.line, start),
+            to: CodeMirror.Pos(cur.line, end),
+          });
+        }
+      }
+    };
+    funcsHint.async = true;
+    funcsHint.supportsSelection = true;
+    // 自动补全
+    keywords.length && CodeMirror.registerHelper('hint', language, funcsHint);
+  };
+  return {
+    codeHintingRegistry,
+    codeHintingMount,
+  };
+};

+ 18 - 12
src/components/Form/src/jeecg/hooks/useSelectBiz.ts

@@ -1,5 +1,6 @@
 import { inject, reactive, ref, watch, unref, Ref } from 'vue';
 import { useMessage } from '/@/hooks/web/useMessage';
+import { isEmpty } from '@/utils/is';
 
 export function useSelectBiz(getList, props) {
   //接收下拉框选项
@@ -25,9 +26,12 @@ export function useSelectBiz(getList, props) {
   watch(
     selectValues,
     () => {
-      if (selectValues['change'] == false) {
+      //update-begin-author:liusq---date:2023-10-19--for: [issues/788]判断有设置数值才去加载
+      //if (selectValues['change'] == false && !isEmpty(selectValues['value'])) {
+      if (selectValues['change'] == false && !isEmpty(selectValues['value'])) {
+        //update-end-author:liusq---date:2023-10-19--for: [issues/788]判断有设置数值才去加载
         //update-begin---author:wangshuai ---date:20220412  for:[VUEN-672]发文草稿箱编辑时拟稿人显示用户名------------
-        const params = {};
+        let params = {};
         params[props.rowKey] = selectValues['value'].join(',');
         //update-end---author:wangshuai ---date:20220412  for:[VUEN-672]发文草稿箱编辑时拟稿人显示用户名--------------
         loadingEcho.value = isFirstLoadEcho;
@@ -48,7 +52,7 @@ export function useSelectBiz(getList, props) {
     checkedKeys.value = selectedRowKeys;
     //判断全选的问题checkedKeys和selectRows必须一致
     if (props.showSelected && unref(checkedKeys).length !== unref(selectRow).length) {
-      const { records } = await getList({
+      let { records } = await getList({
         code: unref(checkedKeys).join(','),
         pageSize: unref(checkedKeys).length,
       });
@@ -70,7 +74,7 @@ export function useSelectBiz(getList, props) {
     onChange: onSelectChange,
     //update-begin-author:wangshuai---date:20221102--for: [VUEN-2562]用户选择,跨页选择后,只有当前页人员 ---
     //table4.4.0新增属性选中之后是否清空上一页下一页的数据,默认false
-    preserveSelectedRowKeys: true,
+    preserveSelectedRowKeys:true,
     //update-end-author:wangshuai---date:20221102--for: [VUEN-2562]用户选择,跨页选择后,只有当前页人员 ---
   };
 
@@ -79,7 +83,7 @@ export function useSelectBiz(getList, props) {
    */
   const indexColumnProps = {
     dataIndex: 'index',
-    width: 20,
+    width: 50,
   };
 
   /**
@@ -88,10 +92,10 @@ export function useSelectBiz(getList, props) {
    * @param flag 是否是默认回显模式加载
    */
   async function getDataSource(params, flag) {
-    const { records } = await getList(params);
+    let { records } = await getList(params);
     dataSource.value = records;
     if (flag) {
-      const options = <any[]>[];
+      let options = <any[]>[];
       records.forEach((item) => {
         options.push({ label: item[props.labelKey], value: item[props.rowKey] });
       });
@@ -99,7 +103,7 @@ export function useSelectBiz(getList, props) {
     }
   }
   async function initSelectRows() {
-    const { records } = await getList({
+    let { records } = await getList({
       code: selectValues['value'].join(','),
       pageSize: selectValues['value'].length,
     });
@@ -121,8 +125,8 @@ export function useSelectBiz(getList, props) {
    * 确定选择
    */
   function getSelectResult(success) {
-    const options = <any[]>[];
-    const values = <any[]>[];
+    let options = <any[]>[];
+    let values = <any[]>[];
     selectRows.value.forEach((item) => {
       options.push({ label: item[props.labelKey], value: item[props.rowKey] });
     });
@@ -138,8 +142,10 @@ export function useSelectBiz(getList, props) {
   }
   //删除已选择的信息
   function handleDeleteSelected(record) {
-    checkedKeys.value = checkedKeys.value.splice(checkedKeys.value.indexOf(record['id']), 1);
-    selectRows.value = selectRows.value.filter((item) => item['id'] !== record['id']);
+    //update-begin---author:wangshuai ---date:20230404  for:【issues/424】开启右侧列表后,在右侧列表中删除用户时,逻辑有问题------------
+    checkedKeys.value = checkedKeys.value.filter((item) => item != record[props.rowKey]);
+    selectRows.value = selectRows.value.filter((item) => item[props.rowKey] !== record[props.rowKey]);
+    //update-end---author:wangshuai ---date:20230404  for:【issues/424】开启右侧列表后,在右侧列表中删除用户时,逻辑有问题------------
   }
   //清空选择项
   function reset() {

+ 30 - 4
src/components/Form/src/jeecg/hooks/useTreeBiz.ts

@@ -3,7 +3,7 @@ import { inject, reactive, ref, computed, unref, watch, nextTick } from 'vue';
 import { TreeActionType } from '/@/components/Tree';
 import { listToTree } from '/@/utils/common/compUtils';
 
-export function useTreeBiz(treeRef, getList, props) {
+export function useTreeBiz(treeRef, getList, props, realProps) {
   //接收下拉框选项
   const selectOptions = inject('selectOptions', ref<Array<object>>([]));
   //接收已选择的值
@@ -19,7 +19,7 @@ export function useTreeBiz(treeRef, getList, props) {
   //是否是打开弹框模式
   const openModal = ref(false);
   // 是否开启父子关联,如果不可以多选,就始终取消父子关联
-  const getCheckStrictly = computed(() => (props.multiple ? props.checkStrictly : true));
+  const getCheckStrictly = computed(() => (realProps.multiple ? props.checkStrictly : true));
   // 是否是首次加载回显,只有首次加载,才会显示 loading
   let isFirstLoadEcho = true;
 
@@ -29,6 +29,9 @@ export function useTreeBiz(treeRef, getList, props) {
   watch(
     selectValues,
     ({ value: values }: Recordable) => {
+      if(!values){
+        return;
+      }
       if (openModal.value == false && values.length > 0) {
         loadingEcho.value = isFirstLoadEcho;
         isFirstLoadEcho = false;
@@ -85,7 +88,7 @@ export function useTreeBiz(treeRef, getList, props) {
   function onCheck(keys, info) {
     if (props.checkable == true) {
       // 如果不能多选,就只保留最后一个选中的
-      if (!props.multiple) {
+      if (!realProps.multiple) {
         if (info.checked) {
           //update-begin-author:taoyan date:20220408 for: 单选模式下,设定rowKey,无法选中数据-
           checkedKeys.value = [info.node.eventKey];
@@ -112,8 +115,31 @@ export function useTreeBiz(treeRef, getList, props) {
   /**
    * 勾选全部
    */
-  function checkALL(checkAll) {
+  async function checkALL(checkAll) {
     getTree().checkAll(checkAll);
+    //update-begin---author:wangshuai ---date:20230403  for:【issues/394】所属部门树操作全部勾选不生效/【issues/4646】部门全部勾选后,点击确认按钮,部门信息丢失------------
+    await nextTick();
+    checkedKeys.value = getTree().getCheckedKeys();
+    if(checkAll){
+      getTreeRow();
+    }else{
+      selectRows.value = [];
+    }
+    //update-end---author:wangshuai ---date:20230403  for:【issues/394】所属部门树操作全部勾选不生效/【issues/4646】部门全部勾选后,点击确认按钮,部门信息丢失------------
+  }
+
+  /**
+   * 获取数列表
+   * @param res
+   */
+  function getTreeRow() {
+    let ids = "";
+    if(unref(checkedKeys).length>0){
+      ids = checkedKeys.value.join(",");
+    }
+    getList({ids:ids}).then((res) =>{
+      selectRows.value = res;
+    })
   }
 
   /**

+ 3 - 0
src/components/Form/src/props.ts

@@ -115,4 +115,7 @@ export const basicProps = {
   labelAlign: propTypes.string,
 
   rowProps: Object as PropType<RowProps>,
+  
+  // 当表单是查询条件的时候 当表单改变后自动查询,不需要点击查询按钮
+  autoSearch: propTypes.bool.def(false),
 };

+ 2 - 2
src/components/Form/src/types/form.ts

@@ -1,4 +1,4 @@
-import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface';
+import type { NamePath, RuleObject, ValidateOptions } from 'ant-design-vue/lib/form/interface';
 import type { VNode, ComputedRef } from 'vue';
 import type { ButtonProps as AntdButtonProps } from '/@/components/Button';
 import type { FormItem } from './formItem';
@@ -37,7 +37,7 @@ export interface FormActionType {
   getProps: ComputedRef<Partial<FormProps>>;
   removeSchemaByFiled: (field: string | string[]) => Promise<void>;
   appendSchemaByField: (schema: FormSchema, prefixField: string | undefined, first?: boolean | undefined) => Promise<void>;
-  validateFields: (nameList?: NamePath[]) => Promise<any>;
+  validateFields: (nameList?: NamePath[], options?: ValidateOptions) => Promise<any>;
   validate: (nameList?: NamePath[]) => Promise<any>;
   scrollToField: (name: NamePath, options?: ScrollOptions) => Promise<void>;
 }

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

@@ -143,6 +143,15 @@ export type ComponentType =
   | 'JSearchSelect'
   | 'JAddInput'
   | 'Time'
+  | 'OnlineSelectCascade'
+  | 'LinkTableCard'
+  | 'LinkTableSelect'
+  | 'LinkTableForQuery'
+  | 'CascaderPcaForQuery'
+  | 'UserSelect'
+  | 'RoleSelect'
   | 'RangeDate'
   | 'RangeNumber'
+  | 'linkRecordSelect'
+  | 'RangeTime'
   | 'JRangeNumber';

+ 8 - 4
src/components/Form/src/utils/Area.ts

@@ -56,15 +56,17 @@ class Area {
       }
     }
   }
-
-  getText(code) {
+  
+//update-begin-author:liusq---date:20230404--for: [issue/382]省市区组件JAreaLinkage数据不回显---
+  getText(code,index=3) {
     if (!code || code.length == 0) {
       return '';
     }
     let arr = [];
-    this.getAreaBycode(code, arr, 3);
+    this.getAreaBycode(code, arr, index);
     return arr.join('/');
   }
+//update-end-author:liusq---date:20230404--for: [issue/382]省市区组件JAreaLinkage数据不回显---
 
   getRealCode(code) {
     let arr = [];
@@ -98,12 +100,14 @@ const jeecgAreaData = new Area();
 
 // 根据code找文本
 const getAreaTextByCode = function (code) {
+  let index = 3;
   //update-begin-author:liusq---date:20220531--for: 判断code是否是多code逗号分割的字符串,是的话,获取最后一位的code ---
   if (code && code.includes(',')) {
+    index = code.split(",").length;
     code = code.substr(code.lastIndexOf(',') + 1);
   }
   //update-end-author:liusq---date:20220531--for: 判断code是否是多code逗号分割的字符串,是的话,获取最后一位的code ---
-  return jeecgAreaData.getText(code);
+  return jeecgAreaData.getText(code,index);
 };
 
 export { getAreaTextByCode };

+ 54 - 15
src/components/Icon/src/IconPicker.vue

@@ -4,14 +4,14 @@
       <a-popover placement="bottomLeft" trigger="click" v-model="visible" :overlayClassName="`${prefixCls}-popover`">
         <template #title>
           <div class="flex justify-between">
-            <a-input :placeholder="t('component.icon.search')"  @change="debounceHandleSearchChange" allowClear />
+            <a-input :placeholder="t('component.icon.search')" v-model:value="searchIconValue"  @change="debounceHandleSearchChange" allowClear />
           </div>
         </template>
 
         <template #content>
           <div v-if="getPaginationList.length">
             <ScrollContainer class="border border-solid border-t-0">
-              <ul class="flex flex-wrap px-2">
+              <ul class="flex flex-wrap px-2" style="padding-right: 0">
                 <li
                   v-for="icon in getPaginationList"
                   :key="icon"
@@ -27,7 +27,7 @@
               </ul>
             </ScrollContainer>
             <div class="flex py-2 items-center justify-center" v-if="getTotal >= pageSize">
-              <a-pagination showLessItems size="small" :pageSize="pageSize" :total="getTotal" @change="handlePageChange" />
+              <a-pagination showLessItems v-model:current="current" :page-size-options="pageSizeOptions" size="small" v-model:pageSize="pageSize" v-model:total="getTotal" @change="handlePageChange" />
             </div>
           </div>
           <template v-else
@@ -36,9 +36,9 @@
         </template>
 
         <span class="cursor-pointer px-2 py-1 flex items-center" v-if="isSvgMode && currentSelect">
-          <SvgIcon :name="currentSelect" />
+          <SvgIcon :name="currentSelect" @click="currentSelectClick"/>
         </span>
-        <Icon :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1" v-else />
+        <Icon :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1" v-else @click="currentSelectClick"/>
       </a-popover>
     </template>
   </a-input>
@@ -85,10 +85,10 @@
   const props = defineProps({
     value: propTypes.string,
     width: propTypes.string.def('100%'),
-    pageSize: propTypes.number.def(140),
     copy: propTypes.bool.def(false),
     mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'),
     disabled: propTypes.bool.def(true),
+    clearSelect: propTypes.bool.def(false)
   });
 
   const emit = defineEmits(['change', 'update:value']);
@@ -106,8 +106,16 @@
   const debounceHandleSearchChange = useDebounceFn(handleSearchChange, 100);
   const { clipboardRef, isSuccessRef } = useCopyToClipboard(props.value);
   const { createMessage } = useMessage();
+  //页数
+  const current = ref<number>(1);
+  //每页条数
+  const pageSize = ref<number>(140);
+  //下拉分页显示
+  const pageSizeOptions = ref<any>(['10', '20', '50', '100', '140'])
+  //下拉搜索值
+  const searchIconValue = ref<string>('');
 
-  const { getPaginationList, getTotal, setCurrentPage } = usePagination(currentList, props.pageSize);
+  const { getPaginationList, getTotal, setCurrentPage, setPageSize } = usePagination(currentList, pageSize.value);
 
   watchEffect(() => {
     currentSelect.value = props.value;
@@ -121,29 +129,60 @@
     }
   );
 
-  function handlePageChange(page: number) {
+  function handlePageChange(page: number,size: number) {
+    //update-begin---author:wangshuai ---date:20230522  for:【issues/4947】菜单编辑页面菜单图标选择模板,每页显示数量切换无效------------
+    current.value = page;
+    pageSize.value = size;
+    setPageSize(size);
+    //update-end---author:wangshuai ---date:20230522  for:【issues/4947】菜单编辑页面菜单图标选择模板,每页显示数量切换无效------------
     setCurrentPage(page);
   }
 
   function handleClick(icon: string) {
-    currentSelect.value = icon;
-    if (props.copy) {
-      clipboardRef.value = icon;
-      if (unref(isSuccessRef)) {
-        createMessage.success(t('component.icon.copy'));
+    if(props.clearSelect === true){
+      if(currentSelect.value===icon){
+        currentSelect.value = ''
+      }else{
+        currentSelect.value = icon;
+      }
+    }else{
+      currentSelect.value = icon;
+      if (props.copy) {
+        clipboardRef.value = icon;
+        if (unref(isSuccessRef)) {
+          createMessage.success(t('component.icon.copy'));
+        }
       }
     }
+    
   }
 
   function handleSearchChange(e: ChangeEvent) {
     const value = e.target.value;
+    //update-begin---author:wangshuai ---date:20230522  for:【issues/4947】菜单编辑页面菜单图标选择模板,每页显示数量切换无效------------
+    setCurrentPage(1);
+    current.value = 1;
+    //update-end---author:wangshuai ---date:20230522  for:【issues/4947】菜单编辑页面菜单图标选择模板,每页显示数量切换无述------------
     if (!value) {
-      setCurrentPage(1);
       currentList.value = icons;
       return;
     }
     currentList.value = icons.filter((item) => item.includes(value));
   }
+  
+  //update-begin---author:wangshuai ---date:20230522  for:【issues/4947】菜单编辑页面菜单图标选择模板,每页显示数量切换无效,输入框后面的图标点击之后清空数据------------
+  /**
+   * 图标点击重置页数
+   */
+  function currentSelectClick() {
+    setCurrentPage(1);
+    setPageSize(140);
+    current.value = 1;
+    pageSize.value = 140;
+    currentList.value = icons;
+    searchIconValue.value = '';
+  }
+  //update-begin---author:wangshuai ---date:20230522  for:【issues/4947】菜单编辑页面菜单图标选择模板,每页显示数量切换无效,输入框后面的图标点击之后清空数据------------
 </script>
 <style lang="less">
   @prefix-cls: ~'@{namespace}-icon-picker';
@@ -155,7 +194,7 @@
     }
 
     &-popover {
-      width: 300px;
+      width: 400px;
 
       .@{ventSpace}-popover-inner-content {
         padding: 0;

+ 2 - 1
src/components/JVxeCustom/src/components/JVxeDepartSelectCell.vue

@@ -30,7 +30,8 @@
 
 <script lang="ts">
   import { computed, defineComponent } from 'vue';
-  import { JSelectDept } from '/@/components/Form';
+  //import { JSelectDept } from '/@/components/Form';
+  import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
   import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
   import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
 

+ 6 - 7
src/components/JVxeCustom/src/components/JVxePcaCell.vue

@@ -1,5 +1,5 @@
 <template>
-  <a-cascader v-bind="getProps" class="pca-select" @change="handleChange" />
+    <a-cascader v-bind="getProps" class="pca-select" @change="handleChange" />
 </template>
 
 <script lang="ts">
@@ -8,7 +8,6 @@
   import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
   import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
   import { dispatchEvent } from '/@/components/jeecg/JVxeTable/utils';
-  import { antPrefixCls } from '/@/settings/designSetting'
 
   export default defineComponent({
     name: 'JVxePcaCell',
@@ -19,7 +18,7 @@
       const selectedValue = computed(() => {
         let val: any = innerValue.value;
         if (!val) {
-          return [];
+          return []
         }
         let arr = getRealCode(val, 3);
         return arr;
@@ -33,10 +32,10 @@
           value: selectedValue.value,
         };
       });
-
+      
       function handleChange(arr) {
         let str = '';
-        if (arr && arr.length == 3) {
+        if(arr && arr.length==3){
           str = arr[2];
         }
         handleChangeCommon(str);
@@ -45,7 +44,7 @@
       return {
         handleChange,
         selectedValue,
-        getProps,
+        getProps
       };
     },
     // 【组件增强】注释详见:JVxeComponent.Enhanced
@@ -60,7 +59,7 @@
         editActived({ $event }) {
           dispatchEvent({
             $event,
-            props: this.props, 
+            props: this.props,
             className: `.${antPrefixCls}-select .${antPrefixCls}-select-selection-search-input`,
             isClick: true,
           });

+ 15 - 7
src/components/JVxeCustom/src/components/JVxePopupCell.vue

@@ -1,9 +1,10 @@
 <template>
-  <JPopup v-bind="popupProps" />
+  <JPopup v-bind="popupProps" @focus="handleFocus" />
 </template>
 <script lang="ts">
-  import { computed, defineComponent } from 'vue';
-  import { JPopup } from '/@/components/Form';
+  import { computed, defineComponent, ref } from 'vue';
+  //import { JPopup } from '/@/components/Form';
+  import JPopup from '/@/components/Form/src/jeecg/components/JPopup.vue';
   import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
   import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
   import { dispatchEvent, vModel } from '/@/components/jeecg/JVxeTable/utils';
@@ -16,17 +17,18 @@
     props: useJVxeCompProps(),
     setup(props: JVxeComponent.Props) {
       const { innerValue, row, originColumn, cellProps, handleChangeCommon } = useJVxeComponent(props);
-
+      const groupId = ref<string>('j-vxe-popup');
       const popupProps = computed(() => {
+        // update-begin--author:liaozhiyang---date:20231009---for:【issues/5371】一对多子表popup增加多选
         return {
-          ...cellProps,
+          ...cellProps.value,
           value: innerValue.value,
           field: originColumn.value.field || originColumn.value.key,
           code: originColumn.value.popupCode,
           fieldConfig: originColumn.value.fieldConfig,
           // orgFields: originColumn.value.orgFields,
           // destFields: originColumn.value.destFields,
-          groupId: 'j-vxe-popup',
+          groupId: groupId.value,
           param: originColumn.value.params,
           sorter: originColumn.value.sorter,
           setFieldsValue: (values) => {
@@ -45,9 +47,15 @@
             }
           },
         };
+        // update-end--author:liaozhiyang---date:20231009---for:【issues/5371】一对多子表popup增加多选
       });
-
+      // update-begin--author:liaozhiyang---date:20230811---for:【issues/675】子表字段Popup弹框数据不更新
+      const handleFocus = () => {
+        groupId.value = '';
+      };
+      // update-end--author:liaozhiyang---date:20230811---for:【issues/675】子表字段Popup弹框数据不更新
       return {
+        handleFocus,
         popupProps,
       };
     },

+ 2 - 2
src/components/JVxeCustom/src/components/JVxeSelectDictSearchCell.ts

@@ -8,7 +8,7 @@ import { dispatchEvent } from '/@/components/jeecg/JVxeTable/utils';
 import { useResolveComponent as rc } from '/@/components/jeecg/JVxeTable/hooks';
 import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
 import { useMessage } from '/@/hooks/web/useMessage';
-import { antPrefixCls } from '/@/settings/designSetting';
+
 /** value - label map,防止重复查询(刷新清空缓存) */
 const LabelMap = new Map<string, any>();
 // 请求id
@@ -43,7 +43,7 @@ export const DictSearchInputCell = defineComponent({
     const filterOption = computed(() => {
       if (isAsync.value) {
         //【jeecgboot-vue3/issues/I5QRT8】JVxeTypes.selectDictSearch sync问题
-        return () => true;
+        return ()=>true;
       }
       return (input, option) => option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0;
     });

+ 2 - 1
src/components/JVxeCustom/src/components/JVxeUserSelectCell.vue

@@ -6,7 +6,8 @@
 
 <script lang="ts">
   import { computed, defineComponent } from 'vue';
-  import { JSelectUser } from '/@/components/Form';
+  //import { JSelectUser } from '/@/components/Form';
+  import JSelectUser from '/@/components/Form/src/jeecg/components/JSelectUser.vue';
   import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
   import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
   import { dispatchEvent } from '/@/components/jeecg/JVxeTable/utils';

+ 4 - 0
src/components/JVxeCustom/src/hooks/useFileCell.ts

@@ -65,6 +65,10 @@ export function useFileCell(props, fileType: UploadTypeEnum, options?) {
     if (path) {
       innerFile.value.path = path;
       handleChangeCommon(innerFile.value);
+    } else {
+      //update-begin-author:liusq date:2023-06-05 for: [issues/530]JVxeTable 的JVxeTypes.image类型,无法全部删除上传图片
+      handleChangeCommon(null);
+      //update-end-author:liusq date:2023-06-05 for:  [issues/530]JVxeTable 的JVxeTypes.image类型,无法全部删除上传图片
     }
   }
 

+ 4 - 1
src/components/Markdown/src/Markdown.vue

@@ -10,7 +10,7 @@
   import { useModalContext } from '../../Modal';
   import { useRootSetting } from '/@/hooks/setting/useRootSetting';
   import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
-  import { getToken } from '/@/utils/auth';
+  import { getTenantId, getToken } from '/@/utils/auth';
   import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
 
   type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
@@ -23,6 +23,7 @@
     },
     emits: ['change', 'get', 'update:value'],
     setup(props, { attrs, emit }) {
+      console.log("---Markdown---初始化---")
       const wrapRef = ref<ElRef>(null);
       const vditorRef = ref(null) as Ref<Nullable<Vditor>>;
       const initedRef = ref(false);
@@ -78,6 +79,7 @@
       //update-begin-author:taoyan date:2022-5-24 for: VUEN-1090 markdown 无法上传
       const uploadUrl = `${window._CONFIG['domianURL']}/sys/common/upload`;
       const token = getToken();
+      const tenantId = getTenantId() ? getTenantId() : '0';
       function formatResult(files, responseText): string {
         let data: any = JSON.parse(responseText);
         // {"success":true,"message":"markdown/aa_1653391146501.png","code":0,"result":null,"timestamp":1653391146501}'
@@ -126,6 +128,7 @@
             setHeaders() {
               return {
                 'X-Access-Token': token as string,
+                'X-Tenant-Id': tenantId,
               };
             },
             format(files, response) {

+ 97 - 67
src/components/Modal/src/BasicModal.vue

@@ -1,62 +1,79 @@
 <template>
   <div class="vent-modal">
-    <Modal v-bind="getBindValue" @cancel="handleCancel">
-      <template #closeIcon v-if="!$slots.closeIcon">
-        <ModalClose
-          :canFullscreen="getProps.canFullscreen"
-          :fullScreen="fullScreenRef"
-          :commentSpan="commentSpan"
-          :enableComment="getProps.enableComment"
-          @comment="handleComment"
-          @cancel="handleCancel"
-          @fullscreen="handleFullScreen"
-        />
-      </template>
+   <Modal v-bind="getBindValue" @cancel="handleCancel">
+    <template #closeIcon v-if="!$slots.closeIcon">
+      <ModalClose :canFullscreen="getProps.canFullscreen" :fullScreen="fullScreenRef" :commentSpan="commentSpan" :enableComment="getProps.enableComment" @comment="handleComment" @cancel="handleCancel" @fullscreen="handleFullScreen" />
+    </template>
+
+    <template #title v-if="!$slots.title">
+      <ModalHeader :helpMessage="getProps.helpMessage" :title="getMergeProps.title" @dblclick="handleTitleDbClick" />
+    </template>
+    <template #title v-if="!isNoTitle">
+      <ModalHeader :helpMessage="getProps.helpMessage" :title="getMergeProps.title" @dblclick="handleTitleDbClick" />
+    </template>
 
-      <template #title v-if="!$slots.title">
-        <ModalHeader :helpMessage="getProps.helpMessage" :title="getMergeProps.title" @dblclick="handleTitleDbClick" />
-      </template>
+    <template #footer v-if="!$slots.footer">
+      <ModalFooter v-bind="getBindValue" @ok="handleOk" @cancel="handleCancel">
+        <template #[item]="data" v-for="item in Object.keys($slots)">
+          <slot :name="item" v-bind="data || {}"></slot>
+        </template>
+      </ModalFooter>
+    </template>
 
-      <template #footer v-if="!$slots.footer">
-        <ModalFooter v-bind="getBindValue" @ok="handleOk" @cancel="handleCancel">
-          <template #[item]="data" v-for="item in Object.keys($slots)">
-            <slot :name="item" v-bind="data || {}"></slot>
-          </template>
-        </ModalFooter>
-      </template>
+    <!-- update-begin-author:taoyan date:2022-7-18 for:  modal弹窗 支持评论 slot -->
+    <a-row v-if="getProps.enableComment" class="jeecg-modal-wrapper">
+      <a-col :span="24-commentSpan" class="jeecg-modal-content">
+        <ModalWrapper
+          :useWrapper="getProps.useWrapper"
+          :footerOffset="wrapperFooterOffset"
+          :fullScreen="fullScreenRef"
+          ref="modalWrapperRef"
+          :loading="getProps.loading"
+          :loading-tip="getProps.loadingTip"
+          :minHeight="getProps.minHeight"
+          :height="getWrapperHeight"
+          :visible="visibleRef"
+          :modalFooterHeight="footer !== undefined && !footer ? 0 : undefined"
+          v-bind="omit(getProps.wrapperProps, 'visible', 'height', 'modalFooterHeight')"
+          @ext-height="handleExtHeight"
+          @height-change="handleHeightChange">
+          <slot></slot>
+        </ModalWrapper>
+      </a-col>
+    </a-row>
+    <!-- update-begin-author:taoyan date:2022-7-18 for:  modal弹窗 支持评论 slot -->
+    <a-row class="jeecg-modal-wrapper">
+      <a-col :span="24-commentSpan" class="jeecg-modal-content">
+        <ModalWrapper
+          :useWrapper="getProps.useWrapper"
+          :footerOffset="wrapperFooterOffset"
+          :fullScreen="fullScreenRef"
+          ref="modalWrapperRef"
+          :loading="getProps.loading"
+          :loading-tip="getProps.loadingTip"
+          :minHeight="getProps.minHeight"
+          :height="getWrapperHeight"
+          :visible="visibleRef"
+          :modalFooterHeight="footer !== undefined && !footer ? 0 : undefined"
+          v-bind="omit(getProps.wrapperProps, 'visible', 'height', 'modalFooterHeight')"
+          @ext-height="handleExtHeight"
+          @height-change="handleHeightChange">
+          <slot></slot>
+        </ModalWrapper>
+      </a-col>
 
-      <!-- update-begin-author:taoyan date:2022-7-18 for:  modal弹窗 支持评论 slot -->
-      <a-row class="jeecg-modal-wrapper">
-        <a-col :span="24 - commentSpan" class="jeecg-modal-content">
-          <ModalWrapper
-            :useWrapper="getProps.useWrapper"
-            :footerOffset="wrapperFooterOffset"
-            :fullScreen="fullScreenRef"
-            ref="modalWrapperRef"
-            :loading="getProps.loading"
-            :loading-tip="getProps.loadingTip"
-            :minHeight="getProps.minHeight"
-            :height="getWrapperHeight"
-            :visible="visibleRef"
-            :modalFooterHeight="footer !== undefined && !footer ? 0 : undefined"
-            v-bind="omit(getProps.wrapperProps, 'visible', 'height', 'modalFooterHeight')"
-            @ext-height="handleExtHeight"
-            @height-change="handleHeightChange"
-          >
-            <slot></slot>
-          </ModalWrapper>
-        </a-col>
 
-        <a-col :span="commentSpan" class="jeecg-comment-outer">
-          <slot name="comment"></slot>
-        </a-col>
-      </a-row>
-      <!-- update-end-author:taoyan date:2022-7-18 for:  modal弹窗 支持评论 slot -->
+      
+      <a-col :span="commentSpan" class="jeecg-comment-outer">
+        <slot name="comment"></slot>
+      </a-col>
+    </a-row>
+    <!-- update-end-author:taoyan date:2022-7-18 for:  modal弹窗 支持评论 slot -->
 
-      <template #[item]="data" v-for="item in Object.keys(omit($slots, 'default'))">
-        <slot :name="item" v-bind="data || {}"></slot>
-      </template>
-    </Modal>
+    <template #[item]="data" v-for="item in Object.keys(omit($slots, 'default'))">
+      <slot :name="item" v-bind="data || {}"></slot>
+    </template>
+  </Modal>
   </div>
 </template>
 <script lang="ts">
@@ -79,8 +96,8 @@
     components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader },
     inheritAttrs: false,
     props: basicProps,
-    emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible'],
-    setup(props, { emit, attrs }) {
+    emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible', 'fullScreen'],
+    setup(props, { emit, attrs , slots}) {
       const visibleRef = ref(false);
       const propsRef = ref<Partial<ModalProps> | null>(null);
       const modalWrapperRef = ref<any>(null);
@@ -111,6 +128,13 @@
           ...(unref(propsRef) as any),
         };
       });
+        //update-begin-author:liusq date:2023-05-25 for:【issues/4856】Modal控件设置 :title = null 无效
+        //是否未设置标题
+        const isNoTitle = computed(() => {
+            //标题为空并且不含有标题插槽
+            return !unref(getMergeProps).title && !slots.title;
+        });
+        //update-end-author:liusq date:2023-05-25 for:【issues/4856】Modal控件设置 :title = null 无效
 
       const { handleFullScreen, getWrapClassName, fullScreenRef } = useFullScreen({
         modalWrapperRef,
@@ -152,10 +176,13 @@
       });
 
       watchEffect(() => {
-        visibleRef.value = !!props.visible;
         fullScreenRef.value = !!props.defaultFullscreen;
       });
 
+      watchEffect(() => {
+        visibleRef.value = !!props.visible;
+      });
+
       watch(
         () => unref(visibleRef),
         (v) => {
@@ -222,22 +249,24 @@
 
       //update-begin-author:taoyan date:2022-7-18 for: modal支持评论 slot
       const commentSpan = ref(0);
-      watch(
-        () => props.enableComment,
-        (flag) => {
-          handleComment(flag);
-        },
-        { immediate: true }
-      );
-      function handleComment(flag) {
-        if (flag === true) {
-          commentSpan.value = 6;
-        } else {
-          commentSpan.value = 0;
+      watch(()=>props.enableComment, (flag)=>{
+        handleComment(flag)
+      }, {immediate:true});
+      function handleComment(flag){
+        if(flag=== true){
+          commentSpan.value = 6
+        }else{
+          commentSpan.value = 0
         }
       }
       //update-end-author:taoyan date:2022-7-18 for: modal支持评论 slot
 
+      // update-begin--author:liaozhiyang---date:20230804---for:【QQYUN-5866】放大行数自适应
+      watch(fullScreenRef,(val)=>{
+        emit('fullScreen',val);
+      });
+      // update-begin--author:liaozhiyang---date:20230804---for:【QQYUN-5866】放大行数自适应
+
       return {
         handleCancel,
         getBindValue,
@@ -255,6 +284,7 @@
         getWrapperHeight,
         commentSpan,
         handleComment,
+        isNoTitle
       };
     },
   });

+ 3 - 3
src/components/Modal/src/components/ModalClose.vue

@@ -11,7 +11,7 @@
 
     <!-- 是否开启评论区域 -->
     <template v-if="enableComment">
-      <Tooltip title="关闭" placement="bottom" v-if="commentSpan>0">
+      <Tooltip title="收起" placement="bottom" v-if="commentSpan>0">
         <RightSquareOutlined @click="handleCloseComment" style="font-size: 16px"/>
       </Tooltip>
       <Tooltip title="展开" placement="bottom" v-else>
@@ -50,7 +50,7 @@
           prefixCls,
           `${prefixCls}--custom`,
           {
-            [`${prefixCls}--can-full`]: props.canFullscreen,
+            [`${prefixCls}--can-full`]: props.canFullscreen || props.enableComment,
           },
         ];
       });
@@ -156,4 +156,4 @@
       }
     }
   }
-</style>
+</style>

+ 6 - 1
src/components/Modal/src/hooks/useModalDrag.ts

@@ -37,7 +37,12 @@ export function useModalDragMove(context: UseModalDragMoveContext) {
 
       const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
       const minDragDomTop = dragDom.offsetTop;
-      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
+      let maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
+      //update-begin-author:liusq---date:20230407--for: [issue/430]弹出页面出现自动吸顶,无法移动和显示头部--- 
+      if(maxDragDomTop<0){
+        maxDragDomTop = screenHeight - dragDom.offsetTop
+      }
+      //update-end-author:liusq---date:20230407--for: [issue/430]弹出页面出现自动吸顶,无法移动和显示头部--- 
       // 获取到的值带px 正则匹配替换
       const domLeft = getStyle(dragDom, 'left');
       const domTop = getStyle(dragDom, 'top');

+ 0 - 2
src/components/Page/index.ts

@@ -4,5 +4,3 @@ import pageFooter from './src/PageFooter.vue';
 import pageWrapper from './src/PageWrapper.vue';
 export const PageFooter = withInstall(pageFooter);
 export const PageWrapper = withInstall(pageWrapper);
-
-export const PageWrapperFixedHeightKey = 'PageWrapperFixedHeight';

+ 1 - 0
src/components/Page/injectionKey.ts

@@ -0,0 +1 @@
+export const PageWrapperFixedHeightKey = 'PageWrapperFixedHeight';

+ 1 - 1
src/components/Page/src/PageWrapper.vue

@@ -43,7 +43,7 @@
   import { omit } from 'lodash-es';
   import { PageHeader } from 'ant-design-vue';
   import { useContentHeight } from '/@/hooks/web/useContentHeight';
-  import { PageWrapperFixedHeightKey } from '..';
+  import { PageWrapperFixedHeightKey } from '../injectionKey';
 
   export default defineComponent({
     name: 'PageWrapper',

+ 151 - 23
src/components/Table/src/BasicTable.vue

@@ -17,35 +17,42 @@
     <!-- antd v3 升级兼容,阻止数据的收集,防止控制台报错 -->
     <!-- https://antdv.com/docs/vue/migration-v3-cn -->
     <a-form-item-rest>
-      <Table
-        ref="tableElRef"
-        v-bind="getBindValues"
-        :rowClassName="getRowClassName"
-        v-show="getEmptyDataIsShowTable"
-        @resizeColumn="handleResizeColumn"
-        @change="handleTableChange"
-      >
-        <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
+      <Table ref="tableElRef" v-bind="getBindValues" :rowClassName="getRowClassName" v-show="getEmptyDataIsShowTable" @resizeColumn="handleResizeColumn" @change="handleTableChange">
+        <!-- antd的原生插槽直接传递 -->
+        <template #[item]="data" v-for="item in slotNamesGroup.native" :key="item">
           <slot :name="item" v-bind="data || {}"></slot>
         </template>
         <template #headerCell="{ column }">
-          <HeaderCell :column="column" />
+          <!-- update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题 -->
+          <CustomSelectHeader v-if="isCustomSelection(column)" v-bind="selectHeaderProps"/>
+          <HeaderCell v-else :column="column" />
+          <!-- update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题 -->
         </template>
         <!-- 增加对antdv3.x兼容 -->
         <template #bodyCell="data">
-          <slot name="bodyCell" v-bind="data || {}"></slot>
+          <!-- update-begin--author:liaozhiyang---date:220230717---for:【issues-179】antd3 一些警告以及报错(针对表格) -->
+          <!-- update-begin--author:liusq---date:20230921---for:【issues/770】slotsBak异常报错的问题,增加判断column是否存在 -->
+          <template v-if="data.column?.slotsBak?.customRender">
+          <!-- update-end--author:liusq---date:20230921---for:【issues/770】slotsBak异常报错的问题,增加判断column是否存在 -->
+            <slot :name="data.column.slotsBak.customRender" v-bind="data || {}"></slot>
+          </template>
+          <template v-else>
+            <slot name="bodyCell" v-bind="data || {}"></slot>
+          </template>
+          <!-- update-begin--author:liaozhiyang---date:22030717---for:【issues-179】antd3 一些警告以及报错(针对表格) -->
         </template>
       </Table>
     </a-form-item-rest>
   </div>
 </template>
 <script lang="ts">
-  import type { BasicTableProps, TableActionType, SizeType, ColumnChangeParam } from './types/table';
+  import type { BasicTableProps, TableActionType, SizeType, ColumnChangeParam, BasicColumn } from './types/table';
 
-  import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect } from 'vue';
+  import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect, watch, onUnmounted, onMounted } from 'vue';
   import { Table } from 'ant-design-vue';
   import { BasicForm, useForm } from '/@/components/Form/index';
-  import { PageWrapperFixedHeightKey } from '/@/components/Page';
+  import { PageWrapperFixedHeightKey } from '/@/components/Page/injectionKey';
+  import CustomSelectHeader from './components/CustomSelectHeader.vue'
   import expandIcon from './components/ExpandIcon';
   import HeaderCell from './components/HeaderCell.vue';
   import { InnerHandlers } from './types/table';
@@ -63,6 +70,7 @@
   import { useTableFooter } from './hooks/useTableFooter';
   import { useTableForm } from './hooks/useTableForm';
   import { useDesign } from '/@/hooks/web/useDesign';
+  import { useCustomSelection } from "./hooks/useCustomSelection";
 
   import { omit } from 'lodash-es';
   import { basicProps } from './props';
@@ -74,6 +82,7 @@
       Table,
       BasicForm,
       HeaderCell,
+      CustomSelectHeader,
     },
     props: basicProps,
     emits: [
@@ -117,11 +126,38 @@
       });
 
       const { getLoading, setLoading } = useLoading(getProps);
-
       const { getPaginationInfo, getPagination, setPagination, setShowPagination, getShowPagination } = usePagination(getProps);
 
-      const { getRowSelection, getRowSelectionRef, getSelectRows, clearSelectedRowKeys, getSelectRowKeys, deleteSelectRowByKey, setSelectedRowKeys } =
-        useRowSelection(getProps, tableData, emit);
+      // update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
+
+      // const { getRowSelection, getRowSelectionRef, getSelectRows, clearSelectedRowKeys, getSelectRowKeys, deleteSelectRowByKey, setSelectedRowKeys } =
+      //   useRowSelection(getProps, tableData, emit);
+
+      // 子级列名
+      const childrenColumnName = computed(() => getProps.value.childrenColumnName || 'children');
+
+      // 自定义选择列
+      const {
+        getRowSelection,
+        getSelectRows,
+        getSelectRowKeys,
+        setSelectedRowKeys,
+        getRowSelectionRef,
+        selectHeaderProps,
+        isCustomSelection,
+        handleCustomSelectColumn,
+        clearSelectedRowKeys,
+        deleteSelectRowByKey,
+        getExpandIconColumnIndex,
+      } = useCustomSelection(
+        getProps,
+        emit,
+        wrapRef,
+        getPaginationInfo,
+        tableData,
+        childrenColumnName
+      )
+      // update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
 
       const {
         handleTableChange: onTableChange,
@@ -161,7 +197,10 @@
 
       const { getViewColumns, getColumns, setCacheColumnsByField, setColumns, getColumnsRef, getCacheColumns } = useColumns(
         getProps,
-        getPaginationInfo
+        getPaginationInfo,
+        // update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
+        handleCustomSelectColumn,
+        // update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
       );
 
       const { getScrollRef, redoHeight } = useTableScroll(getProps, tableElRef, getColumnsRef, getRowSelectionRef, getDataSourceRef);
@@ -188,7 +227,7 @@
 
       const { getHeaderProps } = useTableHeader(getProps, slots, handlers);
 
-      const { getFooterProps } = useTableFooter(getProps, getScrollRef, tableElRef, getDataSourceRef);
+      const { getFooterProps } = useTableFooter(getProps, slots, getScrollRef, tableElRef, getDataSourceRef);
 
       const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } = useTableForm(getProps, slots, fetch, getLoading);
 
@@ -212,16 +251,35 @@
           dataSource,
           footer: unref(getFooterProps),
           ...unref(getExpandOption),
+          // 【QQYUN-5837】动态计算 expandIconColumnIndex
+          expandIconColumnIndex: getExpandIconColumnIndex.value,
         };
-        if (slots.expandedRowRender) {
-          propsData = omit(propsData, 'scroll');
-        }
 
+        //update-begin---author:wangshuai ---date:20230214  for:[QQYUN-4237]代码生成 内嵌子表模式 没有滚动条------------
+        //额外的展开行存在插槽时会将滚动移除掉,注释掉
+        /*if (slots.expandedRowRender) {
+          propsData = omit(propsData, 'scroll');
+        }*/
+        //update-end---author:wangshuai ---date:20230214  for:[QQYUN-4237]代码生成 内嵌子表模式 没有滚动条------------ 
+
+        // update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
+        // 自定义选择列,需要去掉原生的
+        delete propsData.rowSelection
+        // update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
+
+        // update-begin--author:liaozhiyang---date:20230919---for:【QQYUN-6387】展开写法(去掉报错)
+        !propsData.isTreeTable && delete propsData.expandIconColumnIndex;
+        propsData.expandedRowKeys === null && delete propsData.expandedRowKeys;
+        // update-end--author:liaozhiyang---date:20230919---for:【QQYUN-6387】展开写法(去掉报错)
         propsData = omit(propsData, ['class', 'onChange']);
         return propsData;
       });
 
-      console.log(777, getBindValues);
+      // 统一设置表格列宽度
+      const getMaxColumnWidth = computed(() => {
+        const values = unref(getBindValues);
+        return values.maxColumnWidth > 0 ? values.maxColumnWidth + 'px' : null;
+      });
 
       const getWrapperClass = computed(() => {
         const values = unref(getBindValues);
@@ -231,6 +289,9 @@
           {
             [`${prefixCls}-form-container`]: values.useSearchForm,
             [`${prefixCls}--inset`]: values.inset,
+            [`${prefixCls}-col-max-width`]: getMaxColumnWidth.value != null,
+            // 是否显示表尾合计
+            [`${prefixCls}--show-summary`]: values.showSummary,
           },
         ];
       });
@@ -283,10 +344,70 @@
       };
       createTableContext({ ...tableAction, wrapRef, getBindValues });
 
+      // update-begin--author:sunjianlei---date:220230718---for:【issues/179】兼容新老slots写法,移除控制台警告
+      // 获取分组之后的slot名称
+      const slotNamesGroup = computed<{
+        // AntTable原生插槽
+        native: string[];
+        // 列自定义插槽
+        custom: string[];
+      }>(() => {
+        const native: string[] = [];
+        const custom: string[] = [];
+        const columns = unref<Recordable[]>(getViewColumns) as BasicColumn[];
+        const allCustomRender = columns.map<string>((column) => column.slotsBak?.customRender);
+        for (const name of Object.keys(slots)) {
+          // 过滤特殊的插槽
+          if (['bodyCell'].includes(name)) {
+            continue;
+          }
+          if (allCustomRender.includes(name)) {
+            custom.push(name);
+          } else {
+            native.push(name);
+          }
+        }
+        return { native, custom };
+      });
+      // update-end--author:sunjianlei---date:220230718---for:【issues/179】兼容新老slots写法,移除控制台警告
+
       expose(tableAction);
 
       emit('register', tableAction, formActions);
 
+      // update-begin--author:liaozhiyang---date:20230804---for:【issues/638】表格合计,列表table和合计table滚动联动
+      if (getProps.value.showSummary) {
+        let tableBody;
+        const handleSroll = function () {
+          const scrollLeft = this.scrollLeft;
+          tableBody.forEach((elem) => (elem.scrollLeft = scrollLeft));
+        };
+        onMounted(() => {
+          const unwatch = watch(
+            getDataSourceRef,
+            (newVal) => {
+              if (newVal.length > 0) {
+                setTimeout(() => {
+                  unwatch();
+                  tableBody = wrapRef.value.querySelectorAll('.ant-table-body');
+                  tableBody.forEach((elem) => {
+                    elem.addEventListener('scroll', handleSroll, false);
+                  });
+                }, 0);
+                console.log("---表格合计滚动---")
+              }
+            },
+            { immediate: true }
+          );
+        });
+        onUnmounted(() => {
+          if (tableBody.length) {
+            tableBody.forEach((elem) => elem.removeEventListener('scroll', handleSroll));
+          }
+        });
+      }
+      // update-begin--author:liaozhiyang---date:20230804---for:【issues/638】表格合计,列表table和合计table滚动联动
+
       return {
         tableElRef,
         getBindValues,
@@ -306,7 +427,14 @@
         replaceFormSlotKey,
         getFormSlotKeys,
         getWrapperClass,
+        getMaxColumnWidth,
         columns: getViewColumns,
+
+        // update-begin--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
+        selectHeaderProps,
+        isCustomSelection,
+        // update-end--author:sunjianlei---date:220230630---for:【QQYUN-5571】自封装选择列,解决数据行选择卡顿问题
+        slotNamesGroup,
       };
     },
   });

+ 56 - 0
src/components/Table/src/components/CustomSelectHeader.vue

@@ -0,0 +1,56 @@
+<!-- 自定义选择列,表头实现部分 -->
+<template>
+  <template v-if="isRadio">
+    <!-- radio不存在全选,所以放个空标签 -->
+    <span></span>
+  </template>
+  <a-checkbox :disabled="disabled" v-else :checked="checked" :indeterminate="isHalf" @update:checked="onChange" />
+</template>
+<script setup lang="ts">
+  import { computed } from 'vue';
+
+  const props = defineProps({
+    isRadio: {
+      type: Boolean,
+      required: true,
+    },
+    selectedLength: {
+      type: Number,
+      required: true,
+    },
+    // 当前页条目数
+    pageSize: {
+      type: Number,
+      required: true,
+    },
+    // update-begin--author:liaozhiyang---date:20231016---for:【QQYUN-6774】解决checkbox禁用后全选仍能勾选问题
+    disabled: {
+      type: Boolean,
+      required: true,
+    },
+    // update-end--author:liaozhiyang---date:20231016---for:【QQYUN-6774】解决checkbox禁用后全选仍能勾选问题
+  });
+  const emit = defineEmits(['select-all']);
+
+  // 是否全选
+  const checked = computed(() => {
+    if (props.isRadio) {
+      return false;
+    }
+    return props.selectedLength > 0 && props.selectedLength >= props.pageSize;
+  });
+
+  // 是否半选
+  const isHalf = computed(() => {
+    if (props.isRadio) {
+      return false;
+    }
+    return props.selectedLength > 0 && props.selectedLength < props.pageSize;
+  });
+
+  function onChange(checked: boolean) {
+    emit('select-all', checked);
+  }
+</script>
+
+<style scoped lang="scss"></style>

+ 9 - 1
src/components/Table/src/components/TableAction.vue

@@ -21,6 +21,11 @@
     </template>
     <Dropdown :trigger="['hover']" :dropMenuList="getDropdownList" popconfirm v-if="dropDownActions && getDropdownList.length > 0">
       <slot name="more"></slot>
+      <!--  设置插槽   -->
+      <template v-slot:[item.slot] v-for="(item, index) in getDropdownSlotList" :key="`${index}-${item.label}`">
+        <slot :name="item.slot"></slot>
+      </template>
+
       <a-button type="link" size="small" v-if="!$slots.more"> 更多 <Icon icon="mdi-light:chevron-down"></Icon> </a-button>
     </Dropdown>
   </div>
@@ -116,6 +121,9 @@
         });
       });
 
+      const getDropdownSlotList = computed((): any[] => {
+        return unref(getDropdownList).filter((item) => item.slot);
+      });
       const getAlign = computed(() => {
         const columns = (table as TableActionType)?.getColumns?.() || [];
         const actionColumn = columns.find((item) => item.flag === ACTION_COLUMN_FLAG);
@@ -139,7 +147,7 @@
         isInButton && e.stopPropagation();
       }
 
-      return { prefixCls, getActions, getDropdownList, getAlign, onCellClick, getTooltip };
+      return { prefixCls, getActions, getDropdownList, getDropdownSlotList, getAlign, onCellClick, getTooltip };
     },
   });
 </script>

+ 44 - 6
src/components/Table/src/components/TableFooter.vue

@@ -2,7 +2,7 @@
   <Table
     v-if="summaryFunc || summaryData"
     :showHeader="false"
-    :bordered="false"
+    :bordered="bordered"
     :pagination="false"
     :dataSource="getDataSource"
     :rowKey="(r) => r[rowKey]"
@@ -28,6 +28,10 @@
     name: 'BasicTableFooter',
     components: { Table },
     props: {
+      bordered: {
+        type: Boolean,
+        default: false,
+      },
       summaryFunc: {
         type: Function as PropType<Fn>,
       },
@@ -38,6 +42,8 @@
         type: Object as PropType<Recordable>,
       },
       rowKey: propTypes.string.def('key'),
+      // 是否有展开列
+      hasExpandedRow: propTypes.bool,
     },
     setup(props) {
       const table = useTableContext();
@@ -61,13 +67,22 @@
 
       const getColumns = computed(() => {
         const dataSource = unref(getDataSource);
-        const columns: BasicColumn[] = cloneDeep(table.getColumns());
+        let columns: BasicColumn[] = cloneDeep(table.getColumns());
+        // update-begin--author:liaozhiyang---date:220230804---for:【issues/638】表格合计,列自定义隐藏或展示时,合计栏会错位
+        columns = columns.filter((item) => !item.defaultHidden);
+        // update-begin--author:liaozhiyang---date:220230804---for:【issues/638】表格合计,列自定义隐藏或展示时,合计栏会错位
         const index = columns.findIndex((item) => item.flag === INDEX_COLUMN_FLAG);
         const hasRowSummary = dataSource.some((item) => Reflect.has(item, SUMMARY_ROW_KEY));
         const hasIndexSummary = dataSource.some((item) => Reflect.has(item, SUMMARY_INDEX_KEY));
 
+        // 是否有序号列
+        let hasIndexCol = false;
+        // 是否有选择列
+        let hasSelection = table.getRowSelection() && hasRowSummary
+
         if (index !== -1) {
           if (hasIndexSummary) {
+            hasIndexCol = true;
             columns[index].customRender = ({ record }) => record[SUMMARY_INDEX_KEY];
             columns[index].ellipsis = false;
           } else {
@@ -75,15 +90,29 @@
           }
         }
 
-        if (table.getRowSelection() && hasRowSummary) {
-          const isFixed = columns.some((col) => col.fixed === 'left');
+        if (hasSelection) {
+          // update-begin--author:liaozhiyang---date:20231009---for:【issues/776】显示100条/页,复选框只能显示3个的问题(fixed也有可能设置true)
+          const isFixed = columns.some((col) => col.fixed === 'left' || col.fixed === true);
+          // update-begin--author:liaozhiyang---date:20231009---for:【issues/776】显示100条/页,复选框只能显示3个的问题(fixed也有可能设置true)
           columns.unshift({
-            width: 60,
+            width: 50,
             title: 'selection',
             key: 'selectionKey',
             align: 'center',
             ...(isFixed ? { fixed: 'left' } : {}),
-            customRender: ({ record }) => record[SUMMARY_ROW_KEY],
+            customRender: ({ record }) => hasIndexCol ? '' : record[SUMMARY_ROW_KEY],
+          });
+        }
+
+        if (props.hasExpandedRow) {
+          const isFixed = columns.some((col) => col.fixed === 'left');
+          columns.unshift({
+            width: 50,
+            title: 'expandedRow',
+            key: 'expandedRowKey',
+            align: 'center',
+            ...(isFixed ? { fixed: 'left' } : {}),
+            customRender: () => '',
           });
         }
         return columns;
@@ -92,3 +121,12 @@
     },
   });
 </script>
+<style lang="less" scoped>
+  // update-begin--author:liaozhiyang---date:20231009---for:【issues/776】显示100条/页,复选框只能显示3个的问题(隐藏合计的滚动条)
+  .vent-table-wrapper {
+    :deep(.vent-table-body) {
+      overflow-x: hidden !important;
+    }
+  }
+  // update-end--author:liaozhiyang---date:20231009---for:【issues/776】显示100条/页,复选框只能显示3个的问题(隐藏合计的滚动条)
+</style>

+ 1 - 1
src/components/Table/src/components/editable/EditableCell.vue

@@ -190,7 +190,7 @@
           currentValueRef.value = (e as ChangeEvent).target.value;
         } else if (component === 'Checkbox') {
           currentValueRef.value = (e as ChangeEvent).target.checked;
-        } else if (isString(e) || isBoolean(e) || isNumber(e)) {
+        } else if (isString(e) || isBoolean(e) || isNumber(e) || isArray(e)) {
           currentValueRef.value = e;
         }
         const onChange = props.column?.editComponentProps?.onChange;

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov