Browse Source

重大版本发布,1.3.0版本源码上传(全功能趋于稳定健壮)

zhangdaiscott 2 years ago
parent
commit
5aec20bfa0
100 changed files with 1479 additions and 493 deletions
  1. 1 0
      .gitignore
  2. 10 8
      package.json
  3. 1 0
      prettier.config.js
  4. 46 0
      src/api/common/api.ts
  5. 1 1
      src/api/sys/user.ts
  6. BIN
      src/assets/images/panel_cover.png
  7. 2 2
      src/components/Button/index.ts
  8. 41 0
      src/components/Button/src/JUploadButton.vue
  9. 2 2
      src/components/Button/src/props.ts
  10. 4 1
      src/components/Cropper/src/CopperModal.vue
  11. 1 0
      src/components/Form/src/BasicForm.vue
  12. 0 2
      src/components/Form/src/componentMap.ts
  13. 3 2
      src/components/Form/src/components/FormAction.vue
  14. 3 2
      src/components/Form/src/hooks/useForm.ts
  15. 5 1
      src/components/Form/src/jeecg/components/JAddInput.vue
  16. 18 1
      src/components/Form/src/jeecg/components/JAreaLinkage.vue
  17. 4 3
      src/components/Form/src/jeecg/components/JAreaSelect.vue
  18. 13 1
      src/components/Form/src/jeecg/components/JCodeEditor.vue
  19. 10 1
      src/components/Form/src/jeecg/components/JEasyCron/EasyCronInner.vue
  20. 1 1
      src/components/Form/src/jeecg/components/JEllipsis.vue
  21. 60 0
      src/components/Form/src/jeecg/components/JFormContainer.vue
  22. 1 1
      src/components/Form/src/jeecg/components/JImageUpload.vue
  23. 3 1
      src/components/Form/src/jeecg/components/JInputPop.vue
  24. 0 220
      src/components/Form/src/jeecg/components/JOnlineSelectCascade.vue
  25. 5 3
      src/components/Form/src/jeecg/components/JPopup.vue
  26. 24 14
      src/components/Form/src/jeecg/components/JSearchSelect.vue
  27. 12 7
      src/components/Form/src/jeecg/components/JSelectDept.vue
  28. 21 3
      src/components/Form/src/jeecg/components/JSelectMultiple.vue
  29. 7 1
      src/components/Form/src/jeecg/components/JSelectRole.vue
  30. 4 1
      src/components/Form/src/jeecg/components/JSelectUser.vue
  31. 1 1
      src/components/Form/src/jeecg/components/JSwitch.vue
  32. 17 1
      src/components/Form/src/jeecg/components/JTreeSelect.vue
  33. 6 0
      src/components/Form/src/jeecg/components/JUpload/JUpload.vue
  34. 7 1
      src/components/Form/src/jeecg/components/base/JSelectBiz.vue
  35. 13 2
      src/components/Form/src/jeecg/components/modal/DeptSelectModal.vue
  36. 21 3
      src/components/Form/src/jeecg/components/modal/JPopupOnlReportModal.vue
  37. 16 3
      src/components/Form/src/jeecg/components/modal/PositionSelectModal.vue
  38. 10 3
      src/components/Form/src/jeecg/components/modal/RoleSelectModal.vue
  39. 9 2
      src/components/Form/src/jeecg/components/modal/UserSelectByDepModal.vue
  40. 54 6
      src/components/Form/src/jeecg/components/modal/UserSelectModal.vue
  41. 19 3
      src/components/Form/src/jeecg/hooks/useSelectBiz.ts
  42. 7 0
      src/components/Form/src/jeecg/props/props.ts
  43. 5 1
      src/components/Form/src/types/form.ts
  44. 0 1
      src/components/Form/src/types/index.ts
  45. 5 0
      src/components/Form/src/utils/Area.ts
  46. 7 1
      src/components/Form/src/utils/formUtils.ts
  47. 2 1
      src/components/Icon/src/IconPicker.vue
  48. 27 24
      src/components/JVxeCustom/src/components/JVxeDepartSelectCell.vue
  49. 20 3
      src/components/JVxeCustom/src/components/JVxeUserSelectCell.vue
  50. 46 0
      src/components/Markdown/src/Markdown.vue
  51. 4 2
      src/components/Menu/src/BasicMenu.vue
  52. 1 1
      src/components/Menu/src/components/BasicMenuItem.vue
  53. 21 0
      src/components/SimpleMenu/src/components/MenuItem.vue
  54. 28 22
      src/components/Table/src/components/TableAction.vue
  55. 1 1
      src/components/Table/src/components/TableHeader.vue
  56. 78 23
      src/components/Table/src/components/settings/ColumnSetting.vue
  57. 11 16
      src/components/Table/src/hooks/useColumns.ts
  58. 137 0
      src/components/Table/src/hooks/useColumnsCache.ts
  59. 2 1
      src/components/Table/src/hooks/useTableHeader.ts
  60. 6 0
      src/components/Table/src/types/table.ts
  61. 2 2
      src/components/Tinymce/src/Editor.vue
  62. 24 4
      src/components/Tinymce/src/ImgUpload.vue
  63. 3 1
      src/components/jeecg/JVxeTable/src/components/JVxeDetailsModal.vue
  64. 11 1
      src/components/jeecg/JVxeTable/src/components/JVxeSubPopover.vue
  65. 3 1
      src/components/jeecg/JVxeTable/src/components/JVxeToolbar.vue
  66. 7 2
      src/components/jeecg/JVxeTable/src/components/cells/JVxeTextareaCell.vue
  67. 7 1
      src/components/jeecg/JVxeTable/src/hooks/useColumns.ts
  68. 1 1
      src/components/jeecg/JVxeTable/src/hooks/useDataSource.ts
  69. 23 0
      src/components/jeecg/JVxeTable/src/hooks/useJVxeComponent.ts
  70. 7 0
      src/components/jeecg/JVxeTable/src/hooks/useMethods.ts
  71. 12 2
      src/components/jeecg/JVxeTable/src/hooks/useValidateRules.ts
  72. 9 1
      src/components/jeecg/JVxeTable/src/utils/authUtils.ts
  73. 2 2
      src/components/jeecg/JVxeTable/src/utils/registerUtils.ts
  74. 3 1
      src/components/jeecg/OnLine/JPopupOnlReport.vue
  75. 10 1
      src/components/jeecg/OnLine/SearchFormItem.vue
  76. 67 5
      src/components/jeecg/OnLine/hooks/usePopBiz.ts
  77. 9 3
      src/components/registerGlobComp.ts
  78. 3 1
      src/hooks/event/useEventListener.ts
  79. 93 1
      src/hooks/system/useJvxeMethods.ts
  80. 29 9
      src/hooks/system/useListPage.ts
  81. 5 1
      src/hooks/system/useThirdLogin.ts
  82. 59 1
      src/hooks/web/usePermission.ts
  83. 8 1
      src/layouts/default/header/components/notify/NoticeList.vue
  84. 13 2
      src/layouts/default/header/components/notify/index.vue
  85. 4 1
      src/layouts/default/header/components/user-dropdown/DepartSelect.vue
  86. 8 1
      src/layouts/default/header/components/user-dropdown/index.vue
  87. 3 10
      src/layouts/default/header/index.vue
  88. 8 2
      src/layouts/default/menu/index.vue
  89. 4 4
      src/layouts/default/setting/components/SettingFooter.vue
  90. 9 1
      src/layouts/default/tabs/components/TabContent.vue
  91. 1 1
      src/locales/lang/zh-CN/sys.ts
  92. 12 0
      src/store/modules/locale.ts
  93. 15 0
      src/store/modules/permission.ts
  94. 17 6
      src/store/modules/user.ts
  95. 21 8
      src/utils/auth/index.ts
  96. 10 0
      src/utils/common/compUtils.ts
  97. 38 7
      src/utils/common/renderUtils.ts
  98. 15 1
      src/utils/common/vxeUtils.ts
  99. 12 5
      src/utils/dict/JDictSelectUtil.js
  100. 8 1
      src/utils/dict/index.ts

+ 1 - 0
.gitignore

@@ -31,3 +31,4 @@ pnpm-debug.log*
 /os_del.cmd
 /.vscode/
 /.history/
+/svn clear.bat

+ 10 - 8
package.json

@@ -1,6 +1,6 @@
 {
   "name": "jeecgboot-vue3",
-  "version": "1.2.0",
+  "version": "1.3.0",
   "author": {
     "name": "jeecg",
     "email": "jeecgos@163.com",
@@ -36,11 +36,12 @@
   "dependencies": {
     "@jeecg/online": "1.0.1",
     "@iconify/iconify": "^2.0.4",
-    "@fullcalendar/core": "^5.10.1",
-    "@fullcalendar/daygrid": "^5.10.1",
-    "@fullcalendar/interaction": "^5.10.1",
-    "@fullcalendar/timegrid": "^5.10.1",
-    "@fullcalendar/vue3": "^5.10.1",
+    "@fullcalendar/core": "5.10.1",
+    "@fullcalendar/daygrid": "5.10.1",
+    "@fullcalendar/interaction": "5.10.1",
+    "@fullcalendar/resource-timeline": "5.10.1",
+    "@fullcalendar/timegrid": "5.10.1",
+    "@fullcalendar/vue3": "5.10.1",
     "@vueuse/core": "^6.6.2",
     "@zxcvbn-ts/core": "^1.0.0-beta.0",
     "ant-design-vue": "2.2.8",
@@ -66,6 +67,7 @@
     "path-to-regexp": "^6.2.0",
     "pinia": "2.0.0-rc.14",
     "print-js": "^1.6.0",
+    "qiankun": "^2.5.1",
     "qrcode": "^1.4.4",
     "qrcodejs2": "0.0.2",
     "resize-observer-polyfill": "^1.5.1",
@@ -81,12 +83,11 @@
     "vue-print-nb-jeecg": "^1.0.10",
     "vue-router": "^4.0.12",
     "vue-types": "^4.1.1",
+    "vuedraggable": "^4.1.0",
     "vxe-table": "4.1.0",
     "vxe-table-plugin-antd": "^3.0.3",
     "xe-utils": "^3.3.1",
     "xlsx": "^0.17.3",
-    "qiankun": "^2.5.1",
-    "vuedraggable": "^4.1.0",
     "vue-json-pretty": "^2.0.4"
   },
   "devDependencies": {
@@ -291,6 +292,7 @@
         "vue-print-nb-jeecg/src/printarea",
         "vue-router",
         "vue-types",
+        "vuedraggable",
         "vxe-table",
         "vxe-table-plugin-antd",
         "xe-utils",

+ 1 - 0
prettier.config.js

@@ -8,6 +8,7 @@ module.exports = {
   quoteProps: 'as-needed',
   bracketSpacing: true,
   trailingComma: 'es5',
+  jsxBracketSameLine: false,
   jsxSingleQuote: false,
   arrowParens: 'always',
   insertPragma: false,

+ 46 - 0
src/api/common/api.ts

@@ -1,4 +1,5 @@
 import { defHttp } from '/@/utils/http/axios';
+import { message } from 'ant-design-vue';
 import { useGlobSetting } from '/@/hooks/setting';
 const globSetting = useGlobSetting();
 const baseUploadUrl = globSetting.uploadUrl;
@@ -95,3 +96,48 @@ export const loadCategoryData = (params) => {
 export const uploadFile = (params, success) => {
   return defHttp.uploadFile({ url: uploadUrl }, params, { success });
 };
+/**
+ * 下载文件
+ * @param url 文件路径
+ * @param fileName 文件名
+ * @param parameter
+ * @returns {*}
+ */
+export const downloadFile = (url, fileName?, parameter?) => {
+  return getFileblob(url, parameter).then((data) => {
+    if (!data || data.size === 0) {
+      message.warning('文件下载失败');
+      return;
+    }
+    if (typeof window.navigator.msSaveBlob !== 'undefined') {
+      window.navigator.msSaveBlob(new Blob([data]), fileName);
+    } else {
+      let url = window.URL.createObjectURL(new Blob([data]));
+      let link = document.createElement('a');
+      link.style.display = 'none';
+      link.href = url;
+      link.setAttribute('download', fileName);
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link); //下载完成移除元素
+      window.URL.revokeObjectURL(url); //释放掉blob对象
+    }
+  });
+};
+
+/**
+ * 下载文件 用于excel导出
+ * @param url
+ * @param parameter
+ * @returns {*}
+ */
+export const getFileblob = (url, parameter) => {
+  return defHttp.get(
+    {
+      url: url,
+      params: parameter,
+      responseType: 'blob',
+    },
+    { isTransformResponse: false }
+  );
+};

+ 1 - 1
src/api/sys/user.ts

@@ -80,7 +80,7 @@ export function phoneLoginApi(params: LoginParams, mode: ErrorMessageMode = 'mod
 export function getUserInfo() {
   return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' }).catch((e) => {
     // update-begin--author:zyf---date:20220425---for:【VUEN-76】捕获接口超时异常,跳转到登录界面
-    if (e && e.message.includes('timeout')) {
+    if (e && (e.message.includes('timeout') || e.message.includes('401'))) {
       //接口不通时跳转到登录界面
       const userStore = useUserStoreWithOut();
       userStore.setToken('');

BIN
src/assets/images/panel_cover.png


+ 2 - 2
src/components/Button/index.ts

@@ -1,11 +1,11 @@
 import { withInstall } from '/@/utils';
 import type { ExtractPropTypes } from 'vue';
 import button from './src/BasicButton.vue';
-import uploadButton from './src/UploadButton.vue';
+import jUploadButton from './src/JUploadButton.vue';
 import popConfirmButton from './src/PopConfirmButton.vue';
 import { buttonProps } from './src/props';
 
 export const Button = withInstall(button);
-export const UploadButton = withInstall(uploadButton);
+export const JUploadButton = withInstall(jUploadButton);
 export const PopConfirmButton = withInstall(popConfirmButton);
 export declare type ButtonProps = Partial<ExtractPropTypes<typeof buttonProps>>;

+ 41 - 0
src/components/Button/src/JUploadButton.vue

@@ -0,0 +1,41 @@
+<template>
+  <a-upload name="file" :showUploadList="false" :customRequest="(file) => onClick(file)">
+    <Button :type="type" :class="getButtonClass">
+      <template #default="data">
+        <Icon :icon="preIcon" v-if="preIcon" :size="iconSize" />
+        <slot v-bind="data || {}"></slot>
+        <Icon :icon="postIcon" v-if="postIcon" :size="iconSize" />
+      </template>
+    </Button>
+  </a-upload>
+</template>
+
+<script lang="ts">
+  import { defineComponent } from 'vue';
+  export default defineComponent({
+    name: 'JUploadButton',
+    inheritAttrs: false,
+  });
+</script>
+<script lang="ts" setup>
+  import { computed, unref } from 'vue';
+  import { Button } from 'ant-design-vue';
+  import Icon from '/@/components/Icon/src/Icon.vue';
+  import { buttonProps } from './props';
+  import { useAttrs } from '/@/hooks/core/useAttrs';
+  const props = defineProps(buttonProps);
+  // get component class
+  const attrs = useAttrs({ excludeDefaultKeys: false });
+  const getButtonClass = computed(() => {
+    const { color, disabled } = props;
+    return [
+      {
+        [`ant-btn-${color}`]: !!color,
+        [`is-disabled`]: disabled,
+      },
+    ];
+  });
+
+  // get inherit binding value
+  const getBindValue = computed(() => ({ ...unref(attrs), ...props }));
+</script>

+ 2 - 2
src/components/Button/src/props.ts

@@ -13,9 +13,9 @@ export const buttonProps = {
   type: { type: String },
   /**
    * preIcon and postIcon icon size.
-   * @default: 14
+   * @default: 15
    */
-  iconSize: { type: Number, default: 14 },
+  iconSize: { type: Number, default: 15 },
   isUpload: { type: Boolean, default: false },
   onClick: { type: Function as PropType<(...args) => any>, default: null },
 };

+ 4 - 1
src/components/Cropper/src/CopperModal.vue

@@ -129,7 +129,10 @@
           try {
             setModalProps({ confirmLoading: true });
             const result = await uploadApi({ name: 'file', file: blob, filename });
-            emit('uploadSuccess', { source: previewSource.value, data: result.data || result.message });
+            emit('uploadSuccess', {
+              source: previewSource.value,
+              data: result.data || result.message,
+            });
             closeModal();
           } finally {
             setModalProps({ confirmLoading: false });

+ 1 - 0
src/components/Form/src/BasicForm.vue

@@ -233,6 +233,7 @@
         updateSchema,
         resetSchema,
         setProps,
+        getProps,
         removeSchemaByFiled,
         appendSchemaByField,
         clearValidate,

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

@@ -42,7 +42,6 @@ import JUpload from './jeecg/components/JUpload/JUpload.vue';
 import JSearchSelect from './jeecg/components/JSearchSelect.vue';
 import JAddInput from './jeecg/components/JAddInput.vue';
 import { Time } from '/@/components/Time';
-import JOnlineSelectCascade from './jeecg/components/JOnlineSelectCascade.vue';
 import JRangeNumber from './jeecg/components/JRangeNumber.vue';
 
 const componentMap = new Map<ComponentType, Component>();
@@ -110,7 +109,6 @@ componentMap.set('JSelectUserByDept', JSelectUserByDept);
 componentMap.set('JUpload', JUpload);
 componentMap.set('JSearchSelect', JSearchSelect);
 componentMap.set('JAddInput', JAddInput);
-componentMap.set('JOnlineSelectCascade', JOnlineSelectCascade);
 componentMap.set('JRangeNumber', JRangeNumber);
 
 export function add(compName: ComponentType, component: Component) {

+ 3 - 2
src/components/Form/src/components/FormAction.vue

@@ -5,13 +5,11 @@
         <!-- update-begin-author:zyf   Date:20211213  for:调换按钮前后位置-->
         <slot name="submitBefore"></slot>
         <Button type="primary" class="mr-2" v-bind="getSubmitBtnOptions" @click="submitAction" v-if="showSubmitButton">
-          <Icon icon="ant-design:search-outlined"></Icon>
           {{ getSubmitBtnOptions.text }}
         </Button>
 
         <slot name="resetBefore"></slot>
         <Button type="default" class="mr-2" v-bind="getResetBtnOptions" @click="resetAction" v-if="showResetButton">
-          <Icon icon="ic:baseline-restart-alt"></Icon>
           {{ getResetBtnOptions.text }}
         </Button>
         <!-- update-end-author:zyf    Date:20211213  for:调换按钮前后位置-->
@@ -89,6 +87,7 @@
         return Object.assign(
           {
             text: t('common.resetText'),
+            preIcon: 'ic:baseline-restart-alt',
           },
           props.resetButtonOptions
         );
@@ -96,8 +95,10 @@
 
       const getSubmitBtnOptions = computed(() => {
         return Object.assign(
+          {},
           {
             text: t('common.queryText'),
+            preIcon: 'ant-design:search-outlined',
           },
           props.submitButtonOptions
         );

+ 3 - 2
src/components/Form/src/hooks/useForm.ts

@@ -110,17 +110,18 @@ export function useForm(props?: Props): UseFormReturnType {
      */
     validate: async (nameList?: NamePath[]): Promise<Recordable> => {
       const form = await getForm();
+      let getProps = props || form.getProps;
       let values = form.validate(nameList).then((values) => {
         for (let key in values) {
           if (values[key] instanceof Array) {
-            let valueType = getValueType(props, key);
+            let valueType = getValueType(getProps, key);
             if (valueType === 'string') {
               values[key] = values[key].join(',');
             }
           }
         }
         //--@updateBy-begin----author:liusq---date:20210916------for:处理区域事件字典信息------
-        return handleRangeValue(props, values);
+        return handleRangeValue(getProps, values);
         //--@updateBy-end----author:liusq---date:20210916------for:处理区域事件字典信息------
       });
       return values;

+ 5 - 1
src/components/Form/src/jeecg/components/JAddInput.vue

@@ -2,7 +2,7 @@
   <div v-for="(param, index) in dynamicInput.params" :key="index" style="display: flex">
     <a-input placeholder="请输入参数key" v-model:value="param.label" style="width: 30%; margin-bottom: 5px" @input="emitChange" />
     <a-input placeholder="请输入参数value" v-model:value="param.value" style="width: 30%; margin: 0 0 5px 5px" @input="emitChange" />
-    <MinusCircleOutlined v-if="dynamicInput.params.length > 1" class="dynamic-delete-button" @click="remove(param)" style="width: 50px"></MinusCircleOutlined>
+    <MinusCircleOutlined v-if="dynamicInput.params.length > min" class="dynamic-delete-button" @click="remove(param)" style="width: 50px"></MinusCircleOutlined>
   </div>
   <div>
     <a-button type="dashed" style="width: 60%" @click="add">
@@ -26,6 +26,10 @@
     name: 'JAddInput',
     props: {
       value: propTypes.string.def(''),
+      //update-begin---author:wangshuai ---date:20220516  for:[VUEN-1043]系统编码规则,最后一个输入框不能删除------------
+      //自定义删除按钮多少才会显示
+      min: propTypes.integer.def(1),
+      //update-end---author:wangshuai ---date:20220516  for:[VUEN-1043]系统编码规则,最后一个输入框不能删除--------------
     },
     emits: ['change', 'update:value'],
     setup(props, { emit }) {

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

@@ -1,5 +1,5 @@
 <template>
-  <Cascader v-bind="attrs" v-model:value="state" :options="getOptions" @change="handleChange" />
+  <Cascader v-bind="attrs" :value="state" :options="getOptions" @change="handleChange" />
 </template>
 <script lang="ts">
   import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted } from 'vue';
@@ -40,6 +40,23 @@
           return provinceAndCityDataPlus;
         }
       });
+      /**
+       * 监听value变化
+       */
+      watchEffect(() => {
+        props.value && initValue();
+      });
+
+      /**
+       * 将字符串值转化为数组
+       */
+      function initValue() {
+        let value = props.value ? props.value : [];
+        if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
+          state.value = value.split(',');
+        }
+      }
+
       function handleChange(_, ...args) {
         emitData.value = args;
         console.info(emitData);

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

@@ -1,19 +1,19 @@
 <template>
   <div class="area-select">
     <!--省份-->
-    <a-select v-model:value="province" @change="proChange" allowClear>
+    <a-select v-model:value="province" @change="proChange" allowClear :disabled="disabled">
       <template v-for="item in provinceOptions" :key="`${item.value}`">
         <a-select-option :value="item.value">{{ item.label }}</a-select-option>
       </template>
     </a-select>
     <!--城市-->
-    <a-select v-if="level >= 2" v-model:value="city" @change="cityChange">
+    <a-select v-if="level >= 2" v-model:value="city" @change="cityChange" :disabled="disabled">
       <template v-for="item in cityOptions" :key="`${item.value}`">
         <a-select-option :value="item.value">{{ item.label }}</a-select-option>
       </template>
     </a-select>
     <!--地区-->
-    <a-select v-if="level >= 3" v-model:value="area" @change="areaChange">
+    <a-select v-if="level >= 3" v-model:value="area" @change="areaChange" :disabled="disabled">
       <template v-for="item in areaOptions" :key="`${item.value}`">
         <a-select-option :value="item.value">{{ item.label }}</a-select-option>
       </template>
@@ -34,6 +34,7 @@
       city: [String],
       area: [String],
       level: propTypes.number.def(3),
+      disabled: propTypes.bool.def(false),
     },
     emits: ['change', 'update:value'],
     setup(props, { emit, refs }) {

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

@@ -96,7 +96,8 @@
           },
         },
       });
-      let innerValue = '';
+      // 内部存储值,初始为 props.value
+      let innerValue = props.value ?? '';
       // 全屏状态
       const isFullScreen = ref(false);
       const fullScreenIcon = computed(() => (isFullScreen.value ? 'fullscreen-exit' : 'fullscreen'));
@@ -175,6 +176,17 @@
         isFullScreen.value = !isFullScreen.value;
       }
 
+      //update-begin-author:taoyan date:2022-5-9 for: codeEditor禁用功能
+      watch(
+        () => props.disabled,
+        (val) => {
+          if (coder) {
+            coder.setOption('readOnly', val);
+          }
+        }
+      );
+      //update-end-author:taoyan date:2022-5-9 for: codeEditor禁用功能
+
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
       return {
         state,

+ 10 - 1
src/components/Form/src/jeecg/components/JEasyCron/EasyCronInner.vue

@@ -122,7 +122,16 @@
   const month = ref('*');
   const week = ref('?');
   const year = ref('*');
-  const inputValues = reactive({ second: '', minute: '', hour: '', day: '', month: '', week: '', year: '', cron: '' });
+  const inputValues = reactive({
+    second: '',
+    minute: '',
+    hour: '',
+    day: '',
+    month: '',
+    week: '',
+    year: '',
+    cron: '',
+  });
   const preTimeList = ref('执行预览,会忽略年份参数。');
 
   // cron表达式

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

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

+ 60 - 0
src/components/Form/src/jeecg/components/JFormContainer.vue

@@ -0,0 +1,60 @@
+<template>
+  <div :class="disabled ? 'jeecg-form-container-disabled' : ''">
+    <fieldset :disabled="disabled">
+      <slot name="detail"></slot>
+    </fieldset>
+    <slot name="edit"></slot>
+    <fieldset disabled>
+      <slot></slot>
+    </fieldset>
+  </div>
+</template>
+
+<script lang="ts">
+  import { defineComponent } from 'vue';
+
+  export default defineComponent({
+    name: 'JFormContainer',
+    props: {
+      disabled: {
+        type: Boolean,
+        default: false,
+        required: false,
+      },
+    },
+  });
+</script>
+
+<style lang="less" scoped>
+  .jeecg-form-container-disabled {
+    cursor: not-allowed;
+
+    fieldset[disabled] {
+      -ms-pointer-events: none;
+      pointer-events: none;
+    }
+
+    .ant-select {
+      -ms-pointer-events: none;
+      pointer-events: none;
+    }
+
+    .ant-upload-select {
+      display: none;
+    }
+
+    .ant-upload-list {
+      cursor: grabbing;
+    }
+  }
+
+  .jeecg-form-container-disabled fieldset[disabled] .ant-upload-list {
+    -ms-pointer-events: auto !important;
+    pointer-events: auto !important;
+  }
+
+  .jeecg-form-container-disabled .ant-upload-list-item-actions .anticon-delete,
+  .jeecg-form-container-disabled .ant-upload-list-item .anticon-close {
+    display: none;
+  }
+</style>

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

@@ -18,7 +18,7 @@
           <UploadOutlined v-else />
           <div class="ant-upload-text">{{ text }}</div>
         </div>
-        <a-button v-if="listType == 'picture'">
+        <a-button v-if="listType == 'picture'" :disabled="disabled">
           <UploadOutlined></UploadOutlined>
           {{ text }}
         </a-button>

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

@@ -35,7 +35,7 @@
     height: propTypes.number.def(150),
     disabled: propTypes.bool.def(false),
     // 弹出框挂载的元素ID
-    popContainer: propTypes.string.def(''),
+    popContainer: propTypes.oneOfType([propTypes.string, propTypes.func]).def(''),
   });
   const attrs = useAttrs();
   const emit = defineEmits(['change', 'update:value']);
@@ -75,6 +75,8 @@
   function getPopupContainer(node) {
     if (!props.popContainer) {
       return node.parentNode;
+    } else if (typeof props.popContainer === 'function') {
+      return props.popContainer(node);
     } else {
       return document.getElementById(props.popContainer);
     }

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

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

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

@@ -6,9 +6,11 @@
       <template #prefix>
         <Icon icon="ant-design:cluster-outlined"></Icon>
       </template>
-      <template #suffix>
-        <Icon icon="ant-design:close-circle-outlined" @click="handleEmpty" title="清空" v-if="showText"></Icon>
-      </template>
+      <!-- update-begin-author:taoyan date:2022-5-31 for: VUEN-1157 popup 选中后,有两个清除图标;后边这个清除,只是把输入框中数据清除,实际值并没有清除 -->
+      <!-- <template #suffix>
+                <Icon icon="ant-design:close-circle-outlined" @click="handleEmpty" title="清空" v-if="showText"></Icon>
+            </template>-->
+      <!-- update-begin-author:taoyan date:2022-5-31 for: VUEN-1157 popup 选中后,有两个清除图标;后边这个清除,只是把输入框中数据清除,实际值并没有清除 -->
     </a-input>
     <!--popup弹窗-->
     <JPopupOnlReportModal @register="regModal" :code="code" :multi="multi" :sorter="sorter" :groupId="uniqGroupId" :param="param" @ok="callBack"></JPopupOnlReportModal>

+ 24 - 14
src/components/Form/src/jeecg/components/JSearchSelect.vue

@@ -117,15 +117,20 @@
         options.value = [];
         loading.value = true;
         // 字典code格式:table,text,code
-        defHttp.get({ url: `/sys/dict/loadDict/${props.dict}`, params: { keyword: value, pageSize: props.pageSize } }).then((res) => {
-          loading.value = false;
-          if (res && res.length > 0) {
-            if (currentLoad != unref(lastLoad)) {
-              return;
+        defHttp
+          .get({
+            url: `/sys/dict/loadDict/${props.dict}`,
+            params: { keyword: value, pageSize: props.pageSize },
+          })
+          .then((res) => {
+            loading.value = false;
+            if (res && res.length > 0) {
+              if (currentLoad != unref(lastLoad)) {
+                return;
+              }
+              options.value = res;
             }
-            options.value = res;
-          }
-        });
+          });
       }
       /**
        * 初始化value
@@ -186,12 +191,17 @@
           } else {
             //异步一开始也加载一点数据
             loading.value = true;
-            defHttp.get({ url: `/sys/dict/loadDict/${dict}`, params: { pageSize: pageSize, keyword: '' } }).then((res) => {
-              loading.value = false;
-              if (res && res.length > 0) {
-                options.value = res;
-              }
-            });
+            defHttp
+              .get({
+                url: `/sys/dict/loadDict/${dict}`,
+                params: { pageSize: pageSize, keyword: '' },
+              })
+              .then((res) => {
+                loading.value = false;
+                if (res && res.length > 0) {
+                  options.value = res;
+                }
+              });
           }
         }
       }

+ 12 - 7
src/components/Form/src/jeecg/components/JSelectDept.vue

@@ -37,7 +37,7 @@
       //下拉框选项值
       const selectOptions = ref<SelectTypes['options']>([]);
       //下拉框选中值
-      let selectValues = reactive<object>({
+      let selectValues = reactive<Recordable>({
         value: [],
       });
       // 是否正在加载回显数据
@@ -57,14 +57,16 @@
        */
       watchEffect(() => {
         props.value && initValue();
-        // update-begin-author:taoyan date:20220401 for:调用表单的 resetFields不会清空当前部门信息,界面显示上一次的数据
-        if (props.value === '' || props.value === undefined) {
-          state.value = [];
-          selectValues.value = [];
-        }
-        // update-end-author:taoyan date:20220401 for:调用表单的 resetFields不会清空当前部门信息,界面显示上一次的数据
       });
 
+      //update-begin-author:liusq---date:20220609--for: 为了解决弹窗form初始化赋值问题 ---
+      watch(
+        () => props.value,
+        () => {
+          initValue();
+        }
+      );
+      //update-end-author:liusq---date:20220609--for: 为了解决弹窗form初始化赋值问题 ---
       /**
        * 监听selectValues变化
        */
@@ -100,6 +102,9 @@
         if (value && typeof value === 'string') {
           state.value = value.split(',');
           selectValues.value = value.split(',');
+        } else {
+          // 【VUEN-857】兼容数组(行编辑的用法问题)
+          selectValues.value = value;
         }
       }
 

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

@@ -1,6 +1,6 @@
 <!--字典下拉多选-->
 <template>
-  <a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder" allowClear :getPopupContainer="getParentContainer">
+  <a-select :value="arrayValue" @change="onChange" mode="multiple" :filter-option="filterOption" :disabled="disabled" :placeholder="placeholder" allowClear :getPopupContainer="getParentContainer">
     <a-select-option v-for="(item, index) in dictOptions" :key="index" :getPopupContainer="getParentContainer" :value="item.value">
       {{ item.text || item.label }}
     </a-select-option>
@@ -39,7 +39,7 @@
       triggerChange: {
         type: Boolean,
         required: false,
-        default: false,
+        default: true,
       },
       spliter: {
         type: String,
@@ -55,6 +55,10 @@
         type: String,
         required: false,
       },
+      disabled: {
+        type: Boolean,
+        default: false,
+      },
     },
     emits: ['options-change', 'change', 'input', 'update:value'],
     setup(props, { emit, refs }) {
@@ -103,7 +107,14 @@
 
       // 根据字典code查询字典项
       function loadDictOptions() {
-        getDictItems(props.dictCode).then((res) => {
+        //update-begin-author:taoyan date:2022-6-21 for: 字典数据请求前将参数编码处理,但是不能直接编码,因为可能之前已经编码过了
+        let temp = props.dictCode || '';
+        if (temp.indexOf(',') > 0 && temp.indexOf(' ') > 0) {
+          // 编码后 是不包含空格的
+          temp = encodeURI(temp);
+        }
+        //update-end-author:taoyan date:2022-6-21 for: 字典数据请求前将参数编码处理,但是不能直接编码,因为可能之前已经编码过了
+        getDictItems(temp).then((res) => {
           if (res) {
             dictOptions.value = res.map((item) => ({ value: item.value, label: item.text }));
             //console.info('res', dictOptions.value);
@@ -114,6 +125,12 @@
         });
       }
 
+      //update-begin-author:taoyan date:2022-5-31 for: VUEN-1145 下拉多选,搜索时,查不到数据
+      function filterOption(input, option) {
+        return option.children[0].el.data.toLowerCase().indexOf(input.toLowerCase()) >= 0;
+      }
+      //update-end-author:taoyan date:2022-5-31 for: VUEN-1145 下拉多选,搜索时,查不到数据
+
       return {
         state,
         attrs,
@@ -121,6 +138,7 @@
         onChange,
         arrayValue,
         getParentContainer,
+        filterOption,
       };
     },
   });

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

@@ -47,7 +47,7 @@
       //下拉框选项值
       const selectOptions = ref<SelectTypes['options']>([]);
       //下拉框选中值
-      let selectValues = reactive<object>({
+      let selectValues = reactive<Recordable>({
         value: [],
         change: false,
       });
@@ -68,6 +68,10 @@
        */
       watchEffect(() => {
         props.value && initValue();
+        // 查询条件重置的时候,清空界面显示
+        if (!props.value) {
+          selectValues.value = [];
+        }
       });
 
       /**
@@ -97,6 +101,8 @@
         if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
           state.value = value.split(',');
           selectValues.value = value.split(',');
+        } else {
+          selectValues.value = value;
         }
       }
 

+ 4 - 1
src/components/Form/src/jeecg/components/JSelectUser.vue

@@ -48,7 +48,7 @@
       //下拉框选项值
       const selectOptions = ref<SelectTypes['options']>([]);
       //下拉框选中值
-      let selectValues = reactive<object>({
+      let selectValues = reactive<Recordable>({
         value: [],
         change: false,
       });
@@ -102,6 +102,9 @@
         if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
           state.value = value.split(',');
           selectValues.value = value.split(',');
+        } else {
+          // 【VUEN-857】兼容数组(行编辑的用法问题)
+          selectValues.value = value;
         }
       }
 

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

@@ -1,6 +1,6 @@
 <template>
   <div :class="prefixCls">
-    <a-select v-if="query" :options="selectOptions" :disabled="disabled" style="width: 100%" v-bind="attrs" @change="onSelectChange" />
+    <a-select v-if="query" v-model:value="value" :options="selectOptions" :disabled="disabled" style="width: 100%" v-bind="attrs" @change="onSelectChange" />
     <a-switch v-else v-model:checked="checked" :disabled="disabled" v-bind="attrs" @change="onSwitchChange" />
   </div>
 </template>

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

@@ -39,11 +39,14 @@
     dict: propTypes.string.def('id'),
     parentCode: propTypes.string.def(''),
     pidField: propTypes.string.def('pid'),
-    pidValue: propTypes.string.def('0'),
+    //update-begin---author:wangshuai ---date:20220620  for:JTreeSelect组件pidValue还原成空,否则会影响自定义组件树示例------------
+    pidValue: propTypes.string.def(''),
+    //update-end---author:wangshuai ---date:20220620  for:JTreeSelect组件pidValue还原成空,否则会影响自定义组件树示例--------------
     hasChildField: propTypes.string.def(''),
     condition: propTypes.string.def(''),
     multiple: propTypes.bool.def(false),
     loadTriggleChange: propTypes.bool.def(false),
+    reload: propTypes.number.def(1),
   });
   const attrs = useAttrs();
   const emit = defineEmits(['change', 'update:value']);
@@ -75,6 +78,19 @@
     { deep: true, immediate: true }
   );
 
+  //update-begin-author:taoyan date:2022-5-25 for: VUEN-1056 15、严重——online树表单,添加的时候,父亲节点是空的
+  watch(
+    () => props.reload,
+    async () => {
+      treeData.value = [];
+      await loadRoot();
+    },
+    {
+      immediate: false,
+    }
+  );
+  //update-end-author:taoyan date:2022-5-25 for: VUEN-1056 15、严重——online树表单,添加的时候,父亲节点是空的
+
   /**
    * 根据code获取下拉数据并回显
    */

+ 6 - 0
src/components/Form/src/jeecg/components/JUpload/JUpload.vue

@@ -367,6 +367,12 @@
             display: none;
           }
         }
+
+        /* update-begin-author:taoyan date:2022-5-24 for:VUEN-1093详情界面 图片下载按钮显示不全*/
+        .upload-download-handler {
+          right: 6px !important;
+        }
+        /* update-end-author:taoyan date:2022-5-24 for:VUEN-1093详情界面 图片下载按钮显示不全*/
       }
 
       .ant-upload-list-item {

+ 7 - 1
src/components/Form/src/jeecg/components/base/JSelectBiz.vue

@@ -17,13 +17,15 @@
           :open="false"
           :disabled="disabled"
           :options="options"
+          :maxTagCount="maxTagCount"
           @change="handleChange"
           style="width: 100%"
           @click="!disabled && openModal(false)"
         ></a-select>
       </a-col>
       <a-col v-if="showButton" class="right">
-        <a-button type="primary" @click="openModal(true)" :disabled="disabled">选择</a-button>
+        <a-button v-if="buttonIcon" :preIcon="buttonIcon" type="primary" @click="openModal(true)" :disabled="disabled">选择</a-button>
+        <a-button v-else type="primary" @click="openModal(true)" :disabled="disabled">选择</a-button>
       </a-col>
     </a-row>
   </div>
@@ -52,6 +54,10 @@
       },
       // 是否正在加载
       loading: propTypes.bool.def(false),
+      // 最多显示多少个 tag
+      maxTagCount: propTypes.number,
+      // buttonIcon
+      buttonIcon: propTypes.string.def(''),
     },
     emits: ['handleOpen', 'change'],
     setup(props, { emit, refs }) {

+ 13 - 2
src/components/Form/src/jeecg/components/modal/DeptSelectModal.vue

@@ -1,7 +1,7 @@
 <!--部门选择框-->
 <template>
   <div>
-    <BasicModal v-bind="$attrs" @register="register" title="部门选择" width="500px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
+    <BasicModal v-bind="$attrs" @register="register" :title="modalTitle" width="500px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
       <BasicTree
         ref="treeRef"
         :treeData="treeData"
@@ -47,6 +47,11 @@
     },
     props: {
       ...treeProps,
+      //选择框标题
+      modalTitle: {
+        type: String,
+        default: '部门选择',
+      },
     },
     emits: ['register', 'getSelectResult'],
     setup(props, { emit, refs }) {
@@ -55,7 +60,7 @@
       const attrs = useAttrs();
       const treeRef = ref<Nullable<TreeActionType>>(null);
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
-      const queryUrl = props.sync ? queryDepartTreeSync : queryTreeList;
+      const queryUrl = getQueryUrl();
       const [{ visibleChange, checkedKeys, getCheckStrictly, getSelectTreeData, onCheck, onLoadData, treeData, checkALL, expandAll, onSelect }] = useTreeBiz(treeRef, queryUrl, getBindValue);
       const searchInfo = ref(props.params);
       const tree = ref([]);
@@ -75,6 +80,12 @@
         });
       }
 
+      /** 获取查询数据方法 */
+      function getQueryUrl() {
+        let queryFn = props.sync ? queryDepartTreeSync : queryTreeList;
+        return (params) => queryFn(Object.assign({}, params, { primaryKey: props.primaryKey }));
+      }
+
       return {
         tree,
         handleOk,

+ 21 - 3
src/components/Form/src/jeecg/components/modal/JPopupOnlReportModal.vue

@@ -78,7 +78,9 @@
       //此处需要异步加载BasicTable
       BasicModal,
       SearchFormItem: createAsyncComponent(() => import('/@/components/jeecg/OnLine/SearchFormItem.vue'), { loading: false }),
-      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
+      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
+        loading: true,
+      }),
     },
     props: ['multi', 'code', 'sorter', 'groupId', 'param'],
     emits: ['ok', 'register'],
@@ -101,7 +103,7 @@
       const tableScroll = ref({ x: true });
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
       const [
-        { visibleChange, loadColumnsInfo, dynamicParamHandler, loadData, handleChangeInTable, combineRowKey, clickThenCheck, filterUnuseSelect },
+        { visibleChange, loadColumnsInfo, dynamicParamHandler, loadData, handleChangeInTable, combineRowKey, clickThenCheck, filterUnuseSelect, getOkSelectRows },
         { visible, rowSelection, checkedKeys, selectRows, pagination, dataSource, columns, loading, title, iSorter, queryInfo, queryParam, dictOptions },
       ] = usePopBiz(getBindValue);
 
@@ -149,6 +151,19 @@
         }
       });
 
+      //update-begin-author:taoyan date:2022-5-31 for: VUEN-1156 popup 多数据有分页时,选中其他页,关闭popup 再点开,分页仍然选中上一次点击的分页,但数据是第一页的数据 未刷新
+      watch(
+        () => pagination.current,
+        (current) => {
+          if (current) {
+            tableRef.value.setPagination({
+              current: current,
+            });
+          }
+        }
+      );
+      //update-end-author:taoyan date:2022-5-31 for: VUEN-1156 popup 多数据有分页时,选中其他页,关闭popup 再点开,分页仍然选中上一次点击的分页,但数据是第一页的数据 未刷新
+
       function handleToggleSearch() {
         toggleSearchStatus.value = !unref(toggleSearchStatus);
       }
@@ -174,7 +189,10 @@
           createMessage.warning('至少选择一条记录');
           return false;
         }
-        emit('ok', unref(selectRows));
+        //update-begin-author:taoyan date:2022-5-31 for: VUEN-1155 popup 选择数据时,会选择多条重复数据
+        let rows = getOkSelectRows!();
+        emit('ok', rows);
+        //update-end-author:taoyan date:2022-5-31 for: VUEN-1155 popup 选择数据时,会选择多条重复数据
         handleCancel();
       }
 

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

@@ -1,7 +1,7 @@
 <!--职务选择框-->
 <template>
   <div>
-    <BasicModal v-bind="$attrs" @register="register" title="职务选择" width="900px" wrapClassName="j-user-select-modal" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
+    <BasicModal v-bind="$attrs" @register="register" :title="modalTitle" width="900px" wrapClassName="j-user-select-modal" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
       <a-row>
         <a-col :span="showSelected ? 18 : 24">
           <BasicTable
@@ -42,10 +42,17 @@
     components: {
       //此处需要异步加载BasicTable
       BasicModal,
-      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
+      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
+        loading: true,
+      }),
     },
     props: {
       ...selectProps,
+      //选择框标题
+      modalTitle: {
+        type: String,
+        default: '职务选择',
+      },
     },
     emits: ['register', 'getSelectResult'],
     setup(props, { emit, refs }) {
@@ -118,7 +125,13 @@
             dataIndex: 'name',
             width: 40,
           },
-          { title: '操作', dataIndex: 'action', align: 'center', width: 40, slots: { customRender: 'action' } },
+          {
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            width: 40,
+            slots: { customRender: 'action' },
+          },
         ],
       };
       /**

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

@@ -1,7 +1,7 @@
 <!--角色选择框-->
 <template>
   <div>
-    <BasicModal v-bind="$attrs" @register="register" title="角色选择" width="800px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
+    <BasicModal v-bind="$attrs" @register="register" :title="modalTitle" width="800px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
       <BasicTable
         :columns="columns"
         v-bind="config"
@@ -29,10 +29,17 @@
     components: {
       //此处需要异步加载BasicTable
       BasicModal,
-      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
+      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
+        loading: true,
+      }),
     },
     props: {
       ...selectProps,
+      //选择框标题
+      modalTitle: {
+        type: String,
+        default: '角色选择',
+      },
     },
     emits: ['register', 'getSelectResult'],
     setup(props, { emit, refs }) {
@@ -44,7 +51,7 @@
         canResize: false,
         bordered: true,
         size: 'small',
-        rowKey: 'id',
+        rowKey: unref(props).rowKey,
       };
       const getBindValue = Object.assign({}, unref(props), unref(attrs), config);
       const [{ rowSelection, indexColumnProps, visibleChange, getSelectResult }] = useSelectBiz(getRoleList, getBindValue);

+ 9 - 2
src/components/Form/src/jeecg/components/modal/UserSelectByDepModal.vue

@@ -1,6 +1,6 @@
 <!--通过部门选择用户-->
 <template>
-  <BasicModal v-bind="$attrs" @register="register" title="用户选择" width="1200px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
+  <BasicModal v-bind="$attrs" @register="register" :title="modalTitle" width="1200px" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
     <a-row :gutter="10">
       <a-col :md="7" :sm="24">
         <a-card :style="{ minHeight: '613px', overflow: 'auto' }">
@@ -43,10 +43,17 @@
       //此处需要异步加载BasicTable
       BasicModal,
       BasicTree,
-      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
+      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
+        loading: true,
+      }),
     },
     props: {
       ...selectProps,
+      //选择框标题
+      modalTitle: {
+        type: String,
+        default: '部门用户选择',
+      },
     },
     emits: ['register', 'getSelectResult'],
     setup(props, { emit, refs }) {

+ 54 - 6
src/components/Form/src/jeecg/components/modal/UserSelectModal.vue

@@ -1,11 +1,13 @@
 <!--用户选择框-->
 <template>
   <div>
-    <BasicModal v-bind="$attrs" @register="register" title="用户选择" width="900px" wrapClassName="j-user-select-modal" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
+    <BasicModal v-bind="$attrs" @register="register" :title="modalTitle" width="900px" wrapClassName="j-user-select-modal" @ok="handleOk" destroyOnClose @visible-change="visibleChange">
       <a-row>
         <a-col :span="showSelected ? 18 : 24">
           <BasicTable
+            ref="tableRef"
             :columns="columns"
+            :scroll="tableScroll"
             v-bind="getBindValue"
             :useSearchForm="true"
             :formConfig="formConfig"
@@ -13,7 +15,11 @@
             :searchInfo="searchInfo"
             :rowSelection="rowSelection"
             :indexColumnProps="indexColumnProps"
-          ></BasicTable>
+          >
+            <!-- update-begin-author:taoyan date:2022-5-25 for: VUEN-1112一对多 用户选择 未显示选择条数,及清空 -->
+            <template #tableTitle></template>
+            <!-- update-end-author:taoyan date:2022-5-25 for: VUEN-1112一对多 用户选择 未显示选择条数,及清空 -->
+          </BasicTable>
         </a-col>
         <a-col :span="showSelected ? 6 : 0">
           <BasicTable v-bind="selectedTable" :dataSource="selectRows" :useSearchForm="true" :formConfig="{ showActionButtonGroup: false, baseRowStyle: { minHeight: '40px' } }">
@@ -41,15 +47,39 @@
     components: {
       //此处需要异步加载BasicTable
       BasicModal,
-      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
+      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
+        loading: true,
+      }),
     },
     props: {
       ...selectProps,
+      //选择框标题
+      modalTitle: {
+        type: String,
+        default: '选择用户',
+      },
     },
     emits: ['register', 'getSelectResult'],
     setup(props, { emit, refs }) {
+      // update-begin-author:taoyan date:2022-5-24 for: VUEN-1086 【移动端】用户选择 查询按钮 效果不好 列表展示没有滚动条
+      const tableScroll = ref<any>({ x: false });
+      const tableRef = ref();
       //注册弹框
-      const [register, { closeModal }] = useModalInner();
+      const [register, { closeModal }] = useModalInner(() => {
+        if (window.innerWidth < 900) {
+          tableScroll.value = { x: 900 };
+        } else {
+          tableScroll.value = { x: false };
+        }
+        //update-begin-author:taoyan date:2022-6-2 for: VUEN-1112 一对多 用户选择 未显示选择条数,及清空
+        setTimeout(() => {
+          if (tableRef.value) {
+            tableRef.value.setSelectedRowKeys(selectValues['value'] || []);
+          }
+        }, 800);
+        //update-end-author:taoyan date:2022-6-2 for: VUEN-1112 一对多 用户选择 未显示选择条数,及清空
+      });
+      // update-end-author:taoyan date:2022-5-24 for: VUEN-1086 【移动端】用户选择 查询按钮 效果不好 列表展示没有滚动条
       const attrs = useAttrs();
       //表格配置
       const config = {
@@ -58,7 +88,7 @@
         size: 'small',
       };
       const getBindValue = Object.assign({}, unref(props), unref(attrs), config);
-      const [{ rowSelection, visibleChange, indexColumnProps, getSelectResult, handleDeleteSelected, selectRows }] = useSelectBiz(getUserList, getBindValue);
+      const [{ rowSelection, visibleChange, selectValues, indexColumnProps, getSelectResult, handleDeleteSelected, selectRows }] = useSelectBiz(getUserList, getBindValue);
       const searchInfo = ref(props.params);
       //查询form
       const formConfig = {
@@ -71,6 +101,16 @@
           xl: 6,
           xxl: 6,
         },
+        //update-begin-author:taoyan date:2022-5-24 for: VUEN-1086 【移动端】用户选择 查询按钮 效果不好 列表展示没有滚动条---查询表单按钮的栅格布局和表单的保持一致
+        actionColOptions: {
+          xs: 24,
+          sm: 8,
+          md: 8,
+          lg: 8,
+          xl: 8,
+          xxl: 8,
+        },
+        //update-end-author:taoyan date:2022-5-24 for: VUEN-1086 【移动端】用户选择 查询按钮 效果不好 列表展示没有滚动条---查询表单按钮的栅格布局和表单的保持一致
         schemas: [
           {
             label: '账号',
@@ -133,7 +173,13 @@
             dataIndex: 'realname',
             width: 40,
           },
-          { title: '操作', dataIndex: 'action', align: 'center', width: 40, slots: { customRender: 'action' } },
+          {
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            width: 40,
+            slots: { customRender: 'action' },
+          },
         ],
       };
       /**
@@ -163,6 +209,8 @@
         selectRows,
         selectedTable,
         handleDeleteSelected,
+        tableScroll,
+        tableRef,
       };
     },
   });

+ 19 - 3
src/components/Form/src/jeecg/hooks/useSelectBiz.ts

@@ -5,7 +5,7 @@ export function useSelectBiz(getList, props) {
   //接收下拉框选项
   const selectOptions = inject('selectOptions', ref<Array<object>>([]));
   //接收已选择的值
-  const selectValues = <object>inject('selectValues', reactive({}));
+  const selectValues = <object>inject('selectValues', reactive({ value: [], change: false }));
   // 是否正在加载回显
   const loadingEcho = inject<Ref<boolean>>('loadingEcho', ref(false));
   //数据集
@@ -62,7 +62,9 @@ export function useSelectBiz(getList, props) {
    * 选择列配置
    */
   const rowSelection = {
-    type: 'checkbox',
+    //update-begin-author:liusq---date:20220517--for: 动态设置rowSelection的type值,默认是'checkbox' ---
+    type: props.isRadioSelection ? 'radio' : 'checkbox',
+    //update-end-author:liusq---date:20220517--for: 动态设置rowSelection的type值,默认是'checkbox' ---
     columnWidth: 20,
     selectedRowKeys: checkedKeys,
     onChange: onSelectChange,
@@ -141,6 +143,20 @@ export function useSelectBiz(getList, props) {
     selectRows.value = [];
   }
   return [
-    { onSelectChange, getDataSource, visibleChange, selectOptions, selectValues, rowSelection, indexColumnProps, checkedKeys, selectRows, dataSource, getSelectResult, handleDeleteSelected, reset },
+    {
+      onSelectChange,
+      getDataSource,
+      visibleChange,
+      selectOptions,
+      selectValues,
+      rowSelection,
+      indexColumnProps,
+      checkedKeys,
+      selectRows,
+      dataSource,
+      getSelectResult,
+      handleDeleteSelected,
+      reset,
+    },
   ];
 }

+ 7 - 0
src/components/Form/src/jeecg/props/props.ts

@@ -2,6 +2,13 @@
 import { propTypes } from '/@/utils/propTypes';
 
 export const selectProps = {
+  //是否多选
+  isRadioSelection: {
+    type: Boolean,
+    //update-begin---author:wangshuai ---date:20220527  for:部门用户组件默认应该单选,否则其他地方有问题------------
+    default: false,
+    //update-end---author:wangshuai ---date:20220527  for:部门用户组件默认应该单选,否则其他地方有问题--------------
+  },
   //回传value字段名
   rowKey: {
     type: String,

+ 5 - 1
src/components/Form/src/types/form.ts

@@ -1,5 +1,5 @@
 import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface';
-import type { VNode } from 'vue';
+import type { VNode, ComputedRef } from 'vue';
 import type { ButtonProps as AntdButtonProps } from '/@/components/Button';
 import type { FormItem } from './formItem';
 import type { ColEx, ComponentType } from './index';
@@ -34,6 +34,7 @@ export interface FormActionType {
   updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
   resetSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
   setProps: (formProps: Partial<FormProps>) => Promise<void>;
+  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>;
@@ -189,6 +190,9 @@ export interface FormSchema {
   dynamicDisabled?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);
 
   dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[];
+
+  // 这个属性自定义的 用于自定义的业务 比如在表单打开的时候修改表单的禁用状态,但是又不能重写componentProps,因为他的内容太多了,所以使用dynamicDisabled和buss实现
+  buss?: any;
 }
 export interface HelpComponentProps {
   maxWidth: string;

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

@@ -141,5 +141,4 @@ export type ComponentType =
   | 'JSearchSelect'
   | 'JAddInput'
   | 'Time'
-  | 'JOnlineSelectCascade'
   | 'JRangeNumber';

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

@@ -98,6 +98,11 @@ const jeecgAreaData = new Area();
 
 // 根据code找文本
 const getAreaTextByCode = function (code) {
+  //update-begin-author:liusq---date:20220531--for: 判断code是否是多code逗号分割的字符串,是的话,获取最后一位的code ---
+  if (code && code.includes(',')) {
+    code = code.substr(code.lastIndexOf(',') + 1);
+  }
+  //update-end-author:liusq---date:20220531--for: 判断code是否是多code逗号分割的字符串,是的话,获取最后一位的code ---
   return jeecgAreaData.getText(code);
 };
 

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

@@ -58,7 +58,13 @@ export function handleRangeNumberValue(props, values) {
     if (!field || !startNumberKey || !endNumberKey || !values[field]) {
       continue;
     }
-    const [startNumber, endNumber]: number[] = values[field];
+    //update-begin-author:taoyan date:2022-5-10 for: 用于数值的范围查询 数组格式的中间转换不知道哪里出了问题,这里会变成字符串,需要再强制转成数组
+    let temp = values[field];
+    if (typeof temp === 'string') {
+      temp = temp.split(',');
+    }
+    const [startNumber, endNumber]: number[] = temp;
+    //update-end-author:taoyan date:2022-5-10 for: 用于数值的范围查询 数组格式的中间转换不知道哪里出了问题,这里会变成字符串,需要再强制转成数组
     values[startNumberKey] = startNumber;
     values[endNumberKey] = endNumber;
     Reflect.deleteProperty(values, field);

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

@@ -1,5 +1,5 @@
 <template>
-  <a-input disabled :style="{ width }" :placeholder="t('component.icon.placeholder')" :class="prefixCls" v-model:value="currentSelect">
+  <a-input :disabled="disabled" :style="{ width }" :placeholder="t('component.icon.placeholder')" :class="prefixCls" v-model:value="currentSelect">
     <template #addonAfter>
       <a-popover placement="bottomLeft" trigger="click" v-model="visible" :overlayClassName="`${prefixCls}-popover`">
         <template #title>
@@ -88,6 +88,7 @@
     pageSize: propTypes.number.def(140),
     copy: propTypes.bool.def(false),
     mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'),
+    disabled: propTypes.bool.def(true),
   });
 
   const emit = defineEmits(['change', 'update:value']);

+ 27 - 24
src/components/JVxeCustom/src/components/JVxeDepartSelectCell.vue

@@ -23,7 +23,9 @@
   <!--      <a-icon style="margin-left:5px;vertical-align: middle" type="close-circle" @click="handleEmpty" title="清空"/>-->
   <!--    </span>-->
   <!--  </div>-->
-  <JSelectDept :value="selectedValue" :showButton="false" v-bind="cellProps" @change="handleChange" />
+  <div :class="[prefixCls]">
+    <JSelectDept :value="selectedValue" :maxTagCount="1" :showButton="false" v-bind="cellProps" @change="handleChange" />
+  </div>
 </template>
 
 <script lang="ts">
@@ -32,8 +34,6 @@
   import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
   import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
 
-  // import { isArray, isEmpty, isString } from '/@/utils/is'
-
   // import JSelectDepartModal from '@/components/jeecgbiz/modal/JSelectDepartModal'
   import { dispatchEvent } from '/@/components/jeecg/JVxeTable/utils';
   import { isArray, isEmpty, isString } from '/@/utils/is';
@@ -43,32 +43,24 @@
     components: { JSelectDept },
     props: useJVxeCompProps(),
     setup(props: JVxeComponent.Props) {
-      const { innerValue, cellProps, handleChangeCommon } = useJVxeComponent(props);
-
-      // const selectedValue = computed(() => {
-      //   let val: any = innerValue.value
-      //   if (isEmpty(val)) {
-      //     return []
-      //   }
-      //   if (isArray(val)) {
-      //     return val
-      //   }
-      //   if (isString(val)) {
-      //     // @ts-ignore
-      //     return val.split(',')
-      //   }
-      //   return [val]
-      // })
+      const { innerValue, cellProps, handleChangeCommon, useCellDesign } = useJVxeComponent(props);
+      const { prefixCls } = useCellDesign('depart-select');
 
       const selectedValue = computed(() => {
-        let val = innerValue.value;
+        let val: any = innerValue.value;
+        if (val == null) {
+          return val;
+        }
         if (isEmpty(val)) {
-          return '';
+          return [];
         }
         if (isArray(val)) {
-          return (<any>val).join(',');
+          return val;
         }
-        return val;
+        if (isString(val)) {
+          return (<string>val).split(',');
+        }
+        return [val];
       });
 
       const multiple = computed(() => cellProps.value['multi'] != false);
@@ -78,6 +70,7 @@
       }
 
       return {
+        prefixCls,
         selectedValue,
         multiple,
         cellProps,
@@ -191,4 +184,14 @@
   });
 </script>
 
-<style scoped></style>
+<style lang="less">
+  // noinspection LessUnresolvedVariable
+  @prefix-cls: ~'@{namespace}-vxe-cell-depart-select';
+
+  .@{prefix-cls} {
+    // 限制tag最大长度为100px,防止选中文字过多的选项时换行
+    .ant-select .ant-select-selection-overflow-item {
+      max-width: 100px;
+    }
+  }
+</style>

+ 20 - 3
src/components/JVxeCustom/src/components/JVxeUserSelectCell.vue

@@ -1,5 +1,7 @@
 <template>
-  <JSelectUser :value="selectedValue" :showButton="false" v-bind="cellProps" @change="handleChange" />
+  <div :class="[prefixCls]">
+    <JSelectUser :value="selectedValue" :maxTagCount="1" :showButton="false" v-bind="cellProps" @change="handleChange" />
+  </div>
 </template>
 
 <script lang="ts">
@@ -15,10 +17,14 @@
     components: { JSelectUser },
     props: useJVxeCompProps(),
     setup(props: JVxeComponent.Props) {
-      const { innerValue, cellProps, handleChangeCommon } = useJVxeComponent(props);
+      const { innerValue, cellProps, handleChangeCommon, useCellDesign } = useJVxeComponent(props);
+      const { prefixCls } = useCellDesign('user-select');
 
       const selectedValue = computed(() => {
         let val: any = innerValue.value;
+        if (val == null) {
+          return val;
+        }
         if (isEmpty(val)) {
           return [];
         }
@@ -39,6 +45,7 @@
       }
 
       return {
+        prefixCls,
         selectedValue,
         multiple,
         cellProps,
@@ -66,4 +73,14 @@
   });
 </script>
 
-<style scoped></style>
+<style lang="less">
+  // noinspection LessUnresolvedVariable
+  @prefix-cls: ~'@{namespace}-vxe-cell-user-select';
+
+  .@{prefix-cls} {
+    // 限制tag最大长度为100px,防止选中文字过多的选项时换行
+    .ant-select .ant-select-selection-overflow-item {
+      max-width: 100px;
+    }
+  }
+</style>

+ 46 - 0
src/components/Markdown/src/Markdown.vue

@@ -10,6 +10,8 @@
   import { useModalContext } from '../../Modal';
   import { useRootSetting } from '/@/hooks/setting/useRootSetting';
   import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
+  import { getToken } from '/@/utils/auth';
+  import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
 
   type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
 
@@ -73,6 +75,34 @@
         }
         return lang;
       });
+      //update-begin-author:taoyan date:2022-5-24 for: VUEN-1090 markdown 无法上传
+      const uploadUrl = `${window._CONFIG['domianURL']}/sys/common/upload`;
+      const token = getToken();
+      function formatResult(files, responseText): string {
+        let data: any = JSON.parse(responseText);
+        // {"success":true,"message":"markdown/aa_1653391146501.png","code":0,"result":null,"timestamp":1653391146501}'
+        let filename = files[0].name as string;
+        let result = {
+          msg: '',
+          code: 0,
+          data: {
+            errFiles: [''],
+            succMap: {},
+          },
+        };
+        if (data.success) {
+          result.data.errFiles = [];
+          result.data.succMap = {
+            [data.message]: getFileAccessHttpUrl(data.message),
+          };
+        } else {
+          result.code = 1;
+          result.msg = data.message;
+          result.data.errFiles = [filename];
+        }
+        return JSON.stringify(result);
+      }
+      //update-end-author:taoyan date:2022-5-24 for: VUEN-1090 markdown 无法上传
       function init() {
         const wrapEl = unref(wrapRef) as HTMLElement;
         if (!wrapEl) return;
@@ -87,6 +117,22 @@
           preview: {
             actions: [],
           },
+          //update-begin-author:taoyan date:2022-5-24 for: VUEN-1090 markdown 无法上传
+          upload: {
+            accept: 'image/*',
+            url: uploadUrl,
+            fieldName: 'file',
+            extraData: { biz: 'markdown' },
+            setHeaders() {
+              return {
+                'X-Access-Token': token as string,
+              };
+            },
+            format(files, response) {
+              return formatResult(files, response);
+            },
+          },
+          //update-end-author:taoyan date:2022-5-24 for: VUEN-1090 markdown 无法上传
           input: (v) => {
             valueRef.value = v;
             emit('update:value', v);

+ 4 - 2
src/components/Menu/src/BasicMenu.vue

@@ -109,13 +109,15 @@
           }
         );
 
-      async function handleMenuClick({ key }: { key: string; keyPath: string[] }) {
+      //update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
+      async function handleMenuClick({ item, key }: { item: any; key: string; keyPath: string[] }) {
         const { beforeClickFn } = props;
         if (beforeClickFn && isFunction(beforeClickFn)) {
           const flag = await beforeClickFn(key);
           if (!flag) return;
         }
-        emit('menuClick', key);
+        emit('menuClick', key, item);
+        //update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
 
         isClickGo.value = true;
         // const parentPath = await getCurrentParentPath(key);

+ 1 - 1
src/components/Menu/src/components/BasicMenuItem.vue

@@ -1,5 +1,5 @@
 <template>
-  <MenuItem :key="item.path">
+  <MenuItem :key="item.path" :title="item.title">
     <MenuItemContent v-bind="$props" :item="item" />
   </MenuItem>
 </template>

+ 21 - 0
src/components/SimpleMenu/src/components/MenuItem.vue

@@ -24,6 +24,8 @@
   import { useMenuItem } from './useMenu';
   import { Tooltip } from 'ant-design-vue';
   import { useSimpleRootMenuContext } from './useSimpleMenuContext';
+  import { useLocaleStore } from '/@/store/modules/locale';
+
   export default defineComponent({
     name: 'MenuItem',
     components: { Tooltip },
@@ -36,6 +38,7 @@
     },
     setup(props, { slots }) {
       const instance = getCurrentInstance();
+      const localeStore = useLocaleStore();
 
       const active = ref(false);
 
@@ -92,6 +95,8 @@
               }
             });
 
+            //存储路径和标题的关系
+            storePathTitle(props.name);
             rootMenuEmitter.emit('on-update-active-name:submenu', uidList);
           } else {
             active.value = false;
@@ -100,6 +105,22 @@
         { immediate: true }
       );
 
+      //update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
+      function storePathTitle(path) {
+        console.log('storePathTitle', path);
+        let title = '';
+        if (instance!.attrs) {
+          let item: any = instance!.attrs.item;
+          if (item) {
+            title = item.title;
+          }
+        }
+        if (localeStore) {
+          localeStore.setPathTitle(path, title);
+        }
+      }
+      //update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
+
       return { getClass, prefixCls, getItemStyle, getCollapse, handleClickItem, showTooptip };
     },
   });

+ 28 - 22
src/components/Table/src/components/TableAction.vue

@@ -1,16 +1,22 @@
 <template>
   <div :class="[prefixCls, getAlign]" @click="onCellClick">
     <template v-for="(action, index) in getActions" :key="`${index}-${action.label}`">
-      <Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)">
-        <PopConfirmButton v-bind="action">
+      <template v-if="action.slot">
+        <slot name="customButton"></slot>
+      </template>
+      <template v-else>
+        <Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)">
+          <PopConfirmButton v-bind="action">
+            <Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
+            <template v-if="action.label">{{ action.label }}</template>
+          </PopConfirmButton>
+        </Tooltip>
+        <PopConfirmButton v-else v-bind="action">
           <Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
           <template v-if="action.label">{{ action.label }}</template>
         </PopConfirmButton>
-      </Tooltip>
-      <PopConfirmButton v-else v-bind="action">
-        <Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
-        <template v-if="action.label">{{ action.label }}</template>
-      </PopConfirmButton>
+      </template>
+
       <Divider type="vertical" class="action-divider" v-if="divider && index < getActions.length - 1" />
     </template>
     <Dropdown :trigger="['hover']" :dropMenuList="getDropdownList" popconfirm v-if="dropDownActions && getDropdownList.length > 0">
@@ -93,21 +99,21 @@
       });
 
       const getDropdownList = computed((): any[] => {
-        return (toRaw(props.dropDownActions) || [])
-          .filter((action) => {
-            return hasPermission(action.auth) && isIfShow(action);
-          })
-          .map((action, index) => {
-            const { label, popConfirm } = action;
-            return {
-              ...action,
-              ...popConfirm,
-              onConfirm: popConfirm?.confirm,
-              onCancel: popConfirm?.cancel,
-              text: label,
-              divider: index < props.dropDownActions.length - 1 ? props.divider : false,
-            };
-          });
+        //过滤掉隐藏的dropdown,避免出现多余的分割线
+        const list = (toRaw(props.dropDownActions) || []).filter((action) => {
+          return hasPermission(action.auth) && isIfShow(action);
+        });
+        return list.map((action, index) => {
+          const { label, popConfirm } = action;
+          return {
+            ...action,
+            ...popConfirm,
+            onConfirm: popConfirm?.confirm,
+            onCancel: popConfirm?.cancel,
+            text: label,
+            divider: index < list.length - 1 ? props.divider : false,
+          };
+        });
       });
 
       const getAlign = computed(() => {

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

@@ -18,7 +18,7 @@
           <template #content>
             <TableSetting mode="mobile" :setting="tableSetting" v-if="showTableSetting" @columns-change="handleColumnChange" />
           </template>
-          <a-button :class="`${prefixCls}__toolbar-mobile`" type="text" preIcon="ant-design:menu" shape="circle" />
+          <a-button :class="`${prefixCls}__toolbar-mobile`" v-if="showTableSetting" type="text" preIcon="ant-design:menu" shape="circle" />
         </a-popover>
       </div>
     </div>

+ 78 - 23
src/components/Table/src/components/settings/ColumnSetting.vue

@@ -3,7 +3,14 @@
     <template #title>
       <span>{{ t('component.table.settingColumn') }}</span>
     </template>
-    <Popover placement="bottomLeft" trigger="click" @visibleChange="handleVisibleChange" :overlayClassName="`${prefixCls}__cloumn-list`" :getPopupContainer="getPopupContainer">
+    <Popover
+      v-model:visible="popoverVisible"
+      placement="bottomLeft"
+      trigger="click"
+      @visibleChange="handleVisibleChange"
+      :overlayClassName="`${prefixCls}__cloumn-list`"
+      :getPopupContainer="getPopupContainer"
+    >
       <template #title>
         <div :class="`${prefixCls}__popover-title`">
           <Checkbox :indeterminate="indeterminate" v-model:checked="checkAll" @change="onCheckAllChange">
@@ -21,10 +28,6 @@
           <!--                    >-->
           <!--                        {{ t('component.table.settingSelectColumnShow') }}-->
           <!--                    </Checkbox>-->
-
-          <a-button size="small" type="link" @click="reset">
-            {{ t('common.resetText') }}
-          </a-button>
         </div>
       </template>
 
@@ -33,7 +36,7 @@
           <CheckboxGroup v-model:value="checkedList" @change="onChange" ref="columnListRef">
             <template v-for="item in plainOptions" :key="item.value">
               <div :class="`${prefixCls}__check-item`" v-if="!('ifShow' in item && !item.ifShow)">
-                <DragOutlined class="table-coulmn-drag-icon" />
+                <DragOutlined class="table-column-drag-icon" />
                 <Checkbox :value="item.value">
                   {{ item.label }}
                 </Checkbox>
@@ -75,6 +78,12 @@
             </template>
           </CheckboxGroup>
         </ScrollContainer>
+        <div :class="`${prefixCls}__popover-footer`">
+          <a-button size="small" @click="reset">
+            {{ t('common.resetText') }}
+          </a-button>
+          <a-button size="small" type="primary" @click="saveSetting"> 保存 </a-button>
+        </div>
       </template>
       <SettingOutlined />
     </Popover>
@@ -89,14 +98,18 @@
   import { ScrollContainer } from '/@/components/Container';
   import { useI18n } from '/@/hooks/web/useI18n';
   import { useTableContext } from '../../hooks/useTableContext';
+  import { useColumnsCache } from '../../hooks/useColumnsCache';
   import { useDesign } from '/@/hooks/web/useDesign';
-  import { useSortable } from '/@/hooks/web/useSortable';
+  // import { useSortable } from '/@/hooks/web/useSortable';
   import { isFunction, isNullAndUnDef } from '/@/utils/is';
   import { getPopupContainer as getParentContainer } from '/@/utils';
-  import { omit } from 'lodash-es';
+  import { cloneDeep, omit } from 'lodash-es';
+  import Sortablejs from 'sortablejs';
+  import type Sortable from 'sortablejs';
 
   interface State {
     checkAll: boolean;
+    isInit?: boolean;
     checkedList: string[];
     defaultCheckList: string[];
   }
@@ -128,12 +141,13 @@
     setup(props, { emit, attrs }) {
       const { t } = useI18n();
       const table = useTableContext();
+      const popoverVisible = ref(false);
 
       const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys');
       let inited = false;
 
       const cachePlainOptions = ref<Options[]>([]);
-      const plainOptions = ref<Options[]>([]);
+      const plainOptions = ref<Options[] | any>([]);
 
       const plainSortOptions = ref<Options[]>([]);
 
@@ -162,9 +176,26 @@
         return obj;
       });
 
+      let sortable: Sortable;
+      const sortableOrder = ref<string[]>();
+
+      // 列表字段配置缓存
+      const { saveSetting, resetSetting } = useColumnsCache(
+        {
+          state,
+          popoverVisible,
+          plainOptions,
+          plainSortOptions,
+          sortableOrder,
+          checkIndex,
+        },
+        setColumns,
+        handleColumnFixed
+      );
+
       watchEffect(() => {
         const columns = table.getColumns();
-        if (columns.length) {
+        if (columns.length && !state.isInit) {
           init();
         }
       });
@@ -217,6 +248,7 @@
             }
           });
         }
+        state.isInit = true;
         state.checkedList = checkList;
       }
 
@@ -234,16 +266,15 @@
 
       const indeterminate = computed(() => {
         const len = plainOptions.value.length;
-        let checkdedLen = state.checkedList.length;
-        unref(checkIndex) && checkdedLen--;
-        return checkdedLen > 0 && checkdedLen < len;
+        let checkedLen = state.checkedList.length;
+        unref(checkIndex) && checkedLen--;
+        return checkedLen > 0 && checkedLen < len;
       });
 
       // Trigger when check/uncheck a column
       function onChange(checkedList: string[]) {
-        const len = plainOptions.value.length;
+        const len = plainSortOptions.value.length;
         state.checkAll = checkedList.length === len;
-
         const sortList = unref(plainSortOptions).map((item) => item.value);
         checkedList.sort((prev, next) => {
           return sortList.indexOf(prev) - sortList.indexOf(next);
@@ -258,6 +289,10 @@
         plainOptions.value = unref(cachePlainOptions);
         plainSortOptions.value = unref(cachePlainOptions);
         setColumns(table.getCacheColumns());
+        if (sortableOrder.value) {
+          sortable.sort(sortableOrder.value);
+        }
+        resetSetting();
       }
 
       // Open the pop-up window for drag and drop initialization
@@ -269,15 +304,18 @@
           const el = columnListEl.$el as any;
           if (!el) return;
           // Drag and drop sort
-          const { initSortable } = useSortable(el, {
-            handle: '.table-coulmn-drag-icon ',
+          sortable = Sortablejs.create(unref(el), {
+            animation: 500,
+            delay: 400,
+            delayOnTouchOnly: true,
+            handle: '.table-column-drag-icon ',
             onEnd: (evt) => {
               const { oldIndex, newIndex } = evt;
               if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) {
                 return;
               }
               // Sort column
-              const columns = getColumns();
+              const columns = cloneDeep(plainSortOptions.value);
 
               if (oldIndex > newIndex) {
                 columns.splice(newIndex, 0, columns[oldIndex]);
@@ -288,11 +326,13 @@
               }
 
               plainSortOptions.value = columns;
-              plainOptions.value = columns;
               setColumns(columns);
             },
           });
-          initSortable();
+          // 记录原始 order 序列
+          if (!sortableOrder.value) {
+            sortableOrder.value = sortable.toArray();
+          }
           inited = true;
         });
       }
@@ -325,13 +365,13 @@
         if (isFixed && !item.width) {
           item.width = 100;
         }
-        table.setCacheColumnsByField?.(item.dataIndex, { fixed: isFixed });
+        table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed });
         setColumns(columns);
       }
 
       function setColumns(columns: BasicColumn[] | string[]) {
         table.setColumns(columns);
-        const data: ColumnChangeParam[] = unref(plainOptions).map((col) => {
+        const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
           const visible = columns.findIndex((c: BasicColumn | string) => c === col.value || (typeof c !== 'string' && c.dataIndex === col.value)) !== -1;
           return { dataIndex: col.value, fixed: col.fixed, visible };
         });
@@ -347,11 +387,13 @@
         getBindProps,
         t,
         ...toRefs(state),
+        popoverVisible,
         indeterminate,
         onCheckAllChange,
         onChange,
         plainOptions,
         reset,
+        saveSetting,
         prefixCls,
         columnListRef,
         handleVisibleChange,
@@ -369,7 +411,7 @@
 <style lang="less">
   @prefix-cls: ~'@{namespace}-basic-column-setting';
 
-  .table-coulmn-drag-icon {
+  .table-column-drag-icon {
     margin: 0 5px;
     cursor: move;
   }
@@ -382,6 +424,19 @@
       justify-content: space-between;
     }
 
+    /* 卡片底部样式 */
+    &__popover-footer {
+      position: relative;
+      top: 7px;
+      text-align: right;
+      padding: 4px 0 0;
+      border-top: 1px solid #f0f0f0;
+
+      .ant-btn {
+        margin-right: 6px;
+      }
+    }
+
     &__check-item {
       display: flex;
       align-items: center;

+ 11 - 16
src/components/Table/src/hooks/useColumns.ts

@@ -188,11 +188,13 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>, getPagination
       }
     });
   }
+
+  // update-begin--author:sunjianlei---date:20220523---for: 【VUEN-1089】合并vben最新版代码,解决表格字段排序问题
   /**
    * set columns
    * @param columnList key|column
    */
-  function setColumns(columnList: Partial<BasicColumn>[] | string[]) {
+  function setColumns(columnList: Partial<BasicColumn>[] | (string | string[])[]) {
     const columns = cloneDeep(columnList);
     if (!isArray(columns)) return;
 
@@ -205,34 +207,27 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>, getPagination
 
     const cacheKeys = cacheColumns.map((item) => item.dataIndex);
 
-    if (!isString(firstColumn)) {
+    if (!isString(firstColumn) && !isArray(firstColumn)) {
       columnsRef.value = columns as BasicColumn[];
     } else {
-      const columnKeys = columns as string[];
+      const columnKeys = (columns as (string | string[])[]).map((m) => m.toString());
       const newColumns: BasicColumn[] = [];
       cacheColumns.forEach((item) => {
-        if (columnKeys.includes(item.dataIndex! || (item.key as string))) {
-          newColumns.push({
-            ...item,
-            defaultHidden: false,
-          });
-        } else {
-          newColumns.push({
-            ...item,
-            defaultHidden: true,
-          });
-        }
+        newColumns.push({
+          ...item,
+          defaultHidden: !columnKeys.includes(item.dataIndex?.toString() || (item.key as string)),
+        });
       });
-
       // Sort according to another array
       if (!isEqual(cacheKeys, columns)) {
         newColumns.sort((prev, next) => {
-          return cacheKeys.indexOf(prev.dataIndex as string) - cacheKeys.indexOf(next.dataIndex as string);
+          return columnKeys.indexOf(prev.dataIndex?.toString() as string) - columnKeys.indexOf(next.dataIndex?.toString() as string);
         });
       }
       columnsRef.value = newColumns;
     }
   }
+  // update-end--author:sunjianlei---date:20220523---for: 【VUEN-1089】合并vben最新版代码,解决表格字段排序问题
 
   function getColumns(opt?: GetColumnsParams) {
     const { ignoreIndex, ignoreAction, sort } = opt || {};

+ 137 - 0
src/components/Table/src/hooks/useColumnsCache.ts

@@ -0,0 +1,137 @@
+import { computed, nextTick, unref, watchEffect } from 'vue';
+import { router } from '/@/router';
+import { createLocalStorage } from '/@/utils/cache';
+import { useTableContext } from './useTableContext';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+/**
+ * 列表配置缓存
+ */
+export function useColumnsCache(opt, setColumns, handleColumnFixed) {
+  let isInit = false;
+  const table = useTableContext();
+  const $ls = createLocalStorage();
+  const { createMessage: $message } = useMessage();
+  // 列表配置缓存key
+  const cacheKey = computed(() => {
+    let { fullPath } = router.currentRoute.value;
+    let key = fullPath.replace(/[\/\\]/g, '_');
+    let cacheKey = table.getBindValues.value.tableSetting?.cacheKey;
+    if (cacheKey) {
+      key += ':' + cacheKey;
+    }
+    return 'columnCache:' + key;
+  });
+
+  watchEffect(() => {
+    const columns = table.getColumns();
+    if (columns.length) {
+      init();
+    }
+  });
+
+  async function init() {
+    if (isInit) {
+      return;
+    }
+    isInit = true;
+    let columnCache = $ls.get(cacheKey.value);
+    if (columnCache && columnCache.checkedList) {
+      const { checkedList, sortedList, sortableOrder, checkIndex } = columnCache;
+      await nextTick();
+      // checkbox的排序缓存
+      opt.sortableOrder.value = sortableOrder;
+      // checkbox的选中缓存
+      opt.state.checkedList = checkedList;
+      // tableColumn的排序缓存
+      opt.plainSortOptions.value.sort((prev, next) => {
+        return sortedList.indexOf(prev.value) - sortedList.indexOf(next.value);
+      });
+      // 重新排序tableColumn
+      checkedList.sort((prev, next) => sortedList.indexOf(prev) - sortedList.indexOf(next));
+      // 是否显示行号列
+      if (checkIndex) {
+        table.setProps({ showIndexColumn: true });
+      }
+      setColumns(checkedList);
+      // 设置固定列
+      setColumnFixed(columnCache);
+    }
+  }
+
+  /** 设置被固定的列 */
+  async function setColumnFixed(columnCache) {
+    const { fixedColumns } = columnCache;
+    const columns = opt.plainOptions.value;
+    for (const column of columns) {
+      let fixedCol = fixedColumns.find((fc) => fc.key === (column.key || column.dataIndex));
+      if (fixedCol) {
+        await nextTick();
+        handleColumnFixed(column, fixedCol.fixed);
+      }
+    }
+  }
+
+  // 判断列固定状态
+  const fixedReg = /^(true|left|right)$/;
+
+  /** 获取被固定的列 */
+  function getFixedColumns() {
+    let fixedColumns: any[] = [];
+    const columns = opt.plainOptions.value;
+    for (const column of columns) {
+      if (fixedReg.test((column.fixed ?? '').toString())) {
+        fixedColumns.push({
+          key: column.key || column.dataIndex,
+          fixed: column.fixed === true ? 'left' : column.fixed,
+        });
+      }
+    }
+    return fixedColumns;
+  }
+
+  /** 保存列配置 */
+  function saveSetting() {
+    const { checkedList } = opt.state;
+    const sortedList = unref(opt.plainSortOptions).map((item) => item.value);
+    $ls.set(cacheKey.value, {
+      // 保存的列
+      checkedList,
+      // 排序后的列
+      sortedList,
+      // 是否显示行号列
+      checkIndex: unref(opt.checkIndex),
+      // checkbox原始排序
+      sortableOrder: unref(opt.sortableOrder),
+      // 固定列
+      fixedColumns: getFixedColumns(),
+    });
+    $message.success('保存成功');
+    // 保存之后直接关闭
+    opt.popoverVisible.value = false;
+  }
+
+  /** 重置(删除)列配置 */
+  async function resetSetting() {
+    // 重置固定列
+    await resetFixedColumn();
+    $ls.remove(cacheKey.value);
+    $message.success('重置成功');
+  }
+
+  async function resetFixedColumn() {
+    const columns = opt.plainOptions.value;
+    for (const column of columns) {
+      column.fixed;
+      if (fixedReg.test((column.fixed ?? '').toString())) {
+        await nextTick();
+        handleColumnFixed(column, null);
+      }
+    }
+  }
+
+  return {
+    saveSetting,
+    resetSetting,
+  };
+}

+ 2 - 1
src/components/Table/src/hooks/useTableHeader.ts

@@ -48,7 +48,8 @@ export function useTableHeader(propsRef: ComputedRef<BasicTableProps>, slots: Sl
                       tableTop: () => getSlot(slots, 'tableTop'),
                     }
                   : {}),
-                //添加tableTop插槽
+                // 添加alertAfter插槽
+                ...(slots.alertAfter ? { alertAfter: () => getSlot(slots, 'alertAfter') } : {}),
               }
             ),
     };

+ 6 - 0
src/components/Table/src/types/table.ts

@@ -127,9 +127,15 @@ export interface FetchSetting {
 }
 
 export interface TableSetting {
+  // 是否显示刷新按钮
   redo?: boolean;
+  // 是否显示尺寸调整按钮
   size?: boolean;
+  // 是否显示字段调整按钮
   setting?: boolean;
+  // 缓存“字段调整”配置的key,用于页面上有多个表格需要区分的情况
+  cacheKey?: string;
+  // 是否显示全屏按钮
   fullScreen?: boolean;
 }
 

+ 2 - 2
src/components/Tinymce/src/Editor.vue

@@ -66,7 +66,7 @@
     },
 
     toolbar: {
-      type: Array as PropType<string[]>,
+      type: [Array as PropType<string[]>, String],
       default: toolbar,
     },
     plugins: {
@@ -74,7 +74,7 @@
       default: plugins,
     },
     menubar: {
-      type: Object,
+      type: [Object, String],
       default: menubar,
     },
     modelValue: {

+ 24 - 4
src/components/Tinymce/src/ImgUpload.vue

@@ -1,6 +1,6 @@
 <template>
   <div :class="[prefixCls, { fullscreen }]">
-    <Upload name="file" multiple @change="handleChange" :action="uploadUrl" :showUploadList="false" accept=".jpg,.jpeg,.gif,.png,.webp">
+    <Upload name="file" multiple @change="handleChange" :action="uploadUrl" :showUploadList="false" :data="getBizData()" :headers="getheader()" accept=".jpg,.jpeg,.gif,.png,.webp">
       <a-button type="primary" v-bind="{ ...getButtonProps }">
         {{ t('component.upload.imgUpload') }}
       </a-button>
@@ -14,6 +14,8 @@
   import { useDesign } from '/@/hooks/web/useDesign';
   import { useGlobSetting } from '/@/hooks/setting';
   import { useI18n } from '/@/hooks/web/useI18n';
+  import { getToken } from '/@/utils/auth';
+  import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
 
   export default defineComponent({
     name: 'TinymceImageUpload',
@@ -31,7 +33,20 @@
     setup(props, { emit }) {
       let uploading = false;
 
-      const { uploadUrl } = useGlobSetting();
+      //update-begin-author:taoyan date:2022-5-13 for: 富文本上传图片不支持
+      function getheader() {
+        return { 'X-Access-Token': getToken() };
+      }
+
+      function getBizData() {
+        return {
+          biz: 'jeditor',
+          jeditor: '1',
+        };
+      }
+      const { domainUrl } = useGlobSetting();
+      const uploadUrl = domainUrl + '/sys/common/upload';
+      //update-end-author:taoyan date:2022-5-13 for: 富文本上传图片不支持
       const { t } = useI18n();
       const { prefixCls } = useDesign('tinymce-img-upload');
 
@@ -45,7 +60,7 @@
       function handleChange(info: Recordable) {
         const file = info.file;
         const status = file?.status;
-        const url = file?.response?.url;
+        //const url = file?.response?.url;
         const name = file?.name;
 
         if (status === 'uploading') {
@@ -54,7 +69,10 @@
             uploading = true;
           }
         } else if (status === 'done') {
-          emit('done', name, url);
+          //update-begin-author:taoyan date:2022-5-13 for: 富文本上传图片不支持
+          let realUrl = getFileAccessHttpUrl(file.response.message);
+          emit('done', name, realUrl);
+          //update-end-author:taoyan date:2022-5-13 for: 富文本上传图片不支持
           uploading = false;
         } else if (status === 'error') {
           emit('error');
@@ -66,6 +84,8 @@
         prefixCls,
         handleChange,
         uploadUrl,
+        getheader,
+        getBizData,
         t,
         getButtonProps,
       };

+ 3 - 1
src/components/jeecg/JVxeTable/src/components/JVxeDetailsModal.vue

@@ -15,7 +15,9 @@
 
   export default defineComponent({
     components: {
-      BasicModal: createAsyncComponent(() => import('/@/components/Modal/src/BasicModal.vue'), { loading: true }),
+      BasicModal: createAsyncComponent(() => import('/@/components/Modal/src/BasicModal.vue'), {
+        loading: true,
+      }),
     },
     props: {
       trigger: {

+ 11 - 1
src/components/jeecg/JVxeTable/src/components/JVxeSubPopover.vue

@@ -76,6 +76,12 @@
         let className = target.className || '';
         className = isString(className) ? className : className.toString();
 
+        // 获取 td 父级
+        let td = getParentNodeByTagName(target, 'td');
+        // 点击的是拖拽排序列,不做处理
+        if (td && td.querySelector('.j-vxe-drag-box')) {
+          return;
+        }
         // 点击的是expand,不做处理
         if (className.includes('vxe-table--expand-btn')) {
           return;
@@ -119,7 +125,11 @@
           });
         } else {
           let num = ++level;
-          console.warn('【JVxeSubPopover】table or tr 获取失败,正在进行第 ' + num + '次重试', { event, table: parentElem, tr });
+          console.warn('【JVxeSubPopover】table or tr 获取失败,正在进行第 ' + num + '次重试', {
+            event,
+            table: parentElem,
+            tr,
+          });
           window.setTimeout(() => open(event, num), 100);
         }
       }

+ 3 - 1
src/components/jeecg/JVxeTable/src/components/JVxeToolbar.vue

@@ -64,7 +64,9 @@
     return btns.filter((btn) => {
       // 系统默认的批量删除编码配置为 batch_delete 此处需要兼容一下
       if (btn === 'remove') {
-        return hasBtnAuth(btn) || hasBtnAuth('batch_delete');
+        //update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
+        return hasBtnAuth(btn) && hasBtnAuth('batch_delete');
+        //update-end-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
       }
       return hasBtnAuth(btn);
     });

+ 7 - 2
src/components/jeecg/JVxeTable/src/components/cells/JVxeTextareaCell.vue

@@ -1,5 +1,5 @@
 <template>
-  <JInputPop :value="innerValue" :width="300" :height="210" v-bind="cellProps" style="width: 100%" @blur="handleBlurCommon" @change="handleChangeCommon" />
+  <JInputPop :value="innerValue" :width="300" :height="210" :pop-container="getPopupContainer" v-bind="cellProps" style="width: 100%" @blur="handleBlurCommon" @change="handleChangeCommon" />
 </template>
 
 <script lang="ts">
@@ -15,7 +15,12 @@
     props: useJVxeCompProps(),
     setup(props: JVxeComponent.Props) {
       const { innerValue, cellProps, handleChangeCommon, handleBlurCommon } = useJVxeComponent(props);
-      return { innerValue, cellProps, handleChangeCommon, handleBlurCommon };
+
+      function getPopupContainer() {
+        return document.body;
+      }
+
+      return { innerValue, cellProps, handleChangeCommon, handleBlurCommon, getPopupContainer };
     },
     // 【组件增强】注释详见:JVxeComponent.Enhanced
     enhanced: {

+ 7 - 1
src/components/jeecg/JVxeTable/src/hooks/useColumns.ts

@@ -262,7 +262,13 @@ async function handleDict({ col, methods }: HandleArgs) {
       // 查询字典
       if (!isPromise(col.params.optionsPromise)) {
         col.params.optionsPromise = new Promise(async (resolve) => {
-          const dictOptions: any = await initDictOptions(col.params.dictCode);
+          //update-begin-author:taoyan date:2022-6-1 for: VUEN-1180 【代码生成】子表不支持带条件?
+          let dictCodeString = col.params.dictCode;
+          if (dictCodeString) {
+            dictCodeString = encodeURI(dictCodeString);
+          }
+          const dictOptions: any = await initDictOptions(dictCodeString);
+          //update-end-author:taoyan date:2022-6-1 for: VUEN-1180 【代码生成】子表不支持带条件?
           let options = col.params.options ?? [];
           dictOptions.forEach((dict) => {
             // 过滤重复数据

+ 1 - 1
src/components/jeecg/JVxeTable/src/hooks/useDataSource.ts

@@ -4,7 +4,7 @@ import { cloneDeep } from 'lodash-es';
 
 export function useDataSource(props, data: JVxeDataProps, methods: JVxeTableMethods, refs: JVxeRefs) {
   watch(
-    () => [props.dataSource, props.disabledRows],
+    () => props.dataSource,
     async () => {
       data.disabledRowIds = [];
       data.vxeDataSource.value = cloneDeep(props.dataSource);

+ 23 - 0
src/components/jeecg/JVxeTable/src/hooks/useJVxeComponent.ts

@@ -1,5 +1,6 @@
 import { computed, nextTick, ref, unref, watch } from 'vue';
 import { propTypes } from '/@/utils/propTypes';
+import { useDesign } from '/@/hooks/web/useDesign';
 import { getEnhanced, replaceProps, vModel } from '../utils/enhancedUtils';
 import { JVxeRenderType } from '../types/JVxeTypes';
 import { isBoolean, isFunction, isObject, isPromise } from '/@/utils/is';
@@ -59,6 +60,19 @@ export function useJVxeComponent(props: JVxeComponent.Props) {
     if (renderOptions.disabled === true) {
       cellProps['disabled'] = true;
     }
+    //update-begin-author:taoyan date:2022-5-25 for: VUEN-1111 一对多子表 部门选择 不应该级联
+    if (col.checkStrictly === true) {
+      cellProps['checkStrictly'] = true;
+    }
+    //update-end-author:taoyan date:2022-5-25 for: VUEN-1111 一对多子表 部门选择 不应该级联
+
+    //update-begin-author:taoyan date:2022-5-27 for: 用户组件 控制单选多选新的参数配置
+    if (col.isRadioSelection === true) {
+      cellProps['isRadioSelection'] = true;
+    } else if (col.isRadioSelection === false) {
+      cellProps['isRadioSelection'] = false;
+    }
+    //update-end-author:taoyan date:2022-5-27 for: 用户组件 控制单选多选新的参数配置
 
     return cellProps;
   });
@@ -182,10 +196,19 @@ export function useJVxeComponent(props: JVxeComponent.Props) {
     return event;
   }
 
+  /**
+   * 防样式冲突类名生成器
+   * @param scope
+   */
+  function useCellDesign(scope: string) {
+    return useDesign(`vxe-cell-${scope}`);
+  }
+
   return {
     ...context,
     enhanced,
     trigger,
+    useCellDesign,
   };
 }
 

+ 7 - 0
src/components/jeecg/JVxeTable/src/hooks/useMethods.ts

@@ -1,3 +1,4 @@
+import { watch } from 'vue';
 import XEUtils from 'xe-utils';
 import { simpleDebounce } from '/@/utils/common/compUtils';
 import { JVxeDataProps, JVxeRefs, JVxeTableProps, JVxeTypes } from '../types';
@@ -242,6 +243,12 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
     xTable.updateData();
   }
 
+  // 监听 disabledRows,更改时重新计算禁用行
+  watch(
+    () => props.disabledRows,
+    () => recalcDisableRows()
+  );
+
   // 返回值决定是否允许展开、收起行
   function handleExpandToggleMethod({ expanded }) {
     return !(expanded && props.disabled);

+ 12 - 2
src/components/jeecg/JVxeTable/src/hooks/useValidateRules.ts

@@ -82,12 +82,22 @@ const fooPatterns = [
   { title: '6到16位数字', value: 'n6-16', pattern: /^\d{6,16}$/ },
   { title: '6到16位任意字符', value: '*6-16', pattern: /^.{6,16}$/ },
   { title: '6到18位字母', value: 's6-18', pattern: /^[a-z|A-Z]{6,18}$/ },
-  { title: '网址', value: 'url', pattern: /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/ },
+  //update-begin-author:taoyan date:2022-6-1 for: VUEN-1160 对多子表,网址校验不正确
+  {
+    title: '网址',
+    value: 'url',
+    pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/,
+  },
+  //update-end-author:taoyan date:2022-6-1 for: VUEN-1160 对多子表,网址校验不正确
   { title: '电子邮件', value: 'e', pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/ },
   { title: '手机号码', value: 'm', pattern: /^1[3456789]\d{9}$/ },
   { title: '邮政编码', value: 'p', pattern: /^[1-9]\d{5}$/ },
   { title: '字母', value: 's', pattern: /^[A-Z|a-z]+$/ },
   { title: '数字', value: 'n', pattern: /^-?\d+(\.?\d+|\d?)$/ },
   { title: '整数', value: 'z', pattern: /^-?\d+$/ },
-  { title: '金额', value: 'money', pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/ },
+  {
+    title: '金额',
+    value: 'money',
+    pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,5}))$/,
+  },
 ];

+ 9 - 1
src/components/jeecg/JVxeTable/src/utils/authUtils.ts

@@ -16,7 +16,7 @@ export function getJVxeAuths(prefix) {
   }
   // 将所有vxe用到的权限取出来
   for (let auth of allAuthList) {
-    if (auth.status == '1' && auth.action.startsWith(prefix)) {
+    if (auth.status == '1' && (auth.action || '').startsWith(prefix)) {
       authsMap.set(auth.action, { ...auth, isAuth: false });
     }
   }
@@ -27,6 +27,14 @@ export function getJVxeAuths(prefix) {
       getAuth.isAuth = true;
     }
   }
+  //update-begin-author:taoyan date:2022-6-1 for:  VUEN-1162 子表按钮没控制
+  let onlineButtonAuths = permissionStore.getOnlineSubTableAuth(prefix);
+  if (onlineButtonAuths && onlineButtonAuths.length > 0) {
+    for (let auth of onlineButtonAuths) {
+      authsMap.set(prefix + 'btn:' + auth, { action: auth, type: 1, status: 1, isAuth: false });
+    }
+  }
+  //update-end-author:taoyan date:2022-6-1 for:  VUEN-1162 子表按钮没控制
   return authsMap;
 }
 

+ 2 - 2
src/components/jeecg/JVxeTable/src/utils/registerUtils.ts

@@ -21,8 +21,8 @@ export function isRegistered(type: JVxeTypes | string) {
  * 注册vxe自定义组件
  *
  * @param type
- * @param component
- * @param spanComponent
+ * @param component 编辑状态显示的组件
+ * @param spanComponent 非编辑状态显示的组件,可以为空
  */
 export function registerComponent(type: JVxeTypes, component: Component, spanComponent?: Component) {
   addComponent(type, component, spanComponent);

+ 3 - 1
src/components/jeecg/OnLine/JPopupOnlReport.vue

@@ -68,7 +68,9 @@
     name: 'JPopupOnlReport',
     components: {
       SearchFormItem: createAsyncComponent(() => import('/@/components/jeecg/OnLine/SearchFormItem.vue'), { loading: true }),
-      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), { loading: true }),
+      BasicTable: createAsyncComponent(() => import('/@/components/Table/src/BasicTable.vue'), {
+        loading: true,
+      }),
     },
     props: ['multi', 'code', 'id', 'sorter', 'groupId', 'param', 'clickToRowSelect'],
     emits: ['ok', 'register'],

+ 10 - 1
src/components/jeecg/OnLine/SearchFormItem.vue

@@ -239,7 +239,16 @@
           'max-width': labelTextMaxWidth,
         },
       };
-      return { labelTextMaxWidth, labelCol, single_mode, getDictOptionKey, getDictCode, getSqlByDictCode, DateTypeEnum, CompTypeEnum };
+      return {
+        labelTextMaxWidth,
+        labelCol,
+        single_mode,
+        getDictOptionKey,
+        getDictCode,
+        getSqlByDictCode,
+        DateTypeEnum,
+        CompTypeEnum,
+      };
     },
   });
 </script>

+ 67 - 5
src/components/jeecg/OnLine/hooks/usePopBiz.ts

@@ -1,4 +1,4 @@
-import { reactive, ref, unref, defineAsyncComponent } from 'vue';
+import { reactive, ref, unref, defineAsyncComponent, toRaw, markRaw } from 'vue';
 import { httpGroupRequest } from '/@/components/Form/src/utils/GroupRequest';
 import { defHttp } from '/@/utils/http/axios';
 import { filterMultiDictText } from '/@/utils/dict/JDictSelectUtil.js';
@@ -7,6 +7,7 @@ import { OnlineColumn } from '/@/components/jeecg/OnLine/types/onlineConfig';
 import { h } from 'vue';
 import { useRouter } from 'vue-router';
 import { useMethods } from '/@/hooks/system/useMethods';
+import { importViewsFile } from '/@/utils';
 
 export function usePopBiz(props, tableRef?) {
   const { createMessage } = useMessage();
@@ -707,7 +708,12 @@ export function usePopBiz(props, tableRef?) {
       destroyOnClose: true,
       style: dialogStyle,
       // dialogStyle: dialogStyle,
-      bodyStyle: { padding: '8px', height: 'calc(100vh - 108px)', overflow: 'auto', overflowX: 'hidden' },
+      bodyStyle: {
+        padding: '8px',
+        height: 'calc(100vh - 108px)',
+        overflow: 'auto',
+        overflowX: 'hidden',
+      },
       // 隐藏掉取消按钮
       cancelButtonProps: { style: { display: 'none' } },
     },
@@ -739,11 +745,67 @@ export function usePopBiz(props, tableRef?) {
     }
     hrefComponent.value.model.visible = true;
     hrefComponent.value.model.title = '操作';
-    hrefComponent.value.is = defineAsyncComponent(() => import(/* @vite-ignore */ '/@/views/' + (path.startsWith('/') ? path.slice(1) : path)));
+    hrefComponent.value.is = markRaw(defineAsyncComponent(() => importViewsFile(path)));
   }
 
+  //update-begin-author:taoyan date:2022-5-31 for: VUEN-1155 popup 选择数据时,会选择多条重复数据
+  /**
+   * emit事件 获取选中的行数据
+   */
+  function getOkSelectRows(): any[] {
+    let arr = unref(selectRows);
+    let selectedRowKeys = checkedKeys.value;
+    console.log('arr', arr);
+    if (!selectedRowKeys || selectedRowKeys.length <= 0) {
+      return [];
+    }
+    if (!arr || arr.length <= 0) {
+      return [];
+    }
+    let rows: any = [];
+    for (let key of selectedRowKeys) {
+      for (let i = 0; i < arr.length; i++) {
+        let combineKey = combineRowKey(arr[i]);
+        if (key === combineKey) {
+          rows.push(toRaw(arr[i]));
+          break;
+        }
+      }
+    }
+    return rows;
+  }
+  //update-end-author:taoyan date:2022-5-31 for: VUEN-1155 popup 选择数据时,会选择多条重复数据
+
   return [
-    { visibleChange, loadColumnsInfo, loadColumnsAndData, dynamicParamHandler, loadData, handleChangeInTable, combineRowKey, clickThenCheck, filterUnuseSelect, handleExport },
-    { hrefComponent, visible, rowSelection, checkedKeys, selectRows, pagination, dataSource, columns, indexColumnProps, loading, title, iSorter, queryInfo, queryParam, dictOptions },
+    {
+      visibleChange,
+      loadColumnsInfo,
+      loadColumnsAndData,
+      dynamicParamHandler,
+      loadData,
+      handleChangeInTable,
+      combineRowKey,
+      clickThenCheck,
+      filterUnuseSelect,
+      handleExport,
+      getOkSelectRows,
+    },
+    {
+      hrefComponent,
+      visible,
+      rowSelection,
+      checkedKeys,
+      selectRows,
+      pagination,
+      dataSource,
+      columns,
+      indexColumnProps,
+      loading,
+      title,
+      iSorter,
+      queryInfo,
+      queryParam,
+      dictOptions,
+    },
   ];
 }

+ 9 - 3
src/components/registerGlobComp.ts

@@ -1,7 +1,7 @@
 import type { App } from 'vue';
 import { Icon } from './Icon';
 import AIcon from '/@/components/jeecg/AIcon.vue';
-import { Button, UploadButton } from './Button';
+import { Button, JUploadButton } from './Button';
 import {
   // Need
   Button as AntButton,
@@ -47,9 +47,12 @@ import {
   InputNumber,
   Carousel,
   Popconfirm,
+  Skeleton,
+  Cascader,
+  Rate,
 } from 'ant-design-vue';
 
-const compList = [AntButton.Group, Icon, AIcon, UploadButton];
+const compList = [AntButton.Group, Icon, AIcon, JUploadButton];
 
 export function registerGlobComp(app: App) {
   compList.forEach((comp) => {
@@ -99,5 +102,8 @@ export function registerGlobComp(app: App) {
     .use(Slider)
     .use(InputNumber)
     .use(Carousel)
-    .use(Popconfirm);
+    .use(Popconfirm)
+    .use(Skeleton)
+    .use(Cascader)
+    .use(Rate);
 }

+ 3 - 1
src/hooks/event/useEventListener.ts

@@ -12,7 +12,9 @@ export interface UseEventParams {
   isDebounce?: boolean;
   wait?: number;
 }
-export function useEventListener({ el = window, name, listener, options, autoRemove = true, isDebounce = true, wait = 80 }: UseEventParams): { removeEvent: RemoveEventFn } {
+export function useEventListener({ el = window, name, listener, options, autoRemove = true, isDebounce = true, wait = 80 }: UseEventParams): {
+  removeEvent: RemoveEventFn;
+} {
   /* eslint-disable-next-line */
   let remove: RemoveEventFn = () => {};
   const isAddRef = ref(false);

+ 93 - 1
src/hooks/system/useJvxeMethods.ts

@@ -44,7 +44,7 @@ export function useJvxeMethod(requestAddOrEdit, classifyIntoFormData, tableRefs,
     getAllTable()
       .then((tables) => {
         let values = formRef.value.getFieldsValue();
-        return validateFormModelAndTables(formRef.value.validate, values, tables);
+        return validateFormModelAndTables(formRef.value.validate, values, tables, formRef.value.getProps);
       })
       .then((allValues) => {
         /** 一次性验证一对一的所有子表 */
@@ -86,3 +86,95 @@ export function useJvxeMethod(requestAddOrEdit, classifyIntoFormData, tableRefs,
   }
   return [handleChangeTabs, handleSubmit, requestSubTableData, formRef];
 }
+
+//update-begin-author:taoyan date:2022-6-16 for: 代码生成-原生表单用
+/**
+ * 校验多个表单和子表table,用于原生的antd-vue的表单
+ * @param activeKey 子表表单/vxe-table 所在tabs的 activeKey
+ * @param refMap 子表表单/vxe-table对应的ref对象 map结构
+ * 示例:
+ * useValidateAntFormAndTable(activeKey, {
+ *   'tableA': tableARef,
+ *   'formB': formBRef
+ * })
+ */
+export function useValidateAntFormAndTable(activeKey, refMap) {
+  /**
+   * 获取所有子表数据
+   */
+  async function getSubFormAndTableData() {
+    let formData = {};
+    let all = Object.keys(refMap);
+    let key = '';
+    for (let i = 0; i < all.length; i++) {
+      key = all[i];
+      let instance = refMap[key].value;
+      if (instance.isForm) {
+        let subFormData = await validateFormAndGetData(instance, key);
+        if (subFormData) {
+          formData[key + 'List'] = [subFormData];
+        }
+      } else {
+        let arr = await validateTableAndGetData(instance, key);
+        if (arr && arr.length > 0) {
+          formData[key + 'List'] = arr;
+        }
+      }
+    }
+    return formData;
+  }
+
+  /**
+   * 转换数据用 如果有数组转成逗号分割的格式
+   * @param data
+   */
+  function transformData(data) {
+    if (data) {
+      Object.keys(data).map((k) => {
+        if (data[k] instanceof Array) {
+          data[k] = data[k].join(',');
+        }
+      });
+    }
+    return data;
+  }
+
+  /**
+   * 子表table
+   * @param instance
+   * @param key
+   */
+  async function validateTableAndGetData(instance, key) {
+    const errors = await instance.validateTable();
+    if (!errors) {
+      return instance.getTableData();
+    } else {
+      activeKey.value = key;
+      // 自动重置scrollTop状态,防止出现白屏
+      instance.resetScrollTop(0);
+      return Promise.reject(1);
+    }
+  }
+
+  /**
+   * 子表表单
+   * @param instance
+   * @param key
+   */
+  async function validateFormAndGetData(instance, key) {
+    try {
+      let data = await instance.getFormData();
+      transformData(data);
+      return data;
+    } catch (e) {
+      activeKey.value = key;
+      return Promise.reject(e);
+    }
+  }
+
+  return {
+    getSubFormAndTableData,
+    transformData,
+  };
+}
+//update-end-author:taoyan date:2022-6-16 for: 代码生成-原生表单用

+ 29 - 9
src/hooks/system/useListPage.ts

@@ -1,4 +1,4 @@
-import { reactive, ref, Ref } from 'vue';
+import { reactive, ref, Ref, unref } from 'vue';
 import { merge } from 'lodash-es';
 import { DynamicProps } from '/#/utils';
 import { BasicTableProps, TableActionType, useTable } from '/@/components/Table';
@@ -20,7 +20,7 @@ interface ListPageOptions {
   pagination?: boolean;
   // 导出配置
   exportConfig?: {
-    url: string;
+    url: string | (() => string);
     // 导出文件名
     name?: string | (() => string);
     //导出参数
@@ -28,7 +28,9 @@ interface ListPageOptions {
   };
   // 导入配置
   importConfig?: {
-    url: string;
+    //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
+    url: string | (() => string);
+    //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
     // 导出成功后的回调
     success?: (fileInfo?: any) => void;
   };
@@ -63,17 +65,32 @@ export function useListPage(options: ListPageOptions) {
   async function onExportXls() {
     //update-begin---author:wangshuai ---date:20220411  for:导出新增自定义参数------------
     let { url, name, params } = options?.exportConfig ?? {};
-    if (url) {
+    let realUrl = typeof url === 'function' ? url() : url;
+    if (realUrl) {
       let title = typeof name === 'function' ? name() : name;
-      let paramsForm = await getForm().validate();
+      //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-
+      let paramsForm = {};
+      try {
+        paramsForm = await getForm().validate();
+      } catch (e) {
+        console.error(e);
+      }
+      //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-
       //如果参数不为空,则整合到一起
+      //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出动态设置mainId
       if (params) {
-        paramsForm = Object.assign({}, paramsForm, params);
+        Object.keys(params).map((k) => {
+          let temp = (params as object)[k];
+          if (temp) {
+            paramsForm[k] = unref(temp);
+          }
+        });
       }
+      //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导出动态设置mainId
       if (selectedRowKeys.value && selectedRowKeys.value.length > 0) {
         paramsForm['selections'] = selectedRowKeys.value.join(',');
       }
-      return handleExportXls(title as string, url, filterObj(paramsForm));
+      return handleExportXls(title as string, realUrl, filterObj(paramsForm));
       //update-end---author:wangshuai ---date:20220411  for:导出新增自定义参数--------------
     } else {
       $message.createMessage.warn('没有传递 exportConfig.url 参数');
@@ -84,8 +101,11 @@ export function useListPage(options: ListPageOptions) {
   // 导入 excel
   function onImportXls(file) {
     let { url, success } = options?.importConfig ?? {};
-    if (url) {
-      return handleImportXls(file, url, success || reload);
+    //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
+    let realUrl = typeof url === 'function' ? url() : url;
+    if (realUrl) {
+      return handleImportXls(file, realUrl, success || reload);
+      //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
     } else {
       $message.createMessage.warn('没有传递 importConfig.url 参数');
       return Promise.reject();

+ 5 - 1
src/hooks/system/useThirdLogin.ts

@@ -152,7 +152,11 @@ export function useThirdLogin() {
     if (!unref(thirdCaptcha)) {
       cmsFailed('请输入验证码');
     }
-    let params = { mobile: unref(thirdPhone), captcha: unref(thirdCaptcha), thirdUserUuid: unref(thirdUserUuid) };
+    let params = {
+      mobile: unref(thirdPhone),
+      captcha: unref(thirdCaptcha),
+      thirdUserUuid: unref(thirdUserUuid),
+    };
     defHttp.post({ url: '/sys/thirdLogin/bindingThirdPhone', params }, { isTransformResponse: false }).then((res) => {
       if (res.success) {
         bindingPhoneModal.value = false;

+ 59 - 1
src/hooks/web/usePermission.ts

@@ -18,12 +18,29 @@ import { isArray } from '/@/utils/is';
 import { useMultipleTabStore } from '/@/store/modules/multipleTab';
 
 // User permissions related operations
-export function usePermission() {
+export function usePermission(formData?) {
   const userStore = useUserStore();
   const appStore = useAppStore();
   const permissionStore = usePermissionStore();
   const { closeAll } = useTabs(router);
 
+  //==================================工作流权限判断-begin=========================================
+  function hasBpmPermission(code, type) {
+    // 禁用-type=2
+    // 显示-type=1
+    let codeList: string[] = [];
+    let permissionList = formData.permissionList;
+    if (permissionList && permissionList.length > 0) {
+      for (let item of permissionList) {
+        if (item.type == type) {
+          codeList.push(item.action);
+        }
+      }
+    }
+    return codeList.indexOf(code) >= 0;
+  }
+  //==================================工作流权限判断-end=========================================
+
   /**
    * Change permission mode
    */
@@ -71,6 +88,14 @@ export function usePermission() {
     if (PermissionModeEnum.BACK === permMode) {
       const allCodeList = permissionStore.getPermCodeList as string[];
       if (!isArray(value) && allCodeList && allCodeList.length > 0) {
+        //=============================工作流权限判断-显示-begin==============================================
+        if (formData) {
+          let code = value as string;
+          if (hasBpmPermission(code, '1') === true) {
+            return true;
+          }
+        }
+        //=============================工作流权限判断-显示-end==============================================
         return allCodeList.includes(value);
       }
       return (intersection(value, allCodeList) as string[]).length > 0;
@@ -81,6 +106,19 @@ export function usePermission() {
    * 是否禁用组件
    */
   function isDisabledAuth(value?: RoleEnum | RoleEnum[] | string | string[], def = true): boolean {
+    //=============================工作流权限判断-禁用-begin==============================================
+    if (formData) {
+      let code = value as string;
+      if (hasBpmPermission(code, '2') === true) {
+        return true;
+      }
+      //update-begin-author:taoyan date:2022-6-17 for: VUEN-1342【流程】编码方式 节点权限配置好后,未生效
+      if (isCodingButNoConfig(code) == true) {
+        return false;
+      }
+      //update-end-author:taoyan date:2022-6-17 for: VUEN-1342【流程】编码方式 节点权限配置好后,未生效
+    }
+    //=============================工作流权限判断-禁用-end==============================================
     return !hasPermission(value);
   }
 
@@ -107,5 +145,25 @@ export function usePermission() {
     resume();
   }
 
+  //update-begin-author:taoyan date:2022-6-17 for: VUEN-1342【流程】编码方式 节点权限配置好后,未生效
+  /**
+   * 判断是不是 代码里写了逻辑但是没有配置权限这种情况
+   */
+  function isCodingButNoConfig(code) {
+    let all = permissionStore.allAuthList;
+    if (all && all instanceof Array) {
+      let temp = all.filter((item) => item.action == code);
+      if (temp && temp.length > 0) {
+        if (temp[0].status == '0') {
+          return true;
+        }
+      } else {
+        return true;
+      }
+    }
+    return false;
+  }
+  //update-end-author:taoyan date:2022-6-17 for: VUEN-1342【流程】编码方式 节点权限配置好后,未生效
+
   return { changeRole, hasPermission, togglePermissionMode, refreshMenu, isDisabledAuth };
 }

+ 8 - 1
src/layouts/default/header/components/notify/NoticeList.vue

@@ -141,7 +141,14 @@
         props.onTitleClick && props.onTitleClick(item);
       }
 
-      return { prefixCls, getPagination, getData, handleTitleClick, isTitleClickable, PriorityTypes };
+      return {
+        prefixCls,
+        getPagination,
+        getData,
+        handleTitleClick,
+        isTitleClickable,
+        PriorityTypes,
+      };
     },
   });
 </script>

+ 13 - 2
src/layouts/default/header/components/notify/index.vue

@@ -50,7 +50,16 @@
   import { readAllMsg } from '/@/views/monitor/mynews/mynews.api';
   import { getToken } from '/@/utils/auth';
   export default defineComponent({
-    components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList, DetailModal, DynamicNotice },
+    components: {
+      Popover,
+      BellOutlined,
+      Tabs,
+      TabPane: Tabs.TabPane,
+      Badge,
+      NoticeList,
+      DetailModal,
+      DynamicNotice,
+    },
     setup() {
       const { prefixCls } = useDesign('header-notify');
       const instance: any = getCurrentInstance();
@@ -89,7 +98,9 @@
       // 获取系统消息
       async function loadData() {
         try {
-          let { anntMsgList, sysMsgList, anntMsgTotal, sysMsgTotal } = await listCementByUser({ pageSize: 5 });
+          let { anntMsgList, sysMsgList, anntMsgTotal, sysMsgTotal } = await listCementByUser({
+            pageSize: 5,
+          });
           listData.value[0].list = anntMsgList.map(mapAnnouncement);
           listData.value[1].list = sysMsgList.map(mapAnnouncement);
           listData.value[0].count = anntMsgTotal;

+ 4 - 1
src/layouts/default/header/components/user-dropdown/DepartSelect.vue

@@ -182,7 +182,10 @@
       if (!unref(isMultiDepart)) {
         resolve();
       } else {
-        const result = await selectDepart({ username: userStore.getUserInfo.username, orgCode: unref(departSelected) });
+        const result = await selectDepart({
+          username: userStore.getUserInfo.username,
+          orgCode: unref(departSelected),
+        });
         if (result.userInfo) {
           const userInfo = result.userInfo;
           userStore.setUserInfo(userInfo);

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

@@ -85,7 +85,14 @@
         return { realname, avatar: avatar || headerImg, desc };
       });
 
-      const getAvatarUrl = computed(() => getFileAccessHttpUrl(getUserInfo.value?.avatar));
+      const getAvatarUrl = computed(() => {
+        let { avatar } = getUserInfo.value;
+        if (avatar == headerImg) {
+          return avatar;
+        } else {
+          return getFileAccessHttpUrl(avatar);
+        }
+      });
 
       const [register, { openModal }] = useModal();
       /**

+ 3 - 10
src/layouts/default/header/index.vue

@@ -65,8 +65,6 @@
 
   import LoginSelect from '/@/views/sys/login/LoginSelect.vue';
   import { useUserStore } from '/@/store/modules/user';
-  import { getUserTenantId, setAuthCache } from '/@/utils/auth';
-  import { TENANT_ID } from '/@/enums/cacheEnum';
 
   export default defineComponent({
     name: 'LayoutHeader',
@@ -150,15 +148,10 @@
 
       function showLoginSelect() {
         //update-begin---author:liusq  Date:20220101  for:判断登录进来是否需要弹窗选择租户----
-        //判断当前用户的租户在缓存中是否存在
-        const userTenantId = getUserTenantId(userStore.getUserInfo.username);
-        if (!userTenantId && userTenantId != 0) {
-          //当前用户的租户不存在,弹窗选择
-          const loginInfo = toRaw(userStore.getLoginInfo) || {};
+        //判断是否是登陆进来
+        const loginInfo = toRaw(userStore.getLoginInfo) || {};
+        if (!!loginInfo.isLogin) {
           loginSelectRef.value.show(loginInfo);
-        } else {
-          //当前用户的租户存在,直接赋值
-          setAuthCache(TENANT_ID, userTenantId);
         }
         //update-end---author:liusq  Date:20220101  for:判断登录进来是否需要弹窗选择租户----
       }

+ 8 - 2
src/layouts/default/menu/index.vue

@@ -19,6 +19,7 @@
   import { useRootSetting } from '/@/hooks/setting/useRootSetting';
   import { useAppInject } from '/@/hooks/web/useAppInject';
   import { useDesign } from '/@/hooks/web/useDesign';
+  import { useLocaleStore } from '/@/store/modules/locale';
 
   export default defineComponent({
     name: 'LayoutMenu',
@@ -92,10 +93,15 @@
        * click menu
        * @param menu
        */
-
-      function handleMenuClick(path: string) {
+      //update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
+      const localeStore = useLocaleStore();
+      function handleMenuClick(path: string, item) {
+        if (item) {
+          localeStore.setPathTitle(path, item.title || '');
+        }
         go(path);
       }
+      //update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
 
       /**
        * before click menu

+ 4 - 4
src/layouts/default/setting/components/SettingFooter.vue

@@ -1,9 +1,9 @@
 <template>
   <div :class="prefixCls">
-    <!--    <a-button type="primary" block @click="handleCopy">-->
-    <!--      <CopyOutlined class="mr-2" />-->
-    <!--      {{ t('layout.setting.copyBtn') }}-->
-    <!--    </a-button>-->
+    <a-button type="primary" block @click="handleCopy">
+      <CopyOutlined class="mr-2" />
+      {{ t('layout.setting.copyBtn') }}
+    </a-button>
 
     <a-button color="warning" block @click="handleResetSetting" class="my-3">
       <RedoOutlined class="mr-2" />

+ 9 - 1
src/layouts/default/tabs/components/TabContent.vue

@@ -27,6 +27,7 @@
   import { useI18n } from '/@/hooks/web/useI18n';
   import { useTabDropdown } from '../useTabDropdown';
   import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
+  import { useLocaleStore } from '/@/store/modules/locale';
 
   export default defineComponent({
     name: 'TabContent',
@@ -42,10 +43,17 @@
       const { prefixCls } = useDesign('multiple-tabs-content');
       const { t } = useI18n();
 
+      //update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
+      const localeStore = useLocaleStore();
       const getTitle = computed(() => {
-        const { tabItem: { meta } = {} } = props;
+        const { tabItem: { meta, fullPath } = {} } = props;
+        let title = localeStore.getPathTitle(fullPath);
+        if (title) {
+          return title;
+        }
         return meta && t(meta.title as string);
       });
+      //update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
 
       const getIsTabs = computed(() => !props.isExtra);
 

+ 1 - 1
src/locales/lang/zh-CN/sys.ts

@@ -68,7 +68,7 @@ export default {
     forgetFormTitle: '重置密码',
 
     signInTitle: 'Jeecg Boot',
-    signInDesc: '是中国最具影响力的 企业级 低代码平台!',
+    signInDesc: '是中国最具影响力的 企业级低代码平台!在线开发,可视化拖拽设计,零代码实现80%的基础功能~',
     policy: '我同意xxx隐私政策',
     scanSign: `扫码后,即可完成登录`,
     scanSuccess: `扫码成功,登录中`,

+ 12 - 0
src/store/modules/locale.ts

@@ -13,12 +13,14 @@ const lsLocaleSetting = (ls.get(LOCALE_KEY) || localeSetting) as LocaleSetting;
 
 interface LocaleState {
   localInfo: LocaleSetting;
+  pathTitleMap: object;
 }
 
 export const useLocaleStore = defineStore({
   id: 'app-locale',
   state: (): LocaleState => ({
     localInfo: lsLocaleSetting,
+    pathTitleMap: {},
   }),
   getters: {
     getShowPicker(): boolean {
@@ -27,6 +29,11 @@ export const useLocaleStore = defineStore({
     getLocale(): LocaleType {
       return this.localInfo?.locale ?? 'zh_CN';
     },
+    //update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
+    getPathTitle: (state) => {
+      return (path) => state.pathTitleMap[path];
+    },
+    //update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
   },
   actions: {
     /**
@@ -46,6 +53,11 @@ export const useLocaleStore = defineStore({
         ...this.localInfo,
       });
     },
+    //update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
+    setPathTitle(path, title) {
+      this.pathTitleMap[path] = title;
+    },
+    //update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
   },
 });
 

+ 15 - 0
src/store/modules/permission.ts

@@ -53,6 +53,8 @@ interface PermissionState {
   allAuthList: AuthItem[];
   // 系统安全模式
   sysSafeMode: boolean;
+  // online子表按钮权限
+  onlineSubTableAuthMap: object;
 }
 export const usePermissionStore = defineStore({
   id: 'app-permission',
@@ -69,6 +71,7 @@ export const usePermissionStore = defineStore({
     authList: [],
     allAuthList: [],
     sysSafeMode: false,
+    onlineSubTableAuthMap: {},
   }),
   getters: {
     getPermCodeList(): string[] | number[] {
@@ -86,6 +89,12 @@ export const usePermissionStore = defineStore({
     getIsDynamicAddedRoute(): boolean {
       return this.isDynamicAddedRoute;
     },
+
+    //update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
+    getOnlineSubTableAuth: (state) => {
+      return (code) => state.onlineSubTableAuthMap[code];
+    },
+    //update-end-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
   },
   actions: {
     setPermCodeList(codeList: string[]) {
@@ -278,6 +287,12 @@ export const usePermissionStore = defineStore({
     setAllAuthList(authList: AuthItem[]) {
       this.allAuthList = authList;
     },
+
+    //update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
+    setOnlineSubTableAuth(code, hideBtnList) {
+      this.onlineSubTableAuthMap[code] = hideBtnList;
+    },
+    //update-end-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
   },
 });
 

+ 17 - 6
src/store/modules/user.ts

@@ -5,7 +5,7 @@ import { store } from '/@/store';
 import { RoleEnum } from '/@/enums/roleEnum';
 import { PageEnum } from '/@/enums/pageEnum';
 import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY, LOGIN_INFO_KEY, DB_DICT_DATA_KEY, TENANT_ID } from '/@/enums/cacheEnum';
-import { getAuthCache, setAuthCache } from '/@/utils/auth';
+import { getAuthCache, setAuthCache, removeAuthCache } from '/@/utils/auth';
 import { GetUserInfoModel, LoginParams, ThirdLoginParams } from '/@/api/sys/model/userModel';
 import { doLogout, getUserInfo, loginApi, phoneLoginApi, thirdLogin } from '/@/api/sys/user';
 import { useI18n } from '/@/hooks/web/useI18n';
@@ -16,6 +16,7 @@ import { RouteRecordRaw } from 'vue-router';
 import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
 import { isArray } from '/@/utils/is';
 import { useGlobSetting } from '/@/hooks/setting';
+import { JDragConfigEnum } from '/@/enums/jeecgEnum';
 import { useSso } from '/@/hooks/web/useSso';
 interface UserState {
   userInfo: Nullable<UserInfo>;
@@ -99,10 +100,6 @@ export const useUserStore = defineStore({
     setTenant(id) {
       this.tenantid = id;
       setAuthCache(TENANT_ID, id);
-      //update-begin---author:liusq  Date:20220102  for:保存用户租户id----
-      // @ts-ignore
-      setAuthCache(this.userInfo.username, id);
-      //update-end---author:liusq  Date:20220102  for:保存用户租户id----
     },
     setSessionTimeout(flag: boolean) {
       this.sessionTimeout = flag;
@@ -167,7 +164,10 @@ export const useUserStore = defineStore({
           router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw);
           permissionStore.setDynamicAddedRoute(true);
         }
-        await this.setLoginInfo(data);
+        await this.setLoginInfo({ ...data, isLogin: true });
+        //update-begin-author:liusq date:2022-5-5 for:登录成功后缓存拖拽模块的接口前缀
+        localStorage.setItem(JDragConfigEnum.DRAG_BASE_URL, useGlobSetting().domainUrl);
+        //update-end-author:liusq date:2022-5-5 for: 登录成功后缓存拖拽模块的接口前缀
         goHome && (await router.replace((userInfo && userInfo.homePath) || PageEnum.BASE_HOME));
       }
       return data;
@@ -233,11 +233,22 @@ export const useUserStore = defineStore({
           console.log('注销Token失败');
         }
       }
+
+      // //update-begin-author:taoyan date:2022-5-5 for: src/layouts/default/header/index.vue showLoginSelect方法 获取tenantId 退出登录后再次登录依然能获取到值,没有清空
+      // let username:any = this.userInfo && this.userInfo.username;
+      // if(username){
+      //   removeAuthCache(username)
+      // }
+      // //update-end-author:taoyan date:2022-5-5 for: src/layouts/default/header/index.vue showLoginSelect方法 获取tenantId 退出登录后再次登录依然能获取到值,没有清空
+
       this.setToken('');
       setAuthCache(TOKEN_KEY, null);
       this.setSessionTimeout(false);
       this.setUserInfo(null);
       this.setLoginInfo(null);
+      //update-begin-author:liusq date:2022-5-5 for:退出登录后清除拖拽模块的接口前缀
+      localStorage.removeItem(JDragConfigEnum.DRAG_BASE_URL);
+      //update-end-author:liusq date:2022-5-5 for: 退出登录后清除拖拽模块的接口前缀
 
       //如果开启单点登录,则跳转到单点统一登录中心
       const openSso = useGlobSetting().openSso;

+ 21 - 8
src/utils/auth/index.ts

@@ -10,7 +10,7 @@ const isLocal = permissionCacheType === CacheTypeEnum.LOCAL;
  * 获取token
  */
 export function getToken() {
-  return getAuthCache(TOKEN_KEY);
+  return getAuthCache<string>(TOKEN_KEY);
 }
 /**
  * 获取登录信息
@@ -22,13 +22,7 @@ export function getLoginBackInfo() {
  * 获取租户id
  */
 export function getTenantId() {
-  return getAuthCache(TENANT_ID);
-}
-/**
- * 获取用户租户id
- */
-export function getUserTenantId(username) {
-  return getAuthCache(username);
+  return getAuthCache<string>(TENANT_ID);
 }
 
 export function getAuthCache<T>(key: BasicKeys) {
@@ -40,6 +34,25 @@ export function setAuthCache(key: BasicKeys, value) {
   const fn = isLocal ? Persistent.setLocal : Persistent.setSession;
   return fn(key, value, true);
 }
+
+/**
+ * 设置动态key
+ * @param key
+ * @param value
+ */
+export function setCacheByDynKey(key, value) {
+  const fn = isLocal ? Persistent.setLocal : Persistent.setSession;
+  return fn(key, value, true);
+}
+
+/**
+ * 获取动态key
+ * @param key
+ */
+export function getCacheByDynKey<T>(key) {
+  const fn = isLocal ? Persistent.getLocal : Persistent.getSession;
+  return fn(key) as T;
+}
 /**
  * 移除缓存中的某个属性
  * @param key

+ 10 - 0
src/utils/common/compUtils.ts

@@ -315,3 +315,13 @@ export function bindMapFormSchema<T>(spanMap, spanTypeDef: T) {
     );
   };
 }
+
+/**
+ * 字符串是否为null或null字符串
+ * @param str
+ * @return {boolean}
+ */
+export function stringIsNull(str) {
+  // 两个 == 可以同时判断 null 和 undefined
+  return str == null || str === 'null' || str === 'undefined';
+}

+ 38 - 7
src/utils/common/renderUtils.ts

@@ -5,8 +5,9 @@ import { Tinymce } from '/@/components/Tinymce';
 import Icon from '/@/components/Icon';
 import { getDictItemsByCode } from '/@/utils/dict/index';
 import { filterMultiDictText } from '/@/utils/dict/JDictSelectUtil.js';
-import { loadCategoryData } from '/@/api/common/api';
 import { isEmpty } from '/@/utils/is';
+import { useMessage } from '/@/hooks/web/useMessage';
+const { createMessage } = useMessage();
 
 const render = {
   /**
@@ -18,7 +19,12 @@ const render = {
       return h(
         'span',
         avatarList.map((item) => {
-          return h(Avatar, { src: getFileAccessHttpUrl(item), shape: 'square', size: 'default', style: { marginRight: '5px' } });
+          return h(Avatar, {
+            src: getFileAccessHttpUrl(item),
+            shape: 'square',
+            size: 'default',
+            style: { marginRight: '5px' },
+          });
         })
       );
     } else {
@@ -39,7 +45,7 @@ const render = {
    */
   renderDict: (v, code, renderTag = false) => {
     let text = '';
-    let array = getDictItemsByCode(code);
+    let array = getDictItemsByCode(code) || [];
     let obj = array.filter((item) => {
       return item.value == v;
     });
@@ -54,11 +60,12 @@ const render = {
    */
   renderImage: ({ text }) => {
     if (!text) {
+      //update-begin-author:taoyan date:2022-5-24 for:  VUEN-1084 【vue3】online表单测试发现的新问题 41、生成的代码,树默认图大小未改
       return h(
         Avatar,
-        { shape: 'square', size: 'large' },
+        { shape: 'square', size: 25 },
         {
-          icon: () => h(Icon, { icon: 'ant-design:file-image-outlined', size: 30 }),
+          icon: () => h(Icon, { icon: 'ant-design:file-image-outlined', size: 25 }),
         }
       );
     }
@@ -66,9 +73,15 @@ const render = {
     return h(
       'span',
       avatarList.map((item) => {
-        return h(Avatar, { src: getFileAccessHttpUrl(item), shape: 'square', size: 'large', style: { marginRight: '5px' } });
+        return h(Avatar, {
+          src: getFileAccessHttpUrl(item),
+          shape: 'square',
+          size: 25,
+          style: { marginRight: '5px' },
+        });
       })
     );
+    //update-end-author:taoyan date:2022-5-24 for:  VUEN-1084 【vue3】online表单测试发现的新问题 41、生成的代码,树默认图大小未改
   },
   /**
    * 渲染 Tooltip
@@ -141,4 +154,22 @@ const render = {
     return isEmpty(text) ? h('span', text) : h(Tag, { color }, () => text);
   },
 };
-export { render };
+
+/**
+ * 文件下载
+ */
+function downloadFile(url) {
+  if (!url) {
+    createMessage.warning('未知的文件');
+    return;
+  }
+  if (url.indexOf(',') > 0) {
+    url = url.substring(0, url.indexOf(','));
+  }
+  url = getFileAccessHttpUrl(url.split(',')[0]);
+  if (url) {
+    window.open(url);
+  }
+}
+
+export { render, downloadFile };

+ 15 - 1
src/utils/common/vxeUtils.ts

@@ -1,3 +1,5 @@
+import { getValueType } from '/@/utils';
+
 export const VALIDATE_FAILED = Symbol();
 /**
  * 一次性验证主表单和所有的次表单(新版本)
@@ -5,7 +7,7 @@ export const VALIDATE_FAILED = Symbol();
  * @param cases 接收一个数组,每项都是一个JEditableTable实例
  * @returns {Promise<any>}
  */
-export async function validateFormModelAndTables(validate, formData, cases, autoJumpTab?) {
+export async function validateFormModelAndTables(validate, formData, cases, props, autoJumpTab?) {
   if (!(validate && typeof validate === 'function')) {
     throw `validate 参数需要的是一个方法,而传入的却是${typeof validate}`;
   }
@@ -14,6 +16,18 @@ export async function validateFormModelAndTables(validate, formData, cases, auto
     // 验证主表表单
     validate()
       .then(() => {
+        //update-begin---author:wangshuai ---date:20220507  for:[VUEN-912]一对多用户组件(所有风格,单表和树没问题)保存报错------------
+        for (let data in formData) {
+          //如果该数据是数组
+          if (formData[data] instanceof Array) {
+            let valueType = getValueType(props, data);
+            //如果是字符串类型的需要变成以逗号分割的字符串
+            if (valueType === 'string') {
+              formData[data] = formData[data].join(',');
+            }
+          }
+        }
+        //update-end---author:wangshuai ---date:20220507  for:[VUEN-912]一对多用户组件(所有风格,单表和树没问题)保存报错--------------
         resolve(formData);
       })
       .catch(() => {

+ 12 - 5
src/utils/dict/JDictSelectUtil.js

@@ -9,9 +9,10 @@ import { ajaxGetDictItems, getDictItemsByCode } from './index';
 /**
  * 获取字典数组
  * @param dictCode 字典Code
+ * @param isTransformResponse 是否转换返回结果
  * @return List<Map>
  */
-export async function initDictOptions(dictCode) {
+export async function initDictOptions(dictCode, isTransformResponse = true) {
   if (!dictCode) {
     return '字典Code不能为空!';
   }
@@ -20,11 +21,14 @@ export async function initDictOptions(dictCode) {
     let res = {};
     res.result = getDictItemsByCode(dictCode);
     res.success = true;
-    return res;
+    if (isTransformResponse) {
+      return res.result;
+    } else {
+      return res;
+    }
   }
   //获取字典数组
-  let res = await ajaxGetDictItems(dictCode);
-  return res;
+  return await ajaxGetDictItems(dictCode, {}, { isTransformResponse });
 }
 
 /**
@@ -125,7 +129,10 @@ export function filterDictTextByCache(dictCode, key) {
 export async function getDictItems(dictCode, params) {
   //优先从缓存中读取字典配置
   if (getDictItemsByCode(dictCode)) {
-    let desformDictItems = getDictItemsByCode(dictCode).map((item) => ({ ...item, label: item.text }));
+    let desformDictItems = getDictItemsByCode(dictCode).map((item) => ({
+      ...item,
+      label: item.text,
+    }));
     return desformDictItems;
   }
 

+ 8 - 1
src/utils/dict/index.ts

@@ -24,12 +24,19 @@ export const initDictOptions = (code) => {
     });
   }
   //2.获取字典数组
+  //update-begin-author:taoyan date:2022-6-21 for: 字典数据请求前将参数编码处理,但是不能直接编码,因为可能之前已经编码过了
+  if (code.indexOf(',') > 0 && code.indexOf(' ') > 0) {
+    // 编码后类似sys_user%20where%20username%20like%20xxx' 是不包含空格的,这里判断如果有空格和逗号说明需要编码处理
+    code = encodeURI(code);
+  }
+  //update-end-author:taoyan date:2022-6-21 for: 字典数据请求前将参数编码处理,但是不能直接编码,因为可能之前已经编码过了
   return defHttp.get({ url: `/sys/dict/getDictItems/${code}` });
 };
 /**
  * 获取字典数组
  * @param code 字典Code
  * @param params 查询参数
+ * @param options 查询配置
  * @return List<Map>
  */
-export const ajaxGetDictItems = (code, params) => defHttp.get({ url: `/sys/dict/getDictItems/${code}`, params });
+export const ajaxGetDictItems = (code, params, options?) => defHttp.get({ url: `/sys/dict/getDictItems/${code}`, params }, options);

Some files were not shown because too many files changed in this diff