Переглянути джерело

jeecgboot3.4.2版本发布,基础功能升级

zhangdaiscott 2 роки тому
батько
коміт
b777ac0dc4
100 змінених файлів з 1891 додано та 1302 видалено
  1. 7 0
      src/api/common/api.ts
  2. 0 0
      src/assets/svg/fileType/excel.svg
  3. 0 0
      src/assets/svg/fileType/other.svg
  4. 0 0
      src/assets/svg/fileType/pdf.svg
  5. 0 0
      src/assets/svg/fileType/txt.svg
  6. 0 0
      src/assets/svg/fileType/word.svg
  7. 3 2
      src/components/Drawer/src/BasicDrawer.vue
  8. 1 0
      src/components/Drawer/src/props.ts
  9. 2 0
      src/components/Drawer/src/typing.ts
  10. 14 2
      src/components/Form/src/components/FormItem.vue
  11. 6 1
      src/components/Form/src/jeecg/components/JAddInput.vue
  12. 13 15
      src/components/Form/src/jeecg/components/JCategorySelect.vue
  13. 5 5
      src/components/Form/src/jeecg/components/JCheckbox.vue
  14. 5 8
      src/components/Form/src/jeecg/components/JCodeEditor.vue
  15. 17 5
      src/components/Form/src/jeecg/components/JDictSelectTag.vue
  16. 8 1
      src/components/Form/src/jeecg/components/JEasyCron/EasyCronInput.vue
  17. 3 1
      src/components/Form/src/jeecg/components/JEllipsis.vue
  18. 0 4
      src/components/Form/src/jeecg/components/JFormContainer.vue
  19. 3 1
      src/components/Form/src/jeecg/components/JImportModal.vue
  20. 7 1
      src/components/Form/src/jeecg/components/JInputPop.vue
  21. 9 1
      src/components/Form/src/jeecg/components/JPopup.vue
  22. 37 2
      src/components/Form/src/jeecg/components/JSearchSelect.vue
  23. 3 3
      src/components/Form/src/jeecg/components/JSelectDept.vue
  24. 20 1
      src/components/Form/src/jeecg/components/JSelectMultiple.vue
  25. 3 3
      src/components/Form/src/jeecg/components/JSelectPosition.vue
  26. 3 3
      src/components/Form/src/jeecg/components/JSelectRole.vue
  27. 4 4
      src/components/Form/src/jeecg/components/JSelectUser.vue
  28. 4 3
      src/components/Form/src/jeecg/components/JSelectUserByDept.vue
  29. 9 1
      src/components/Form/src/jeecg/components/JSwitch.vue
  30. 8 1
      src/components/Form/src/jeecg/components/JTreeSelect.vue
  31. 9 1
      src/components/Form/src/jeecg/components/base/JTreeBiz.vue
  32. 7 5
      src/components/Form/src/jeecg/components/modal/DeptSelectModal.vue
  33. 26 2
      src/components/Form/src/jeecg/components/modal/JPopupOnlReportModal.vue
  34. 20 3
      src/components/Form/src/jeecg/components/modal/PositionSelectModal.vue
  35. 20 3
      src/components/Form/src/jeecg/components/modal/UserSelectModal.vue
  36. 6 1
      src/components/Form/src/types/form.ts
  37. 2 2
      src/components/Icon/src/IconPicker.vue
  38. 10 1
      src/components/JVxeCustom/src/components/JVxeImageCell.vue
  39. 2 1
      src/components/JVxeCustom/src/components/JVxeSelectDictSearchCell.ts
  40. 29 33
      src/components/Modal/src/BasicModal.vue
  41. 24 41
      src/components/Modal/src/components/ModalClose.vue
  42. 10 3
      src/components/Modal/src/index.less
  43. 2 0
      src/components/Modal/src/typing.ts
  44. 14 2
      src/components/Page/src/PageWrapper.vue
  45. 2 1
      src/components/Scrollbar/src/bar.ts
  46. 8 1
      src/components/SimpleMenu/src/SimpleMenu.vue
  47. 6 1
      src/components/SimpleMenu/src/SimpleSubMenu.vue
  48. 26 13
      src/components/Table/src/BasicTable.vue
  49. 2 2
      src/components/Table/src/components/HeaderCell.vue
  50. 1 1
      src/components/Table/src/components/TableHeader.vue
  51. 4 1
      src/components/Table/src/components/editable/CellComponent.ts
  52. 13 9
      src/components/Table/src/components/settings/ColumnSetting.vue
  53. 1 1
      src/components/Table/src/components/settings/SizeSetting.vue
  54. 5 5
      src/components/Table/src/hooks/useColumns.ts
  55. 4 1
      src/components/Table/src/hooks/useCustomRow.ts
  56. 5 1
      src/components/Table/src/hooks/useDataSource.ts
  57. 7 6
      src/components/Table/src/hooks/useRowSelection.ts
  58. 2 3
      src/components/Table/src/hooks/useTableFooter.ts
  59. 7 1
      src/components/Table/src/hooks/useTableForm.ts
  60. 2 1
      src/components/Table/src/types/table.ts
  61. 8 1
      src/components/Tinymce/src/Editor.vue
  62. 10 1
      src/components/Tinymce/src/ImgUpload.vue
  63. 3 2
      src/components/Tree/index.ts
  64. 456 0
      src/components/Tree/src/BasicTree.vue
  65. 2 6
      src/components/Tree/src/TreeIcon.ts
  66. 171 0
      src/components/Tree/src/components/TreeHeader.vue
  67. 207 0
      src/components/Tree/src/hooks/useTree.ts
  68. 195 0
      src/components/Tree/src/types/tree.ts
  69. 52 0
      src/components/Tree/style/index.less
  70. 1 0
      src/components/Tree/style/index.ts
  71. 5 0
      src/components/Tree_backup/index.ts
  72. 2 1
      src/components/Tree_backup/src/Tree.vue
  73. 7 1
      src/components/Tree_backup/src/TreeHeader.vue
  74. 17 0
      src/components/Tree_backup/src/TreeIcon.ts
  75. 0 0
      src/components/Tree_backup/src/props.ts
  76. 0 0
      src/components/Tree_backup/src/typing.ts
  77. 0 0
      src/components/Tree_backup/src/useTree.ts
  78. 5 1
      src/components/Upload/src/UploadModal.vue
  79. 8 1
      src/components/Upload/src/UploadPreviewModal.vue
  80. 11 1
      src/components/Upload/src/useUpload.ts
  81. 12 2
      src/components/Verify/src/DragVerify.vue
  82. 3 1
      src/components/Verify/src/ImgRotate.vue
  83. 21 15
      src/components/jeecg/JPrompt/JPrompt.vue
  84. 1 0
      src/components/jeecg/JPrompt/hooks/useJPrompt.ts
  85. 28 2
      src/components/jeecg/OnLine/JPopupOnlReport.vue
  86. 63 11
      src/components/jeecg/OnLine/SearchFormItem.vue
  87. 1 1
      src/components/jeecg/OnLine/hooks/usePopBiz.ts
  88. 6 0
      src/components/jeecg/OnLine/types/onlineConfig.ts
  89. 0 401
      src/components/jeecg/super/superquery/SuperQuery.vue
  90. 0 98
      src/components/jeecg/super/superquery/SuperQueryValComponent.vue
  91. 0 524
      src/components/jeecg/super/superquery/useSuperQuery.ts
  92. 6 1
      src/components/jeecg/thirdApp/JThirdAppButton.vue
  93. 3 1
      src/components/jeecg/thirdApp/JThirdAppDropdown.vue
  94. 4 0
      src/components/registerGlobComp.ts
  95. 15 0
      src/hooks/web/useTabs.ts
  96. 4 2
      src/layouts/default/header/components/user-dropdown/DepartSelect.vue
  97. 16 2
      src/layouts/default/header/index.vue
  98. 23 3
      src/layouts/default/menu/index.vue
  99. 48 8
      src/layouts/default/setting/SettingDrawer.tsx
  100. 7 1
      src/layouts/default/setting/components/SwitchItem.vue

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

@@ -141,3 +141,10 @@ export const getFileblob = (url, parameter) => {
     { isTransformResponse: false }
   );
 };
+
+/**
+ * 【用于评论功能】自定义文件上传-方法
+ */
+export const uploadMyFile = (url, data) => {
+  return defHttp.uploadMyFile(url, data);
+};

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
src/assets/svg/fileType/excel.svg


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
src/assets/svg/fileType/other.svg


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
src/assets/svg/fileType/pdf.svg


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
src/assets/svg/fileType/txt.svg


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
src/assets/svg/fileType/word.svg


+ 3 - 2
src/components/Drawer/src/BasicDrawer.vue

@@ -70,13 +70,14 @@
           visible: unref(visibleRef),
         };
         opt.title = undefined;
-        const { isDetail, width, wrapClassName, getContainer } = opt;
+        let { isDetail, width, wrapClassName, getContainer } = opt;
         if (isDetail) {
           if (!width) {
             opt.width = '100%';
           }
           const detailCls = `${prefixCls}__detail`;
-          opt.wrapClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
+          wrapClassName = opt['class'] ? opt['class'] : wrapClassName;
+          opt.class = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
 
           if (!getContainer) {
             // TODO type error?

+ 1 - 0
src/components/Drawer/src/props.ts

@@ -25,6 +25,7 @@ export const footerProps = {
   },
 };
 export const basicProps = {
+  class: {type: [String, Object, Array]},
   isDetail: { type: Boolean },
   title: { type: String, default: '' },
   loadingText: { type: String },

+ 2 - 0
src/components/Drawer/src/typing.ts

@@ -133,6 +133,8 @@ export interface DrawerProps extends DrawerFooterProps {
    * The class name of the container of the Drawer dialog.
    * @type string
    */
+  class?: string;
+  // 兼容老版本的写法(后续可能会删除,优先写class)
   wrapClassName?: string;
 
   /**

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

@@ -264,14 +264,26 @@
        * @updateBy:zyf
        */
       function renderLabelHelpMessage() {
-        const { label, helpMessage, helpComponentProps, subLabel } = props.schema;
+        //update-begin-author:taoyan date:2022-9-7 for: VUEN-2061【样式】online表单超出4个 .. 省略显示
+        //label宽度支持自定义
+        const { label, helpMessage, helpComponentProps, subLabel, labelLength } = props.schema;
+        let showLabel:string = (label+'')
+        if(labelLength && showLabel.length>4){
+          showLabel = showLabel.substr(0, labelLength);
+        }
+        const titleObj = {title: label}
         const renderLabel = subLabel ? (
           <span>
             {label} <span class="text-secondary">{subLabel}</span>
           </span>
         ) : (
-          label
+          labelLength ? (
+            <label {...titleObj}>{showLabel}</label>
+          ) : (
+            label
+          ) 
         );
+        //update-end-author:taoyan date:2022-9-7 for: VUEN-2061【样式】online表单超出4个 .. 省略显示
         const getHelpMessage = isFunction(helpMessage) ? helpMessage(unref(getValues)) : helpMessage;
         if (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0)) {
           return renderLabel;

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

@@ -2,7 +2,12 @@
   <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 > min" 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">

+ 13 - 15
src/components/Form/src/jeecg/components/JCategorySelect.vue

@@ -102,17 +102,17 @@
         };
         console.info(param);
         loadTreeData(param).then((res) => {
-          if (res && res.length > 0) {
-            for (let i of res) {
-              i.value = i.key;
-              if (i.leaf == false) {
-                i.isLeaf = false;
-              } else if (i.leaf == true) {
-                i.isLeaf = true;
-              }
-            }
-            treeData.value = res;
-          }
+            if(res && res.length>0){
+                for (let i of res) {
+                    i.value = i.key;
+                    if (i.leaf == false) {
+                        i.isLeaf = false;
+                    } else if (i.leaf == true) {
+                        i.isLeaf = true;
+                    }
+                }
+                treeData.value = res;
+						}
         });
       }
 
@@ -149,13 +149,12 @@
 
       function asyncLoadTreeData(treeNode) {
         let dataRef = treeNode.dataRef;
-        return new Promise((resolve) => {
-          if (treeNode.children.length > 0) {
+        return new Promise<void>((resolve) => {
+          if (treeNode.children && treeNode.children.length > 0) {
             resolve();
             return;
           }
           let pid = dataRef.key;
-          console.info(treeNode);
           let param = {
             pid: pid,
             condition: props.condition,
@@ -178,7 +177,6 @@
       }
 
       function addChildren(pid, children, treeArray) {
-        console.info('treeArray', treeArray);
         if (treeArray && treeArray.length > 0) {
           for (let item of treeArray) {
             if (item.key == pid) {

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

@@ -11,7 +11,7 @@
   export default defineComponent({
     name: 'JCheckbox',
     props: {
-      value: propTypes.oneOfType([propTypes.string, propTypes.number]),
+      value:propTypes.oneOfType([propTypes.string, propTypes.number]),
       dictCode: propTypes.string,
       options: {
         type: Array,
@@ -31,11 +31,11 @@
       watchEffect(() => {
         //update-begin-author:taoyan date:2022-7-4 for:issues/I5E7YX AUTO在线表单进入功能测试之后一直卡在功能测试界面
         let temp = props.value;
-        if (!temp && temp !== 0) {
-          checkboxArray.value = [];
-        } else {
+        if(!temp && temp!==0){
+          checkboxArray.value = []
+        }else{
           temp = temp + '';
-          checkboxArray.value = temp.split(',');
+          checkboxArray.value = temp.split(',')
         }
         //update-end-author:taoyan date:2022-7-4 for:issues/I5E7YX AUTO在线表单进入功能测试之后一直卡在功能测试界面
         //update-begin-author:taoyan date:20220401 for: 调用表单的 resetFields不会清空当前信息,界面显示上一次的数据

+ 5 - 8
src/components/Form/src/jeecg/components/JCodeEditor.vue

@@ -188,16 +188,13 @@
         }
       );
       //update-end-author:taoyan date:2022-5-9 for: codeEditor禁用功能
-
+      
       // 支持动态设置语言
-      watch(
-        () => props.language,
-        (val) => {
-          if (val && coder) {
-            coder.setOption('mode', val);
-          }
+      watch(()=>props.language, (val)=>{
+        if(val && coder){
+          coder.setOption('mode', val);
         }
-      );
+      });
 
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
       return {

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

@@ -22,8 +22,16 @@
         <LoadingOutlined />
       </template>
     </a-input>
-    <a-select v-else :placeholder="placeholder" v-bind="attrs" v-model:value="state" :filterOption="handleFilterOption" :getPopupContainer="getPopupContainer" @change="handleChange">
-      <a-select-option v-if="showChooseOption" :value="undefined">请选择</a-select-option>
+    <a-select
+      v-else
+      :placeholder="placeholder"
+      v-bind="attrs"
+      v-model:value="state"
+      :filterOption="handleFilterOption"
+      :getPopupContainer="getPopupContainer"
+      @change="handleChange"
+    >
+      <a-select-option v-if="showChooseOption" :value="null">请选择…</a-select-option>
       <template v-for="item in dictOptions" :key="`${item.value}`">
         <a-select-option :value="item.value">
           <span style="display: inline-block; width: 100%" :title="item.label">
@@ -35,13 +43,14 @@
   </template>
 </template>
 <script lang="ts">
-  import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted } from 'vue';
+  import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted, nextTick } from 'vue';
+  import { Form } from 'ant-design-vue';
   import { propTypes } from '/@/utils/propTypes';
   import { useAttrs } from '/@/hooks/core/useAttrs';
-  import { initDictOptions } from '/@/utils/dict/index';
+  import { initDictOptions } from '/@/utils/dict';
   import { get, omit } from 'lodash-es';
   import { useRuleFormItem } from '/@/hooks/component/useFormItem';
-  import { CompTypeEnum } from '/@/enums/CompTypeEnum.ts';
+  import { CompTypeEnum } from '/@/enums/CompTypeEnum';
   import { LoadingOutlined } from '@ant-design/icons-vue';
 
   export default defineComponent({
@@ -69,6 +78,7 @@
     },
     emits: ['options-change', 'change'],
     setup(props, { emit, refs }) {
+      const { onFieldChange } = Form.useInjectFormItemContext();
       const emitData = ref<any[]>([]);
       const dictOptions = ref<any[]>([]);
       const attrs = useAttrs();
@@ -108,6 +118,7 @@
         () => {
           if (props.value === '') {
             emit('change', '');
+            nextTick(() => onFieldChange());
           }
         }
       );
@@ -132,6 +143,7 @@
 
       function handleChange(e) {
         emitData.value = [e?.target?.value || e];
+        nextTick(() => onFieldChange());
       }
 
       /** 用于搜索下拉框中的内容 */

+ 8 - 1
src/components/Form/src/jeecg/components/JEasyCron/EasyCronInput.vue

@@ -8,7 +8,14 @@
         </a>
       </template>
     </a-input>
-    <EasyCronModal @register="registerModal" v-model:value="editCronValue" :exeStartTime="exeStartTime" :hideYear="hideYear" :remote="remote" :hideSecond="hideSecond" />
+    <EasyCronModal
+      @register="registerModal"
+      v-model:value="editCronValue"
+      :exeStartTime="exeStartTime"
+      :hideYear="hideYear"
+      :remote="remote"
+      :hideSecond="hideSecond"
+    />
   </div>
 </template>
 

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

@@ -15,5 +15,7 @@
     length: propTypes.number.def(25),
   });
   //显示的文本
-  const showText = computed(() => (props.value ? (props.value.length > props.length ? props.value.slice(0, props.length) + '...' : props.value) : props.value));
+  const showText = computed(() =>
+    props.value ? (props.value.length > props.length ? props.value.slice(0, props.length) + '...' : props.value) : props.value
+  );
 </script>

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

@@ -1,10 +1,6 @@
 <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>

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

@@ -15,7 +15,9 @@
       <!--页脚-->
       <template #footer>
         <a-button @click="handleClose">关闭</a-button>
-        <a-button type="primary" @click="handleImport" :disabled="uploadDisabled" :loading="uploading">{{ uploading ? '上传中...' : '开始上传' }}</a-button>
+        <a-button type="primary" @click="handleImport" :disabled="uploadDisabled" :loading="uploading">{{
+          uploading ? '上传中...' : '开始上传'
+        }}</a-button>
       </template>
     </BasicModal>
   </div>

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

@@ -1,5 +1,11 @@
 <template>
-  <a-popover trigger="contextmenu" v-model:visible="visible" :overlayClassName="`${prefixCls}-popover`" :getPopupContainer="getPopupContainer" :placement="position">
+  <a-popover
+    trigger="contextmenu"
+    v-model:visible="visible"
+    :overlayClassName="`${prefixCls}-popover`"
+    :getPopupContainer="getPopupContainer"
+    :placement="position"
+  >
     <template #title>
       <span>{{ title }}</span>
       <span style="float: right" title="关闭">

+ 9 - 1
src/components/Form/src/jeecg/components/JPopup.vue

@@ -13,7 +13,15 @@
       <!-- 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>
+    <JPopupOnlReportModal
+      @register="regModal"
+      :code="code"
+      :multi="multi"
+      :sorter="sorter"
+      :groupId="uniqGroupId"
+      :param="param"
+      @ok="callBack"
+    ></JPopupOnlReportModal>
   </div>
 </template>
 <script lang="ts">

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

@@ -64,6 +64,15 @@
         type: Function,
         default: (node) => node.parentNode,
       },
+      //是否在有值后立即触发change
+      immediateChange: propTypes.bool.def(false),
+      //update-begin-author:taoyan date:2022-8-15 for: VUEN-1971 【online 专项测试】关联记录和他表字段 1
+      //支持传入查询参数,如排序信息
+      params:{
+        type: Object,
+        default: ()=>{}
+      },
+      //update-end-author:taoyan date:2022-8-15 for: VUEN-1971 【online 专项测试】关联记录和他表字段 1
     },
     emits: ['change', 'update:value'],
     setup(props, { emit, refs }) {
@@ -116,11 +125,12 @@
         const currentLoad = unref(lastLoad);
         options.value = [];
         loading.value = true;
+        let keywordInfo = getKeywordParam(value);
         // 字典code格式:table,text,code
         defHttp
           .get({
             url: `/sys/dict/loadDict/${props.dict}`,
-            params: { keyword: value, pageSize: props.pageSize },
+            params: { keyword: keywordInfo, pageSize: props.pageSize },
           })
           .then((res) => {
             loading.value = false;
@@ -152,11 +162,21 @@
                   label: res,
                 };
                 selectedAsyncValue.value = { ...obj };
+                //update-begin-author:taoyan date:2022-8-11 for: 值改变触发change事件--用于online关联记录配置页面
+                if(props.immediateChange == true){
+                  emit('change', value);
+                }
+                //update-end-author:taoyan date:2022-8-11 for: 值改变触发change事件--用于online关联记录配置页面
               }
             });
           }
         } else {
           selectedValue.value = value.toString();
+          //update-begin-author:taoyan date:2022-8-11 for: 值改变触发change事件--用于online他表字段配置界面
+          if(props.immediateChange == true){
+            emit('change', value.toString());
+          }
+          //update-end-author:taoyan date:2022-8-11 for: 值改变触发change事件--用于online他表字段配置界面
         }
       }
 
@@ -191,10 +211,11 @@
           } else {
             //异步一开始也加载一点数据
             loading.value = true;
+            let keywordInfo = getKeywordParam('');
             defHttp
               .get({
                 url: `/sys/dict/loadDict/${dict}`,
-                params: { pageSize: pageSize, keyword: '' },
+                params: { pageSize: pageSize, keyword: keywordInfo },
               })
               .then((res) => {
                 loading.value = false;
@@ -255,6 +276,20 @@
         }
         // update-end-author:taoyan date:20220407 for: getPopupContainer一直有值 导致popContainer的逻辑永远走不进去,把它挪到前面判断
       }
+
+      //update-begin-author:taoyan date:2022-8-15 for: VUEN-1971 【online 专项测试】关联记录和他表字段 1
+      //获取关键词参数 支持设置排序信息
+      function getKeywordParam(text){
+        // 如果设定了排序信息,需要写入排序信息,在关键词后加 [orderby:create_time,desc]
+        if(props.params && props.params.column && props.params.order){
+          let temp = text||''
+          return temp+'[orderby:'+props.params.column+','+props.params.order+']'
+        }else{
+          return text;
+        }
+      }
+      //update-end-author:taoyan date:2022-8-15 for: VUEN-1971 【online 专项测试】关联记录和他表字段 1
+      
       return {
         attrs,
         options,

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

@@ -13,7 +13,7 @@
   import { propTypes } from '/@/utils/propTypes';
   import { useRuleFormItem } from '/@/hooks/component/useFormItem';
   import { useAttrs } from '/@/hooks/core/useAttrs';
-  import { SelectTypes } from 'ant-design-vue/es/select';
+  import { SelectValue } from 'ant-design-vue/es/select';
 
   export default defineComponent({
     name: 'JSelectDept',
@@ -29,13 +29,13 @@
     },
     emits: ['options-change', 'change', 'select', 'update:value'],
     setup(props, { emit, refs }) {
-      const emitData = ref<object>();
+      const emitData = ref<any[]>();
       //注册model
       const [regModal, { openModal }] = useModal();
       //表单值
       const [state] = useRuleFormItem(props, 'value', 'change', emitData);
       //下拉框选项值
-      const selectOptions = ref<SelectTypes['options']>([]);
+      const selectOptions = ref<SelectValue>([]);
       //下拉框选中值
       let selectValues = reactive<Recordable>({
         value: [],

+ 20 - 1
src/components/Form/src/jeecg/components/JSelectMultiple.vue

@@ -1,6 +1,15 @@
 <!--字典下拉多选-->
 <template>
-  <a-select :value="arrayValue" @change="onChange" mode="multiple" :filter-option="filterOption" :disabled="disabled" :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>
@@ -88,9 +97,19 @@
         }
       );
 
+      //适用于 动态改变下拉选项的操作
+      watch(()=>props.options, ()=>{
+        if (props.dictCode) {
+          // nothing to do
+        } else {
+          dictOptions.value = props.options;
+        }
+      });
+
       function onChange(selectedValue) {
         if (props.triggerChange) {
           emit('change', selectedValue.join(props.spliter));
+          emit('update:value', selectedValue.join(props.spliter));
         } else {
           emit('input', selectedValue.join(props.spliter));
           emit('update:value', selectedValue.join(props.spliter));

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

@@ -13,7 +13,7 @@
   import { propTypes } from '/@/utils/propTypes';
   import { useRuleFormItem } from '/@/hooks/component/useFormItem';
   import { useAttrs } from '/@/hooks/core/useAttrs';
-  import { SelectTypes } from 'ant-design-vue/es/select';
+  import { SelectValue } from 'ant-design-vue/es/select';
 
   export default defineComponent({
     name: 'JSelectPosition',
@@ -39,13 +39,13 @@
     },
     emits: ['options-change', 'change'],
     setup(props, { emit, refs }) {
-      const emitData = ref<object>();
+      const emitData = ref<any[]>();
       //注册model
       const [regModal, { openModal }] = useModal();
       //表单值
       const [state] = useRuleFormItem(props, 'value', 'change', emitData);
       //下拉框选项值
-      const selectOptions = ref<SelectTypes['options']>([]);
+      const selectOptions = ref<SelectValue>([]);
       //下拉框选中值
       let selectValues = reactive<object>({
         value: [],

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

@@ -13,7 +13,7 @@
   import { propTypes } from '/@/utils/propTypes';
   import { useRuleFormItem } from '/@/hooks/component/useFormItem';
   import { useAttrs } from '/@/hooks/core/useAttrs';
-  import { SelectTypes } from 'ant-design-vue/es/select';
+  import { SelectValue } from 'ant-design-vue/es/select';
 
   export default defineComponent({
     name: 'JSelectRole',
@@ -39,13 +39,13 @@
     },
     emits: ['options-change', 'change'],
     setup(props, { emit, refs }) {
-      const emitData = ref<object>();
+      const emitData = ref<any[]>();
       //注册model
       const [regModal, { openModal }] = useModal();
       //表单值
       const [state] = useRuleFormItem(props, 'value', 'change', emitData);
       //下拉框选项值
-      const selectOptions = ref<SelectTypes['options']>([]);
+      const selectOptions = ref<SelectValue>([]);
       //下拉框选中值
       let selectValues = reactive<Recordable>({
         value: [],

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

@@ -14,7 +14,7 @@
   import { propTypes } from '/@/utils/propTypes';
   import { useRuleFormItem } from '/@/hooks/component/useFormItem';
   import { useAttrs } from '/@/hooks/core/useAttrs';
-  import { SelectTypes } from 'ant-design-vue/es/select';
+  import { SelectValue } from 'ant-design-vue/es/select';
 
   export default defineComponent({
     name: 'JSelectUser',
@@ -39,14 +39,14 @@
       },
     },
     emits: ['options-change', 'change', 'update:value'],
-    setup(props, { emit, refs }) {
-      const emitData = ref<object>();
+    setup(props, { emit }) {
+      const emitData = ref<any[]>();
       //注册model
       const [regModal, { openModal }] = useModal();
       //表单值
       const [state] = useRuleFormItem(props, 'value', 'change', emitData);
       //下拉框选项值
-      const selectOptions = ref<SelectTypes['options']>([]);
+      const selectOptions = ref<SelectValue>([]);
       //下拉框选中值
       let selectValues = reactive<Recordable>({
         value: [],

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

@@ -14,7 +14,7 @@
   import { propTypes } from '/@/utils/propTypes';
   import { useRuleFormItem } from '/@/hooks/component/useFormItem';
   import { useAttrs } from '/@/hooks/core/useAttrs';
-  import { SelectTypes } from 'ant-design-vue/es/select';
+  import { SelectValue } from 'ant-design-vue/es/select';
   export default defineComponent({
     name: 'JSelectUserByDept',
     components: {
@@ -35,13 +35,13 @@
     },
     emits: ['options-change', 'change', 'update:value'],
     setup(props, { emit, refs }) {
-      const emitData = ref<object>();
+      const emitData = ref<any[]>();
       //注册model
       const [regModal, { openModal }] = useModal();
       //表单值
       const [state] = useRuleFormItem(props, 'value', 'change', emitData);
       //下拉框选项值
-      const selectOptions = ref<SelectTypes['options']>([]);
+      const selectOptions = ref<SelectValue>([]);
       //下拉框选中值
       let selectValues = reactive<object>({
         value: [],
@@ -107,6 +107,7 @@
         state.value = values;
         selectValues.value = values;
         emit('update:value', values);
+        emit('options-change', options);
       }
 
       function handleChange(values) {

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

@@ -1,6 +1,14 @@
 <template>
   <div :class="prefixCls">
-    <a-select v-if="query" v-model:value="value" :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>

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

@@ -43,6 +43,7 @@
     pidValue: propTypes.string.def(''),
     //update-end---author:wangshuai ---date:20220620  for:JTreeSelect组件pidValue还原成空,否则会影响自定义组件树示例--------------
     hasChildField: propTypes.string.def(''),
+    converIsLeafVal: propTypes.integer.def(1),
     condition: propTypes.string.def(''),
     multiple: propTypes.bool.def(false),
     loadTriggleChange: propTypes.bool.def(false),
@@ -96,7 +97,11 @@
    */
   async function loadItemByCode() {
     if (!props.value || props.value == '0') {
-      treeValue.value = null;
+      if(props.multiple){
+        treeValue.value = [];
+      }else{
+        treeValue.value = null;
+      }
     } else {
       let params = { key: props.value };
       let result = await defHttp.get({ url: `${Api.view}${props.dict}`, params }, { isTransformResponse: false });
@@ -137,6 +142,7 @@
       pid: props.pidValue,
       pidField: props.pidField,
       hasChildField: props.hasChildField,
+      converIsLeafVal: props.converIsLeafVal,
       condition: props.condition,
       tableName: unref(tableName),
       text: unref(text),
@@ -166,6 +172,7 @@
       pid: pid,
       pidField: props.pidField,
       hasChildField: props.hasChildField,
+      converIsLeafVal: props.converIsLeafVal,
       condition: props.condition,
       tableName: unref(tableName),
       text: unref(text),

+ 9 - 1
src/components/Form/src/jeecg/components/base/JTreeBiz.vue

@@ -2,7 +2,15 @@
   <div>
     <a-row class="j-select-row" type="flex" :gutter="8">
       <a-col class="left" :class="{ full: !showButton }">
-        <a-select ref="select" v-model:value="selectValues.value" :mode="multiple" :open="false" :options="options" @change="handleChange" style="width: 100%" />
+        <a-select
+          ref="select"
+          v-model:value="selectValues.value"
+          :mode="multiple"
+          :open="false"
+          :options="options"
+          @change="handleChange"
+          style="width: 100%"
+        />
       </a-col>
       <a-col v-if="showButton" class="right">
         <a-button type="primary" @click="openModal">选择</a-button>

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

@@ -9,13 +9,13 @@
         v-bind="getBindValue"
         @select="onSelect"
         @check="onCheck"
-        :replaceFields="replaceFields"
+        :fieldNames="fieldNames"
         :checkedKeys="checkedKeys"
         :checkStrictly="getCheckStrictly"
       />
       <!--树操作部分-->
       <template #insertFooter>
-        <a-dropdown placement="topCenter">
+        <a-dropdown placement="top">
           <template #overlay>
             <a-menu>
               <a-menu-item v-if="multiple" key="1" @click="checkALL(true)">全部勾选</a-menu-item>
@@ -61,13 +61,15 @@
       const treeRef = ref<Nullable<TreeActionType>>(null);
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
       const queryUrl = getQueryUrl();
-      const [{ visibleChange, checkedKeys, getCheckStrictly, getSelectTreeData, onCheck, onLoadData, treeData, checkALL, expandAll, onSelect }] = useTreeBiz(treeRef, queryUrl, getBindValue);
+      const [{ visibleChange, checkedKeys, getCheckStrictly, getSelectTreeData, onCheck, onLoadData, treeData, checkALL, expandAll, onSelect }] =
+        useTreeBiz(treeRef, queryUrl, getBindValue);
       const searchInfo = ref(props.params);
       const tree = ref([]);
       //替换treeNode中key字段为treeData中对应的字段
-      const replaceFields = {
+      const fieldNames = {
         key: props.rowKey,
       };
+      // {children:'children', title:'title', key:'key' }
       /**
        * 确定选择
        */
@@ -98,7 +100,7 @@
         onSelect,
         checkALL,
         expandAll,
-        replaceFields,
+        fieldNames,
         checkedKeys,
         register,
         getBindValue,

+ 26 - 2
src/components/Form/src/jeecg/components/modal/JPopupOnlReportModal.vue

@@ -103,8 +103,32 @@
       const tableScroll = ref({ x: true });
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
       const [
-        { visibleChange, loadColumnsInfo, dynamicParamHandler, loadData, handleChangeInTable, combineRowKey, clickThenCheck, filterUnuseSelect, getOkSelectRows },
-        { visible, rowSelection, checkedKeys, selectRows, pagination, dataSource, columns, loading, title, iSorter, queryInfo, queryParam, dictOptions },
+        {
+          visibleChange,
+          loadColumnsInfo,
+          dynamicParamHandler,
+          loadData,
+          handleChangeInTable,
+          combineRowKey,
+          clickThenCheck,
+          filterUnuseSelect,
+          getOkSelectRows,
+        },
+        {
+          visible,
+          rowSelection,
+          checkedKeys,
+          selectRows,
+          pagination,
+          dataSource,
+          columns,
+          loading,
+          title,
+          iSorter,
+          queryInfo,
+          queryParam,
+          dictOptions,
+        },
       ] = usePopBiz(getBindValue);
 
       const showSearchFlag = computed(() => unref(queryInfo) && unref(queryInfo).length > 0);

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

@@ -1,7 +1,16 @@
 <!--职务选择框-->
 <template>
   <div>
-    <BasicModal v-bind="$attrs" @register="register" :title="modalTitle" 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
@@ -17,7 +26,12 @@
           ></BasicTable>
         </a-col>
         <a-col :span="showSelected ? 6 : 0">
-          <BasicTable v-bind="selectedTable" :dataSource="selectRows" :useSearchForm="true" :formConfig="{ showActionButtonGroup: false, baseRowStyle: { minHeight: '40px' } }">
+          <BasicTable
+            v-bind="selectedTable"
+            :dataSource="selectRows"
+            :useSearchForm="true"
+            :formConfig="{ showActionButtonGroup: false, baseRowStyle: { minHeight: '40px' } }"
+          >
             <!--操作栏-->
             <template #action="{ record }">
               <a href="javascript:void(0)" @click="handleDeleteSelected(record)"><Icon icon="ant-design:delete-outlined"></Icon></a>
@@ -67,7 +81,10 @@
         rowKey: 'code',
       };
       const getBindValue = Object.assign({}, unref(props), unref(attrs), config);
-      const [{ rowSelection, visibleChange, indexColumnProps, getSelectResult, handleDeleteSelected, selectRows }] = useSelectBiz(getPositionList, getBindValue);
+      const [{ rowSelection, visibleChange, indexColumnProps, getSelectResult, handleDeleteSelected, selectRows }] = useSelectBiz(
+        getPositionList,
+        getBindValue
+      );
       const searchInfo = ref(props.params);
       //查询form
       const formConfig = {

+ 20 - 3
src/components/Form/src/jeecg/components/modal/UserSelectModal.vue

@@ -1,7 +1,16 @@
 <!--用户选择框-->
 <template>
   <div>
-    <BasicModal v-bind="$attrs" @register="register" :title="modalTitle" 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
@@ -22,7 +31,12 @@
           </BasicTable>
         </a-col>
         <a-col :span="showSelected ? 6 : 0">
-          <BasicTable v-bind="selectedTable" :dataSource="selectRows" :useSearchForm="true" :formConfig="{ showActionButtonGroup: false, baseRowStyle: { minHeight: '40px' } }">
+          <BasicTable
+            v-bind="selectedTable"
+            :dataSource="selectRows"
+            :useSearchForm="true"
+            :formConfig="{ showActionButtonGroup: false, baseRowStyle: { minHeight: '40px' } }"
+          >
             <!--操作栏-->
             <template #action="{ record }">
               <a href="javascript:void(0)" @click="handleDeleteSelected(record)"><Icon icon="ant-design:delete-outlined"></Icon></a>
@@ -88,7 +102,10 @@
         size: 'small',
       };
       const getBindValue = Object.assign({}, unref(props), unref(attrs), config);
-      const [{ rowSelection, visibleChange, selectValues, 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 = {

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

@@ -145,7 +145,9 @@ export interface FormSchema {
   // render component
   component: ComponentType;
   // Component parameters
-  componentProps?: ((opt: { schema: FormSchema; tableAction: TableActionType; formActionType: FormActionType; formModel: Recordable }) => Recordable) | object;
+  componentProps?:
+    | ((opt: { schema: FormSchema; tableAction: TableActionType; formActionType: FormActionType; formModel: Recordable }) => Recordable)
+    | object;
   // Required
   required?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);
 
@@ -193,6 +195,9 @@ export interface FormSchema {
 
   // 这个属性自定义的 用于自定义的业务 比如在表单打开的时候修改表单的禁用状态,但是又不能重写componentProps,因为他的内容太多了,所以使用dynamicDisabled和buss实现
   buss?: any;
+  
+  //label字数控制(label宽度)
+  labelLength?: number
 }
 export interface HelpComponentProps {
   maxWidth: string;

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

@@ -1,10 +1,10 @@
 <template>
-  <a-input :disabled="disabled" :style="{ width }" :placeholder="t('component.icon.placeholder')" :class="prefixCls" v-model:value="currentSelect">
+  <a-input :disabled="disabled" :style="{ width }" readOnly :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>
           <div class="flex justify-between">
-            <a-input :placeholder="t('component.icon.search')" @change="debounceHandleSearchChange" allowClear />
+            <a-input :placeholder="t('component.icon.search')"  @change="debounceHandleSearchChange" allowClear />
           </div>
         </template>
 

+ 10 - 1
src/components/JVxeCustom/src/components/JVxeImageCell.vue

@@ -18,7 +18,16 @@
       </div>
     </template>
     <div class="j-vxe-image-upload">
-      <a-upload name="file" :data="{ isup: 1 }" :multiple="false" :action="uploadAction" :headers="uploadHeaders" :showUploadList="false" v-bind="cellProps" @change="handleChangeUpload">
+      <a-upload
+        name="file"
+        :data="{ isup: 1 }"
+        :multiple="false"
+        :action="uploadAction"
+        :headers="uploadHeaders"
+        :showUploadList="false"
+        v-bind="cellProps"
+        @change="handleChangeUpload"
+      >
         <a-button v-if="!hasFile" preIcon="ant-design:upload">{{ originColumn.btnText || '上传图片' }}</a-button>
         <div v-if="hasFile && imgList.length < maxCount" class="j-vxe-plus" @click="">
           <Icon icon="ant-design:plus" />

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

@@ -32,7 +32,8 @@ export const DictSearchInputCell = defineComponent({
   props: useJVxeCompProps(),
   setup(props: JVxeComponent.Props) {
     const { createMessage } = useMessage();
-    const { dict, loading, isAsync, options, innerOptions, originColumn, cellProps, innerSelectValue, handleChangeCommon } = useSelectDictSearch(props);
+    const { dict, loading, isAsync, options, innerOptions, originColumn, cellProps, innerSelectValue, handleChangeCommon } =
+      useSelectDictSearch(props);
     const hasRequest = ref(false);
     // 提示信息
     const tipsContent = computed(() => {

+ 29 - 33
src/components/Modal/src/BasicModal.vue

@@ -1,15 +1,7 @@
 <template>
   <Modal v-bind="getBindValue" @cancel="handleCancel">
     <template #closeIcon v-if="!$slots.closeIcon">
-      <ModalClose
-        :canFullscreen="getProps.canFullscreen"
-        :fullScreen="fullScreenRef"
-        :commentSpan="commentSpan"
-        :enableComment="getProps.enableComment"
-        @comment="handleComment"
-        @cancel="handleCancel"
-        @fullscreen="handleFullScreen"
-      />
+      <ModalClose :canFullscreen="getProps.canFullscreen" :fullScreen="fullScreenRef" :commentSpan="commentSpan" :enableComment="getProps.enableComment" @comment="handleComment" @cancel="handleCancel" @fullscreen="handleFullScreen" />
     </template>
 
     <template #title v-if="!$slots.title">
@@ -25,8 +17,8 @@
     </template>
 
     <!-- update-begin-author:taoyan date:2022-7-18 for:  modal弹窗 支持评论 slot -->
-    <a-row>
-      <a-col :span="24 - commentSpan" class="jeecg-modal-content">
+    <a-row class="jeecg-modal-wrapper">
+      <a-col :span="24-commentSpan" class="jeecg-modal-content">
         <ModalWrapper
           :useWrapper="getProps.useWrapper"
           :footerOffset="wrapperFooterOffset"
@@ -40,18 +32,18 @@
           :modalFooterHeight="footer !== undefined && !footer ? 0 : undefined"
           v-bind="omit(getProps.wrapperProps, 'visible', 'height', 'modalFooterHeight')"
           @ext-height="handleExtHeight"
-          @height-change="handleHeightChange"
-        >
+          @height-change="handleHeightChange">
           <slot></slot>
         </ModalWrapper>
       </a-col>
-
+      
       <a-col :span="commentSpan" class="jeecg-comment-outer">
         <slot name="comment"></slot>
       </a-col>
+      
     </a-row>
     <!-- update-end-author:taoyan date:2022-7-18 for:  modal弹窗 支持评论 slot -->
-
+    
     <template #[item]="data" v-for="item in Object.keys(omit($slots, 'default'))">
       <slot :name="item" v-bind="data || {}"></slot>
     </template>
@@ -220,18 +212,14 @@
 
       //update-begin-author:taoyan date:2022-7-18 for: modal支持评论 slot
       const commentSpan = ref(0);
-      watch(
-        () => props.enableComment,
-        (flag) => {
-          handleComment(flag);
-        },
-        { immediate: true }
-      );
-      function handleComment(flag) {
-        if (flag === true) {
-          commentSpan.value = 6;
-        } else {
-          commentSpan.value = 0;
+      watch(()=>props.enableComment, (flag)=>{
+        handleComment(flag)
+      }, {immediate:true});
+      function handleComment(flag){
+        if(flag=== true){
+          commentSpan.value = 6
+        }else{
+          commentSpan.value = 0
         }
       }
       //update-end-author:taoyan date:2022-7-18 for: modal支持评论 slot
@@ -252,21 +240,29 @@
         handleTitleDbClick,
         getWrapperHeight,
         commentSpan,
-        handleComment,
+        handleComment
       };
     },
   });
 </script>
 <style lang="less">
+  /*update-begin-author:taoyan date:2022-7-27 for:modal评论区域样式*/
   .jeecg-comment-outer {
     border-left: 1px solid #f0f0f0;
-    .ant-tabs-nav-wrap {
-      /*  text-align: center;*/
+    .ant-tabs-nav-wrap{
+    /*  text-align: center;*/
     }
   }
-  .jeecg-modal-content {
-    > .scroll-container {
+  .jeecg-modal-content{
+    >.scroll-container{
       padding: 14px;
     }
   }
-</style>
+  /*update-end-author:taoyan date:2022-7-27 for:modal评论区域样式*/
+
+  // wrapper设为100%,兼容之前写过的弹窗自定义样式
+  .jeecg-modal-wrapper,
+  .jeecg-modal-content {
+    height: 100%;
+  }
+</style>

+ 24 - 41
src/components/Modal/src/components/ModalClose.vue

@@ -1,6 +1,6 @@
 <template>
-  <div :class="getClass" :style="{ width: closeWidth + 'px' }">
-    <template v-if="canFullscreen">
+  <div :class="getClass">
+    <template v-if="fullScreenStatus">
       <Tooltip :title="t('component.modal.restore')" placement="bottom" v-if="fullScreen">
         <FullscreenExitOutlined role="full" @click="handleFullScreen" />
       </Tooltip>
@@ -11,14 +11,14 @@
 
     <!-- 是否开启评论区域 -->
     <template v-if="enableComment">
-      <Tooltip title="关闭" placement="bottom" v-if="commentSpan > 0">
-        <RightSquareOutlined @click="handleCloseComment" style="font-size: 16px" />
+      <Tooltip title="关闭" placement="bottom" v-if="commentSpan>0">
+        <RightSquareOutlined @click="handleCloseComment" style="font-size: 16px"/>
       </Tooltip>
       <Tooltip title="展开" placement="bottom" v-else>
-        <LeftSquareOutlined @click="handleOpenComment" style="font-size: 16px" />
+        <LeftSquareOutlined @click="handleOpenComment" style="font-size: 16px"/>
       </Tooltip>
     </template>
-
+    
     <Tooltip :title="t('component.modal.close')" placement="bottom">
       <CloseOutlined @click="handleCancel" />
     </Tooltip>
@@ -62,29 +62,19 @@
       function handleFullScreen(e: Event) {
         e?.stopPropagation();
         e?.preventDefault();
-        if (props.commentSpan == 0 || props.enableComment == false) {
+        if(props.commentSpan==0 || props.enableComment == false){
           emit('fullscreen');
         }
       }
 
-      //update-begin-author:taoyan date:2022-7-18 for: 关闭按钮的区域宽度 取决于是否有其他图标
-      const closeWidth = computed(() => {
-        if (props.canFullscreen && props.enableComment) {
-          return 140;
-        } else {
-          return 96;
-        }
-      });
-      //update-end-author:taoyan date:2022-7-18 for: 关闭按钮的区域宽度 取决于是否有其他图标
-
       /**
        * 开启评论区域
        * @param e
        */
-      function handleOpenComment(e: Event) {
+      function handleOpenComment(e: Event){
         e?.stopPropagation();
         e?.preventDefault();
-        if (props.fullScreen == false) {
+        if(props.fullScreen==false){
           emit('fullscreen');
         }
         emit('comment', true);
@@ -94,21 +84,32 @@
        * 关闭评论区域
        * @param e
        */
-      function handleCloseComment(e: Event) {
+      function handleCloseComment(e: Event){
         e?.stopPropagation();
         e?.preventDefault();
         emit('comment', false);
       }
 
+      /**
+       * 有评论的时候不需要设置全屏
+       */
+      const fullScreenStatus = computed(()=>{
+        if(props.enableComment===true){
+          return false
+        }else{
+          return props.canFullscreen;
+        }
+      });
+      
       return {
         t,
         getClass,
         prefixCls,
         handleCancel,
         handleFullScreen,
-        closeWidth,
         handleOpenComment,
         handleCloseComment,
+        fullScreenStatus
       };
     },
   });
@@ -120,10 +121,6 @@
     height: 95%;
     align-items: center;
 
-    .ant-modal-close-x {
-      width: 140px !important;
-    }
-
     > span {
       margin-left: 48px;
       font-size: 16px;
@@ -141,12 +138,6 @@
           font-weight: 700;
         }
       }
-      /** 展开/关闭 评论图标样式*/
-      > span:nth-child(2) {
-        &:hover {
-          font-weight: 700;
-        }
-      }
     }
 
     & span:nth-child(1) {
@@ -157,20 +148,12 @@
         color: @primary-color;
       }
     }
-    /** 展开/关闭 评论图标样式*/
-    & span:nth-child(2) {
-      display: inline-block;
-      padding: 10px 10px 10px 0;
-
-      &:hover {
-        color: @primary-color;
-      }
-    }
 
     & span:last-child {
+      padding: 10px 10px 10px 0;
       &:hover {
         color: @error-color;
       }
     }
   }
-</style>
+</style>

+ 10 - 3
src/components/Modal/src/index.less

@@ -7,11 +7,18 @@
     bottom: 0 !important;
     left: 0 !important;
     width: 100% !important;
-    height: 100%;
+    height: 100% !important;
+    max-width: 100% !important;
+    max-height: 100% !important;
 
     &-content {
       height: 100%;
     }
+
+    .ant-modal-header,
+    .@{namespace}-basic-title {
+      cursor: default !important;
+    }
   }
 }
 
@@ -70,8 +77,8 @@
 
   &-close-x {
     display: inline-block;
-    /*width: 96px;*/
-    width: auto;
+    width: 96px;
+/*    width: auto;*/
     height: 56px;
     line-height: 56px;
   }

+ 2 - 0
src/components/Modal/src/typing.ts

@@ -194,6 +194,8 @@ export interface ModalProps {
    * @type number
    */
   zIndex?: number;
+
+  enableComment?: boolean;
 }
 
 export interface ModalWrapperProps {

+ 14 - 2
src/components/Page/src/PageWrapper.vue

@@ -1,6 +1,12 @@
 <template>
   <div :class="getClass" ref="wrapperRef">
-    <PageHeader :ghost="ghost" :title="title" v-bind="omit($attrs, 'class')" ref="headerRef" v-if="content || $slots.headerContent || title || getHeaderSlots.length">
+    <PageHeader
+      :ghost="ghost"
+      :title="title"
+      v-bind="omit($attrs, 'class')"
+      ref="headerRef"
+      v-if="content || $slots.headerContent || title || getHeaderSlots.length"
+    >
       <template #default>
         <template v-if="content">
           {{ content }}
@@ -74,7 +80,13 @@
       });
 
       const getUpwardSpace = computed(() => props.upwardSpace);
-      const { redoHeight, setCompensation, contentHeight } = useContentHeight(getIsContentFullHeight, wrapperRef, [headerRef, footerRef], [contentRef], getUpwardSpace);
+      const { redoHeight, setCompensation, contentHeight } = useContentHeight(
+        getIsContentFullHeight,
+        wrapperRef,
+        [headerRef, footerRef],
+        [contentRef],
+        getUpwardSpace
+      );
       setCompensation({ useLayoutFooter: true, elements: [footerRef] });
 
       const getClass = computed(() => {

+ 2 - 1
src/components/Scrollbar/src/bar.ts

@@ -28,7 +28,8 @@ export default defineComponent({
       }
       window.getSelection()?.removeAllRanges();
       startDrag(e);
-      barStore.value[bar.value.axis] = e.currentTarget[bar.value.offset] - (e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction]);
+      barStore.value[bar.value.axis] =
+        e.currentTarget[bar.value.offset] - (e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction]);
     };
 
     const clickTrackHandler = (e: any) => {

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

@@ -1,5 +1,12 @@
 <template>
-  <Menu v-bind="getBindValues" :activeName="activeName" :openNames="getOpenKeys" :class="prefixCls" :activeSubMenuNames="activeSubMenuNames" @select="handleSelect">
+  <Menu
+    v-bind="getBindValues"
+    :activeName="activeName"
+    :openNames="getOpenKeys"
+    :class="prefixCls"
+    :activeSubMenuNames="activeSubMenuNames"
+    @select="handleSelect"
+  >
     <template v-for="item in items" :key="item.path">
       <SimpleSubMenu :item="item" :parent="true" :collapsedShowTitle="collapsedShowTitle" :collapse="collapse" />
     </template>

+ 6 - 1
src/components/SimpleMenu/src/SimpleSubMenu.vue

@@ -80,7 +80,12 @@
       });
 
       function menuHasChildren(menuTreeItem: Menu): boolean {
-        return !menuTreeItem.meta?.hideChildrenInMenu && Reflect.has(menuTreeItem, 'children') && !!menuTreeItem.children && menuTreeItem.children.length > 0;
+        return (
+          !menuTreeItem.meta?.hideChildrenInMenu &&
+          Reflect.has(menuTreeItem, 'children') &&
+          !!menuTreeItem.children &&
+          menuTreeItem.children.length > 0
+        );
       }
 
       return {

+ 26 - 13
src/components/Table/src/BasicTable.vue

@@ -14,15 +14,22 @@
       </template>
     </BasicForm>
 
-    <Table ref="tableElRef" v-bind="getBindValues" :rowClassName="getRowClassName" v-show="getEmptyDataIsShowTable" @change="handleTableChange">
-      <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
-        <slot :name="item" v-bind="data || {}"></slot>
-      </template>
-
-      <template #[`header-${column.dataIndex}`] v-for="column in columns" :key="column.dataIndex">
-        <HeaderCell :column="column" />
-      </template>
-    </Table>
+    <!-- antd v3 升级兼容,阻止数据的收集,防止控制台报错 -->
+    <!-- https://antdv.com/docs/vue/migration-v3-cn -->
+    <a-form-item-rest>
+      <Table ref="tableElRef" v-bind="getBindValues" :rowClassName="getRowClassName" v-show="getEmptyDataIsShowTable" @change="handleTableChange">
+        <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
+          <slot :name="item" v-bind="data || {}"></slot>
+        </template>
+        <template #headerCell="{ column }">
+          <HeaderCell :column="column" />
+        </template>
+        <!-- 增加对antdv3.x兼容 -->
+        <template #bodyCell="data">
+          <slot name="bodyCell" v-bind="data || {}"></slot>
+        </template>
+      </Table>
+    </a-form-item-rest>
   </div>
 </template>
 <script lang="ts">
@@ -97,13 +104,16 @@
 
       const isFixedHeightPage = inject(PageWrapperFixedHeightKey, false);
       watchEffect(() => {
-        unref(isFixedHeightPage) && props.canResize && warn("'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)");
+        unref(isFixedHeightPage) &&
+          props.canResize &&
+          warn("'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)");
       });
 
       const { getLoading, setLoading } = useLoading(getProps);
       const { getPaginationInfo, getPagination, setPagination, setShowPagination, getShowPagination } = usePagination(getProps);
 
-      const { getRowSelection, getRowSelectionRef, getSelectRows, clearSelectedRowKeys, getSelectRowKeys, deleteSelectRowByKey, setSelectedRowKeys } = useRowSelection(getProps, tableData, emit);
+      const { getRowSelection, getRowSelectionRef, getSelectRows, clearSelectedRowKeys, getSelectRowKeys, deleteSelectRowByKey, setSelectedRowKeys } =
+        useRowSelection(getProps, tableData, emit);
 
       const {
         handleTableChange: onTableChange,
@@ -141,7 +151,10 @@
         onChange && isFunction(onChange) && onChange.call(undefined, ...args);
       }
 
-      const { getViewColumns, getColumns, setCacheColumnsByField, setColumns, getColumnsRef, getCacheColumns } = useColumns(getProps, getPaginationInfo);
+      const { getViewColumns, getColumns, setCacheColumnsByField, setColumns, getColumnsRef, getCacheColumns } = useColumns(
+        getProps,
+        getPaginationInfo
+      );
 
       const { getScrollRef, redoHeight } = useTableScroll(getProps, tableElRef, getColumnsRef, getRowSelectionRef, getDataSourceRef);
 
@@ -381,7 +394,7 @@
         border: none !important;
       }
 
-      .ant-table-body {
+      .ant-table-content {
         overflow-x: hidden !important;
         //  overflow-y: scroll !important;
       }

+ 2 - 2
src/components/Table/src/components/HeaderCell.vue

@@ -29,7 +29,7 @@
       const { prefixCls } = useDesign('basic-table-header-cell');
 
       const getIsEdit = computed(() => !!props.column?.edit);
-      const getTitle = computed(() => props.column?.customTitle);
+      const getTitle = computed(() => props.column?.customTitle || props.column?.title);
       const getHelpMessage = computed(() => props.column?.helpMessage);
 
       return { prefixCls, getIsEdit, getTitle, getHelpMessage };
@@ -42,7 +42,7 @@
   .@{prefix-cls} {
     &__help {
       margin-left: 8px;
-      color: rgba(0, 0, 0, 0.65) !important;
+      color: rgb(0 0 0 / 65%) !important;
     }
   }
 </style>

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

@@ -13,7 +13,7 @@
       <div :class="`${prefixCls}__toolbar`">
         <slot name="toolbar"></slot>
         <Divider type="vertical" v-if="$slots.toolbar && showTableSetting" />
-        <TableSetting :class="`${prefixCls}__toolbar-desktop`" :setting="tableSetting" v-if="showTableSetting" @columns-change="handleColumnChange" />
+        <TableSetting :class="`${prefixCls}__toolbar-desktop`" style="white-space: nowrap;" :setting="tableSetting" v-if="showTableSetting" @columns-change="handleColumnChange" />
         <a-popover :overlayClassName="`${prefixCls}__toolbar-mobile`" trigger="click" placement="left" :getPopupContainer="(n) => n.parentElement">
           <template #content>
             <TableSetting mode="mobile" :setting="tableSetting" v-if="showTableSetting" @columns-change="handleColumnChange" />

+ 4 - 1
src/components/Table/src/components/editable/CellComponent.ts

@@ -13,7 +13,10 @@ export interface ComponentProps {
   getPopupContainer?: Fn;
 }
 
-export const CellComponent: FunctionalComponent = ({ component = 'Input', rule = true, ruleMessage, popoverVisible, getPopupContainer }: ComponentProps, { attrs }) => {
+export const CellComponent: FunctionalComponent = (
+  { component = 'Input', rule = true, ruleMessage, popoverVisible, getPopupContainer }: ComponentProps,
+  { attrs }
+) => {
   const Comp = componentMap.get(component) as typeof defineComponent;
 
   const DefaultComp = h(Comp, attrs);

+ 13 - 9
src/components/Table/src/components/settings/ColumnSetting.vue

@@ -7,7 +7,7 @@
       v-model:visible="popoverVisible"
       placement="bottomLeft"
       trigger="click"
-      @visibleChange="handleVisibleChange"
+      @visible-change="handleVisibleChange"
       :overlayClassName="`${prefixCls}__cloumn-list`"
       :getPopupContainer="getPopupContainer"
     >
@@ -93,6 +93,7 @@
   import type { BasicColumn, ColumnChangeParam } from '../../types/table';
   import { defineComponent, ref, reactive, toRefs, watchEffect, nextTick, unref, computed } from 'vue';
   import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue';
+  import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface';
   import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue';
   import { Icon } from '/@/components/Icon';
   import { ScrollContainer } from '/@/components/Container';
@@ -194,10 +195,12 @@
       );
 
       watchEffect(() => {
-        const columns = table.getColumns();
-        if (columns.length && !state.isInit) {
-          init();
-        }
+        setTimeout(() => {
+          const columns = table.getColumns();
+          if (columns.length && !state.isInit) {
+            init();
+          }
+        }, 0);
       });
 
       watchEffect(() => {
@@ -253,7 +256,7 @@
       }
 
       // checkAll change
-      function onCheckAllChange(e: ChangeEvent) {
+      function onCheckAllChange(e: CheckboxChangeEvent) {
         const checkList = plainOptions.value.map((item) => item.value);
         if (e.target.checked) {
           state.checkedList = checkList;
@@ -338,14 +341,14 @@
       }
 
       // Control whether the serial number column is displayed
-      function handleIndexCheckChange(e: ChangeEvent) {
+      function handleIndexCheckChange(e: CheckboxChangeEvent) {
         table.setProps({
           showIndexColumn: e.target.checked,
         });
       }
 
       // Control whether the check box is displayed
-      function handleSelectCheckChange(e: ChangeEvent) {
+      function handleSelectCheckChange(e: CheckboxChangeEvent) {
         table.setProps({
           rowSelection: e.target.checked ? defaultRowSelection : undefined,
         });
@@ -372,7 +375,8 @@
       function setColumns(columns: BasicColumn[] | string[]) {
         table.setColumns(columns);
         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;
+          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 };
         });
 

+ 1 - 1
src/components/Table/src/components/settings/SizeSetting.vue

@@ -4,7 +4,7 @@
       <span>{{ t('component.table.settingDens') }}</span>
     </template>
 
-    <Dropdown placement="bottomCenter" :trigger="['click']" :getPopupContainer="getPopupContainer">
+    <Dropdown placement="bottom" :trigger="['click']" :getPopupContainer="getPopupContainer">
       <ColumnHeightOutlined />
       <template #overlay>
         <Menu @click="handleTitleClick" selectable v-model:selectedKeys="selectedKeysRef">

+ 5 - 5
src/components/Table/src/hooks/useColumns.ts

@@ -1,7 +1,7 @@
 import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table';
 import type { PaginationProps } from '../types/pagination';
 import type { ComputedRef } from 'vue';
-import { computed, Ref, ref, toRaw, unref, watch } from 'vue';
+import { computed, Ref, ref, toRaw, unref, watch, reactive } from 'vue';
 import { renderEditCell } from '../components/editable';
 import { usePermission } from '/@/hooks/web/usePermission';
 import { useI18n } from '/@/hooks/web/useI18n';
@@ -141,11 +141,11 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>, getPagination
         return hasPermission(column.auth) && isIfShow(column);
       })
       .map((column) => {
-        const { slots, dataIndex, customRender, format, edit, editRow, flag, title: metaTitle } = column;
+        const { slots, customRender, format, edit, editRow, flag, title: metaTitle } = column;
 
         if (!slots || !slots?.title) {
-          column.slots = { title: `header-${dataIndex}`, ...(slots || {}) };
-          column.customTitle = column.title;
+          // column.slots = { title: `header-${dataIndex}`, ...(slots || {}) };
+          column.customTitle = column.title as string;
           Reflect.deleteProperty(column, 'title');
         }
         //update-begin-author:taoyan date:20211203 for:【online报表】分组标题显示错误,都显示成了联系信息 LOWCOD-2343
@@ -165,7 +165,7 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>, getPagination
         if ((edit || editRow) && !isDefaultAction) {
           column.customRender = renderEditCell(column);
         }
-        return column;
+        return reactive(column);
       });
   });
 

+ 4 - 1
src/components/Table/src/hooks/useCustomRow.ts

@@ -25,7 +25,10 @@ function getKey(record: Recordable, rowKey: string | ((record: Record<string, an
   return null;
 }
 
-export function useCustomRow(propsRef: ComputedRef<BasicTableProps>, { setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options) {
+export function useCustomRow(
+  propsRef: ComputedRef<BasicTableProps>,
+  { setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options
+) {
   const customRow = (record: Recordable, index: number) => {
     return {
       onClick: (e: Event) => {

+ 5 - 1
src/components/Table/src/hooks/useDataSource.ts

@@ -22,7 +22,11 @@ interface SearchState {
   sortInfo: Recordable;
   filterInfo: Record<string, string[]>;
 }
-export function useDataSource(propsRef: ComputedRef<BasicTableProps>, { getPaginationInfo, setPagination, setLoading, validate, clearSelectedRowKeys, tableData }: ActionType, emit: EmitType) {
+export function useDataSource(
+  propsRef: ComputedRef<BasicTableProps>,
+  { getPaginationInfo, setPagination, setLoading, validate, clearSelectedRowKeys, tableData }: ActionType,
+  emit: EmitType
+) {
   const searchState = reactive<SearchState>({
     sortInfo: {},
     filterInfo: {},

+ 7 - 6
src/components/Table/src/hooks/useRowSelection.ts

@@ -17,11 +17,8 @@ export function useRowSelection(propsRef: ComputedRef<BasicTableProps>, tableDat
 
     return {
       selectedRowKeys: unref(selectedRowKeysRef),
-      hideDefaultSelections: false,
       onChange: (selectedRowKeys: string[]) => {
         setSelectedRowKeys(selectedRowKeys);
-        // selectedRowKeysRef.value = selectedRowKeys;
-        // selectedRowRef.value = selectedRows;
       },
       ...omit(rowSelection, ['onChange']),
     };
@@ -63,9 +60,13 @@ export function useRowSelection(propsRef: ComputedRef<BasicTableProps>, tableDat
 
   function setSelectedRowKeys(rowKeys: string[]) {
     selectedRowKeysRef.value = rowKeys;
-    const allSelectedRows = findNodeAll(toRaw(unref(tableData)).concat(toRaw(unref(selectedRowRef))), (item) => rowKeys.includes(item[unref(getRowKey) as string]), {
-      children: propsRef.value.childrenColumnName ?? 'children',
-    });
+    const allSelectedRows = findNodeAll(
+      toRaw(unref(tableData)).concat(toRaw(unref(selectedRowRef))),
+      (item) => rowKeys.includes(item[unref(getRowKey) as string]),
+      {
+        children: propsRef.value.childrenColumnName ?? 'children',
+      }
+    );
     const trueSelectedRows: any[] = [];
     rowKeys.forEach((key: string) => {
       const found = allSelectedRows.find((item) => item[unref(getRowKey) as string] === key);

+ 2 - 3
src/components/Table/src/hooks/useTableFooter.ts

@@ -34,13 +34,12 @@ export function useTableFooter(
     nextTick(() => {
       const tableEl = unref(tableElRef);
       if (!tableEl) return;
-      const bodyDomList = tableEl.$el.querySelectorAll('.ant-table-body');
-      const bodyDom = bodyDomList[0];
+      const bodyDom = tableEl.$el.querySelector('.ant-table-content');
       useEventListener({
         el: bodyDom,
         name: 'scroll',
         listener: () => {
-          const footerBodyDom = tableEl.$el.querySelector('.ant-table-footer .ant-table-body') as HTMLDivElement;
+          const footerBodyDom = tableEl.$el.querySelector('.ant-table-footer .ant-table-content') as HTMLDivElement;
           if (!footerBodyDom || !bodyDom) return;
           footerBodyDom.scrollLeft = bodyDom.scrollLeft;
         },

+ 7 - 1
src/components/Table/src/hooks/useTableForm.ts

@@ -4,7 +4,12 @@ import { unref, computed } from 'vue';
 import type { FormProps } from '/@/components/Form';
 import { isFunction } from '/@/utils/is';
 
-export function useTableForm(propsRef: ComputedRef<BasicTableProps>, slots: Slots, fetch: (opt?: FetchParams | undefined) => Promise<void>, getLoading: ComputedRef<boolean | undefined>) {
+export function useTableForm(
+  propsRef: ComputedRef<BasicTableProps>,
+  slots: Slots,
+  fetch: (opt?: FetchParams | undefined) => Promise<void>,
+  getLoading: ComputedRef<boolean | undefined>
+) {
   const getFormProps = computed((): Partial<FormProps> => {
     const { formConfig } = unref(propsRef);
     const { submitButtonOptions } = formConfig || {};
@@ -13,6 +18,7 @@ export function useTableForm(propsRef: ComputedRef<BasicTableProps>, slots: Slot
       ...formConfig,
       submitButtonOptions: { loading: unref(getLoading), ...submitButtonOptions },
       compact: true,
+      autoSubmitOnEnter: true,
     };
   });
 

+ 2 - 1
src/components/Table/src/types/table.ts

@@ -1,7 +1,8 @@
 import type { VNodeChild } from 'vue';
 import type { PaginationProps } from './pagination';
 import type { FormProps } from '/@/components/Form';
-import type { ColumnProps, TableRowSelection as ITableRowSelection } from 'ant-design-vue/lib/table/interface';
+import type { TableRowSelection as ITableRowSelection } from 'ant-design-vue/lib/table/interface';
+import type { ColumnProps } from 'ant-design-vue/lib/table';
 
 import { ComponentType } from './componentType';
 import { VueNode } from '/@/utils/propTypes';

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

@@ -1,6 +1,13 @@
 <template>
   <div :class="prefixCls" :style="{ width: containerWidth }">
-    <ImgUpload :fullscreen="fullscreen" @uploading="handleImageUploading" @done="handleDone" v-if="showImageUpload" v-show="editorRef" :disabled="disabled" />
+    <ImgUpload
+      :fullscreen="fullscreen"
+      @uploading="handleImageUploading"
+      @done="handleDone"
+      v-if="showImageUpload"
+      v-show="editorRef"
+      :disabled="disabled"
+    />
     <textarea :id="tinymceId" ref="elRef" :style="{ visibility: 'hidden' }" v-if="!initOptions.inline"></textarea>
     <slot v-else></slot>
   </div>

+ 10 - 1
src/components/Tinymce/src/ImgUpload.vue

@@ -1,6 +1,15 @@
 <template>
   <div :class="[prefixCls, { fullscreen }]">
-    <Upload name="file" multiple @change="handleChange" :action="uploadUrl" :showUploadList="false" :data="getBizData()" :headers="getheader()" 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>

+ 3 - 2
src/components/Tree/index.ts

@@ -1,5 +1,6 @@
-import BasicTree from './src/Tree.vue';
+import BasicTree from './src/BasicTree.vue';
+import './style';
 
 export { BasicTree };
 export type { ContextMenuItem } from '/@/hooks/web/useContextMenu';
-export * from './src/typing';
+export * from './src/types/tree';

+ 456 - 0
src/components/Tree/src/BasicTree.vue

@@ -0,0 +1,456 @@
+<script lang="tsx">
+  import type { CSSProperties } from 'vue';
+  import type {
+    FieldNames,
+    TreeState,
+    TreeItem,
+    KeyType,
+    CheckKeys,
+    TreeActionType,
+  } from './types/tree';
+
+  import {
+    defineComponent,
+    reactive,
+    computed,
+    unref,
+    ref,
+    watchEffect,
+    toRaw,
+    watch,
+    onMounted,
+  } from 'vue';
+  import TreeHeader from './components/TreeHeader.vue';
+  import { Tree, Spin, Empty } from 'ant-design-vue';
+  import { TreeIcon } from './TreeIcon';
+  import { ScrollContainer } from '/@/components/Container';
+  import { omit, get, difference, cloneDeep } from 'lodash-es';
+  import { isArray, isBoolean, isEmpty, isFunction } from '/@/utils/is';
+  import { extendSlots, getSlot } from '/@/utils/helper/tsxHelper';
+  import { filter, treeToList, eachTree } from '/@/utils/helper/treeHelper';
+  import { useTree } from './hooks/useTree';
+  import { useContextMenu } from '/@/hooks/web/useContextMenu';
+  import { CreateContextOptions } from '/@/components/ContextMenu';
+  import { treeEmits, treeProps } from './types/tree';
+  import { createBEM } from '/@/utils/bem';
+
+  export default defineComponent({
+    name: 'BasicTree',
+    inheritAttrs: false,
+    props: treeProps,
+    emits: treeEmits,
+    setup(props, { attrs, slots, emit, expose }) {
+      const [bem] = createBEM('tree');
+
+      const state = reactive<TreeState>({
+        checkStrictly: props.checkStrictly,
+        expandedKeys: props.expandedKeys || [],
+        selectedKeys: props.selectedKeys || [],
+        checkedKeys: props.checkedKeys || [],
+      });
+
+      const searchState = reactive({
+        startSearch: false,
+        searchText: '',
+        searchData: [] as TreeItem[],
+      });
+
+      const treeDataRef = ref<TreeItem[]>([]);
+
+      const [createContextMenu] = useContextMenu();
+
+      const getFieldNames = computed((): Required<FieldNames> => {
+        const { fieldNames } = props;
+        return {
+          children: 'children',
+          title: 'title',
+          key: 'key',
+          ...fieldNames,
+        };
+      });
+
+      const getBindValues = computed(() => {
+        let propsData = {
+          blockNode: true,
+          ...attrs,
+          ...props,
+          expandedKeys: state.expandedKeys,
+          selectedKeys: state.selectedKeys,
+          checkedKeys: state.checkedKeys,
+          checkStrictly: state.checkStrictly,
+          fieldNames: unref(getFieldNames),
+          'onUpdate:expandedKeys': (v: KeyType[]) => {
+            state.expandedKeys = v;
+            emit('update:expandedKeys', v);
+          },
+          'onUpdate:selectedKeys': (v: KeyType[]) => {
+            state.selectedKeys = v;
+            emit('update:selectedKeys', v);
+          },
+          onCheck: (v: CheckKeys, e) => {
+            let currentValue = toRaw(state.checkedKeys) as KeyType[];
+            if (isArray(currentValue) && searchState.startSearch) {
+              const { key } = unref(getFieldNames);
+              currentValue = difference(currentValue, getChildrenKeys(e.node.$attrs.node[key]));
+              if (e.checked) {
+                currentValue.push(e.node.$attrs.node[key]);
+              }
+              state.checkedKeys = currentValue;
+            } else {
+              state.checkedKeys = v;
+            }
+
+            const rawVal = toRaw(state.checkedKeys);
+            emit('update:value', rawVal);
+            emit('check', rawVal, e);
+          },
+          onRightClick: handleRightClick,
+        };
+        return omit(propsData, 'treeData', 'class');
+      });
+
+      const getTreeData = computed((): TreeItem[] =>
+        searchState.startSearch ? searchState.searchData : unref(treeDataRef),
+      );
+
+      const getNotFound = computed((): boolean => {
+        return !getTreeData.value || getTreeData.value.length === 0;
+      });
+
+      const {
+        deleteNodeByKey,
+        insertNodeByKey,
+        insertNodesByKey,
+        filterByLevel,
+        updateNodeByKey,
+        getAllKeys,
+        getChildrenKeys,
+        getEnabledKeys,
+        getSelectedNode,
+      } = useTree(treeDataRef, getFieldNames);
+
+      function getIcon(params: Recordable, icon?: string) {
+        if (!icon) {
+          if (props.renderIcon && isFunction(props.renderIcon)) {
+            return props.renderIcon(params);
+          }
+        }
+        return icon;
+      }
+
+      async function handleRightClick({ event, node }: Recordable) {
+        const { rightMenuList: menuList = [], beforeRightClick } = props;
+        let contextMenuOptions: CreateContextOptions = { event, items: [] };
+
+        if (beforeRightClick && isFunction(beforeRightClick)) {
+          let result = await beforeRightClick(node, event);
+          if (Array.isArray(result)) {
+            contextMenuOptions.items = result;
+          } else {
+            Object.assign(contextMenuOptions, result);
+          }
+        } else {
+          contextMenuOptions.items = menuList;
+        }
+        if (!contextMenuOptions.items?.length) return;
+        contextMenuOptions.items = contextMenuOptions.items.filter((item) => !item.hidden);
+        createContextMenu(contextMenuOptions);
+      }
+
+      function setExpandedKeys(keys: KeyType[]) {
+        state.expandedKeys = keys;
+      }
+
+      function getExpandedKeys() {
+        return state.expandedKeys;
+      }
+      function setSelectedKeys(keys: KeyType[]) {
+        state.selectedKeys = keys;
+      }
+
+      function getSelectedKeys() {
+        return state.selectedKeys;
+      }
+
+      function setCheckedKeys(keys: CheckKeys) {
+        state.checkedKeys = keys;
+      }
+
+      function getCheckedKeys() {
+        return state.checkedKeys;
+      }
+
+      function checkAll(checkAll: boolean) {
+        state.checkedKeys = checkAll ? getEnabledKeys() : ([] as KeyType[]);
+      }
+
+      function expandAll(expandAll: boolean) {
+        state.expandedKeys = expandAll ? getAllKeys() : ([] as KeyType[]);
+      }
+
+      function onStrictlyChange(strictly: boolean) {
+        state.checkStrictly = strictly;
+      }
+
+      watch(
+        () => props.searchValue,
+        (val) => {
+          if (val !== searchState.searchText) {
+            searchState.searchText = val;
+          }
+        },
+        {
+          immediate: true,
+        },
+      );
+
+      watch(
+        () => props.treeData,
+        (val) => {
+          if (val) {
+            handleSearch(searchState.searchText);
+          }
+        },
+      );
+
+      function handleSearch(searchValue: string) {
+        if (searchValue !== searchState.searchText) searchState.searchText = searchValue;
+        emit('update:searchValue', searchValue);
+        if (!searchValue) {
+          searchState.startSearch = false;
+          return;
+        }
+        const { filterFn, checkable, expandOnSearch, checkOnSearch, selectedOnSearch } =
+          unref(props);
+        searchState.startSearch = true;
+        const { title: titleField, key: keyField } = unref(getFieldNames);
+
+        const matchedKeys: string[] = [];
+        searchState.searchData = filter(
+          unref(treeDataRef),
+          (node) => {
+            const result = filterFn
+              ? filterFn(searchValue, node, unref(getFieldNames))
+              : node[titleField]?.includes(searchValue) ?? false;
+            if (result) {
+              matchedKeys.push(node[keyField]);
+            }
+            return result;
+          },
+          unref(getFieldNames),
+        );
+
+        if (expandOnSearch) {
+          const expandKeys = treeToList(searchState.searchData).map((val) => {
+            return val[keyField];
+          });
+          if (expandKeys && expandKeys.length) {
+            setExpandedKeys(expandKeys);
+          }
+        }
+
+        if (checkOnSearch && checkable && matchedKeys.length) {
+          setCheckedKeys(matchedKeys);
+        }
+
+        if (selectedOnSearch && matchedKeys.length) {
+          setSelectedKeys(matchedKeys);
+        }
+      }
+
+      function handleClickNode(key: string, children: TreeItem[]) {
+        if (!props.clickRowToExpand || !children || children.length === 0) return;
+        if (!state.expandedKeys.includes(key)) {
+          setExpandedKeys([...state.expandedKeys, key]);
+        } else {
+          const keys = [...state.expandedKeys];
+          const index = keys.findIndex((item) => item === key);
+          if (index !== -1) {
+            keys.splice(index, 1);
+          }
+          setExpandedKeys(keys);
+        }
+      }
+
+      watchEffect(() => {
+        treeDataRef.value = props.treeData as TreeItem[];
+      });
+
+      onMounted(() => {
+        const level = parseInt(props.defaultExpandLevel);
+        if (level > 0) {
+          state.expandedKeys = filterByLevel(level);
+        } else if (props.defaultExpandAll) {
+          expandAll(true);
+        }
+      });
+
+      watchEffect(() => {
+        state.expandedKeys = props.expandedKeys;
+      });
+
+      watchEffect(() => {
+        state.selectedKeys = props.selectedKeys;
+      });
+
+      watchEffect(() => {
+        state.checkedKeys = props.checkedKeys;
+      });
+
+      watch(
+        () => props.value,
+        () => {
+          state.checkedKeys = toRaw(props.value || []);
+        },
+        { immediate: true },
+      );
+
+      watch(
+        () => state.checkedKeys,
+        () => {
+          const v = toRaw(state.checkedKeys);
+          emit('update:value', v);
+          emit('change', v);
+        },
+      );
+
+      watchEffect(() => {
+        state.checkStrictly = props.checkStrictly;
+      });
+
+      const instance: TreeActionType = {
+        setExpandedKeys,
+        getExpandedKeys,
+        setSelectedKeys,
+        getSelectedKeys,
+        setCheckedKeys,
+        getCheckedKeys,
+        insertNodeByKey,
+        insertNodesByKey,
+        deleteNodeByKey,
+        updateNodeByKey,
+        getSelectedNode,
+        checkAll,
+        expandAll,
+        filterByLevel: (level: number) => {
+          state.expandedKeys = filterByLevel(level);
+        },
+        setSearchValue: (value: string) => {
+          handleSearch(value);
+        },
+        getSearchValue: () => {
+          return searchState.searchText;
+        },
+      };
+
+      function renderAction(node: TreeItem) {
+        const { actionList } = props;
+        if (!actionList || actionList.length === 0) return;
+        return actionList.map((item, index) => {
+          let nodeShow = true;
+          if (isFunction(item.show)) {
+            nodeShow = item.show?.(node);
+          } else if (isBoolean(item.show)) {
+            nodeShow = item.show;
+          }
+
+          if (!nodeShow) return null;
+
+          return (
+            <span key={index} class={bem('action')}>
+              {item.render(node)}
+            </span>
+          );
+        });
+      }
+
+      const treeData = computed(() => {
+        const data = cloneDeep(getTreeData.value);
+        eachTree(data, (item, _parent) => {
+          const searchText = searchState.searchText;
+          const { highlight } = unref(props);
+          const {
+            title: titleField,
+            key: keyField,
+            children: childrenField,
+          } = unref(getFieldNames);
+
+          const icon = getIcon(item, item.icon);
+          const title = get(item, titleField);
+
+          const searchIdx = searchText ? title.indexOf(searchText) : -1;
+          const isHighlight =
+            searchState.startSearch && !isEmpty(searchText) && highlight && searchIdx !== -1;
+          const highlightStyle = `color: ${isBoolean(highlight) ? '#f50' : highlight}`;
+
+          const titleDom = isHighlight ? (
+            <span class={unref(getBindValues)?.blockNode ? `${bem('content')}` : ''}>
+              <span>{title.substr(0, searchIdx)}</span>
+              <span style={highlightStyle}>{searchText}</span>
+              <span>{title.substr(searchIdx + (searchText as string).length)}</span>
+            </span>
+          ) : (
+            title
+          );
+          item[titleField] = (
+            <span
+              class={`${bem('title')} pl-2`}
+              onClick={handleClickNode.bind(null, item[keyField], item[childrenField])}
+            >
+              {slots?.title ? (
+                getSlot(slots, 'title', item)
+              ) : (
+                <>
+                  {icon && <TreeIcon icon={icon} />}
+                  {titleDom}
+                  <span class={bem('actions')}>{renderAction(item)}</span>
+                </>
+              )}
+            </span>
+          );
+          return item;
+        });
+        return data;
+      });
+
+      expose(instance);
+
+      return () => {
+        const { title, helpMessage, toolbar, search, checkable } = props;
+        const showTitle = title || toolbar || search || slots.headerTitle;
+        const scrollStyle: CSSProperties = { height: 'calc(100% - 38px)' };
+        return (
+          <div class={[bem(), 'h-full', attrs.class]}>
+            {showTitle && (
+              <TreeHeader
+                checkable={checkable}
+                checkAll={checkAll}
+                expandAll={expandAll}
+                title={title}
+                search={search}
+                toolbar={toolbar}
+                helpMessage={helpMessage}
+                onStrictlyChange={onStrictlyChange}
+                onSearch={handleSearch}
+                onClickSearch={($event) => emit('search', $event)}
+                searchText={searchState.searchText}
+              >
+                {extendSlots(slots)}
+              </TreeHeader>
+            )}
+            <Spin spinning={unref(props.loading)} tip="加载中...">
+              <ScrollContainer style={scrollStyle} v-show={!unref(getNotFound)}>
+                <Tree {...unref(getBindValues)} showIcon={false} treeData={treeData.value} />
+              </ScrollContainer>
+              <Empty
+                v-show={unref(getNotFound)}
+                image={Empty.PRESENTED_IMAGE_SIMPLE}
+                class="!mt-4"
+              />
+            </Spin>
+          </div>
+        );
+      };
+    },
+  });
+</script>

+ 2 - 6
src/components/Tree/src/TreeIcon.ts

@@ -1,14 +1,10 @@
 import type { VNode, FunctionalComponent } from 'vue';
 
 import { h } from 'vue';
-import { isString } from '/@/utils/is';
+import { isString } from '@vue/shared';
 import { Icon } from '/@/components/Icon';
 
-export interface ComponentProps {
-  icon: VNode | string;
-}
-
-export const TreeIcon: FunctionalComponent = ({ icon }: ComponentProps) => {
+export const TreeIcon: FunctionalComponent = ({ icon }: { icon: VNode | string }) => {
   if (!icon) return null;
   if (isString(icon)) {
     return h(Icon, { icon, class: 'mr-1' });

+ 171 - 0
src/components/Tree/src/components/TreeHeader.vue

@@ -0,0 +1,171 @@
+<template>
+  <div :class="bem()" class="flex px-2 py-1.5 items-center">
+    <slot name="headerTitle" v-if="slots.headerTitle"></slot>
+    <BasicTitle :helpMessage="helpMessage" v-if="!slots.headerTitle && title">
+      {{ title }}
+    </BasicTitle>
+    <div
+      class="flex items-center flex-1 cursor-pointer justify-self-stretch justify-end"
+      v-if="search || toolbar"
+    >
+      <div :class="getInputSearchCls" v-if="search">
+        <InputSearch
+          :placeholder="t('common.searchText')"
+          size="small"
+          allowClear
+          v-model:value="searchValue"
+          @search="$emit('clickSearch', $event)"
+        />
+      </div>
+      <Dropdown @click.prevent v-if="toolbar">
+        <Icon icon="ion:ellipsis-vertical" />
+        <template #overlay>
+          <Menu @click="handleMenuClick">
+            <template v-for="item in toolbarList" :key="item.value">
+              <MenuItem v-bind="{ key: item.value }">
+                {{ item.label }}
+              </MenuItem>
+              <MenuDivider v-if="item.divider" />
+            </template>
+          </Menu>
+        </template>
+      </Dropdown>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { computed, ref, watch, useSlots } from 'vue';
+  import { Dropdown, Menu, MenuItem, MenuDivider, InputSearch } from 'ant-design-vue';
+  import { Icon } from '/@/components/Icon';
+  import { BasicTitle } from '/@/components/Basic';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { useDebounceFn } from '@vueuse/core';
+  import { createBEM } from '/@/utils/bem';
+  import { ToolbarEnum } from '../types/tree';
+
+  const searchValue = ref('');
+
+  const [bem] = createBEM('tree-header');
+
+  const props = defineProps({
+    helpMessage: {
+      type: [String, Array] as PropType<string | string[]>,
+      default: '',
+    },
+    title: {
+      type: String,
+      default: '',
+    },
+    toolbar: {
+      type: Boolean,
+      default: false,
+    },
+    checkable: {
+      type: Boolean,
+      default: false,
+    },
+    search: {
+      type: Boolean,
+      default: false,
+    },
+    searchText: {
+      type: String,
+      default: '',
+    },
+    checkAll: {
+      type: Function,
+      default: undefined,
+    },
+    expandAll: {
+      type: Function,
+      default: undefined,
+    },
+  } as const);
+  const emit = defineEmits(['strictly-change', 'search', 'clickSearch']);
+
+  const slots = useSlots();
+  const { t } = useI18n();
+
+  const getInputSearchCls = computed(() => {
+    const titleExists = slots.headerTitle || props.title;
+    return [
+      'mr-1',
+      'w-full',
+      {
+        ['ml-5']: titleExists,
+      },
+    ];
+  });
+
+  const toolbarList = computed(() => {
+    const { checkable } = props;
+    const defaultToolbarList = [
+      { label: t('component.tree.expandAll'), value: ToolbarEnum.EXPAND_ALL },
+      {
+        label: t('component.tree.unExpandAll'),
+        value: ToolbarEnum.UN_EXPAND_ALL,
+        divider: checkable,
+      },
+    ];
+
+    return checkable
+      ? [
+          { label: t('component.tree.selectAll'), value: ToolbarEnum.SELECT_ALL },
+          {
+            label: t('component.tree.unSelectAll'),
+            value: ToolbarEnum.UN_SELECT_ALL,
+            divider: checkable,
+          },
+          ...defaultToolbarList,
+          { label: t('component.tree.checkStrictly'), value: ToolbarEnum.CHECK_STRICTLY },
+          { label: t('component.tree.checkUnStrictly'), value: ToolbarEnum.CHECK_UN_STRICTLY },
+        ]
+      : defaultToolbarList;
+  });
+
+  function handleMenuClick(e: { key: ToolbarEnum }) {
+    const { key } = e;
+    switch (key) {
+      case ToolbarEnum.SELECT_ALL:
+        props.checkAll?.(true);
+        break;
+      case ToolbarEnum.UN_SELECT_ALL:
+        props.checkAll?.(false);
+        break;
+      case ToolbarEnum.EXPAND_ALL:
+        props.expandAll?.(true);
+        break;
+      case ToolbarEnum.UN_EXPAND_ALL:
+        props.expandAll?.(false);
+        break;
+      case ToolbarEnum.CHECK_STRICTLY:
+        emit('strictly-change', false);
+        break;
+      case ToolbarEnum.CHECK_UN_STRICTLY:
+        emit('strictly-change', true);
+        break;
+    }
+  }
+
+  function emitChange(value?: string): void {
+    emit('search', value);
+  }
+
+  const debounceEmitChange = useDebounceFn(emitChange, 200);
+
+  watch(
+    () => searchValue.value,
+    (v) => {
+      debounceEmitChange(v);
+    },
+  );
+
+  watch(
+    () => props.searchText,
+    (v) => {
+      if (v !== searchValue.value) {
+        searchValue.value = v;
+      }
+    },
+  );
+</script>

+ 207 - 0
src/components/Tree/src/hooks/useTree.ts

@@ -0,0 +1,207 @@
+import type { InsertNodeParams, KeyType, FieldNames, TreeItem } from '../types/tree';
+import type { Ref, ComputedRef } from 'vue';
+import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
+
+import { cloneDeep } from 'lodash-es';
+import { unref } from 'vue';
+import { forEach } from '/@/utils/helper/treeHelper';
+
+export function useTree(treeDataRef: Ref<TreeDataItem[]>, getFieldNames: ComputedRef<FieldNames>) {
+  function getAllKeys(list?: TreeDataItem[]) {
+    const keys: string[] = [];
+    const treeData = list || unref(treeDataRef);
+    const { key: keyField, children: childrenField } = unref(getFieldNames);
+    if (!childrenField || !keyField) return keys;
+
+    for (let index = 0; index < treeData.length; index++) {
+      const node = treeData[index];
+      keys.push(node[keyField]!);
+      const children = node[childrenField];
+      if (children && children.length) {
+        keys.push(...(getAllKeys(children) as string[]));
+      }
+    }
+    return keys as KeyType[];
+  }
+
+  // get keys that can be checked and selected
+  function getEnabledKeys(list?: TreeDataItem[]) {
+    const keys: string[] = [];
+    const treeData = list || unref(treeDataRef);
+    const { key: keyField, children: childrenField } = unref(getFieldNames);
+    if (!childrenField || !keyField) return keys;
+
+    for (let index = 0; index < treeData.length; index++) {
+      const node = treeData[index];
+      node.disabled !== true && node.selectable !== false && keys.push(node[keyField]!);
+      const children = node[childrenField];
+      if (children && children.length) {
+        keys.push(...(getEnabledKeys(children) as string[]));
+      }
+    }
+    return keys as KeyType[];
+  }
+
+  function getChildrenKeys(nodeKey: string | number, list?: TreeDataItem[]) {
+    const keys: KeyType[] = [];
+    const treeData = list || unref(treeDataRef);
+    const { key: keyField, children: childrenField } = unref(getFieldNames);
+    if (!childrenField || !keyField) return keys;
+    for (let index = 0; index < treeData.length; index++) {
+      const node = treeData[index];
+      const children = node[childrenField];
+      if (nodeKey === node[keyField]) {
+        keys.push(node[keyField]!);
+        if (children && children.length) {
+          keys.push(...(getAllKeys(children) as string[]));
+        }
+      } else {
+        if (children && children.length) {
+          keys.push(...getChildrenKeys(nodeKey, children));
+        }
+      }
+    }
+    return keys as KeyType[];
+  }
+
+  // Update node
+  function updateNodeByKey(key: string, node: TreeDataItem, list?: TreeDataItem[]) {
+    if (!key) return;
+    const treeData = list || unref(treeDataRef);
+    const { key: keyField, children: childrenField } = unref(getFieldNames);
+
+    if (!childrenField || !keyField) return;
+
+    for (let index = 0; index < treeData.length; index++) {
+      const element: any = treeData[index];
+      const children = element[childrenField];
+
+      if (element[keyField] === key) {
+        treeData[index] = { ...treeData[index], ...node };
+        break;
+      } else if (children && children.length) {
+        updateNodeByKey(key, node, element[childrenField]);
+      }
+    }
+  }
+
+  // Expand the specified level
+  function filterByLevel(level = 1, list?: TreeDataItem[], currentLevel = 1) {
+    if (!level) {
+      return [];
+    }
+    const res: (string | number)[] = [];
+    const data = list || unref(treeDataRef) || [];
+    for (let index = 0; index < data.length; index++) {
+      const item = data[index];
+
+      const { key: keyField, children: childrenField } = unref(getFieldNames);
+      const key = keyField ? item[keyField] : '';
+      const children = childrenField ? item[childrenField] : [];
+      res.push(key);
+      if (children && children.length && currentLevel < level) {
+        currentLevel += 1;
+        res.push(...filterByLevel(level, children, currentLevel));
+      }
+    }
+    return res as string[] | number[];
+  }
+
+  /**
+   * 添加节点
+   */
+  function insertNodeByKey({ parentKey = null, node, push = 'push' }: InsertNodeParams) {
+    const treeData: any = cloneDeep(unref(treeDataRef));
+    if (!parentKey) {
+      treeData[push](node);
+      treeDataRef.value = treeData;
+      return;
+    }
+    const { key: keyField, children: childrenField } = unref(getFieldNames);
+    if (!childrenField || !keyField) return;
+
+    forEach(treeData, (treeItem) => {
+      if (treeItem[keyField] === parentKey) {
+        treeItem[childrenField] = treeItem[childrenField] || [];
+        treeItem[childrenField][push](node);
+        return true;
+      }
+    });
+    treeDataRef.value = treeData;
+  }
+  /**
+   * 批量添加节点
+   */
+  function insertNodesByKey({ parentKey = null, list, push = 'push' }: InsertNodeParams) {
+    const treeData: any = cloneDeep(unref(treeDataRef));
+    if (!list || list.length < 1) {
+      return;
+    }
+    if (!parentKey) {
+      for (let i = 0; i < list.length; i++) {
+        treeData[push](list[i]);
+      }
+    } else {
+      const { key: keyField, children: childrenField } = unref(getFieldNames);
+      if (!childrenField || !keyField) return;
+
+      forEach(treeData, (treeItem) => {
+        if (treeItem[keyField] === parentKey) {
+          treeItem[childrenField] = treeItem[childrenField] || [];
+          for (let i = 0; i < list.length; i++) {
+            treeItem[childrenField][push](list[i]);
+          }
+          treeDataRef.value = treeData;
+          return true;
+        }
+      });
+    }
+  }
+  // Delete node
+  function deleteNodeByKey(key: string, list?: TreeDataItem[]) {
+    if (!key) return;
+    const treeData = list || unref(treeDataRef);
+    const { key: keyField, children: childrenField } = unref(getFieldNames);
+    if (!childrenField || !keyField) return;
+
+    for (let index = 0; index < treeData.length; index++) {
+      const element: any = treeData[index];
+      const children = element[childrenField];
+
+      if (element[keyField] === key) {
+        treeData.splice(index, 1);
+        break;
+      } else if (children && children.length) {
+        deleteNodeByKey(key, element[childrenField]);
+      }
+    }
+  }
+
+  // Get selected node
+  function getSelectedNode(key: KeyType, list?: TreeItem[], selectedNode?: TreeItem | null) {
+    if (!key && key !== 0) return null;
+    const treeData = list || unref(treeDataRef);
+    treeData.forEach((item) => {
+      if (selectedNode?.key || selectedNode?.key === 0) return selectedNode;
+      if (item.key === key) {
+        selectedNode = item;
+        return;
+      }
+      if (item.children && item.children.length) {
+        selectedNode = getSelectedNode(key, item.children, selectedNode);
+      }
+    });
+    return selectedNode || null;
+  }
+  return {
+    deleteNodeByKey,
+    insertNodeByKey,
+    insertNodesByKey,
+    filterByLevel,
+    updateNodeByKey,
+    getAllKeys,
+    getChildrenKeys,
+    getEnabledKeys,
+    getSelectedNode,
+  };
+}

+ 195 - 0
src/components/Tree/src/types/tree.ts

@@ -0,0 +1,195 @@
+import type { ExtractPropTypes } from 'vue';
+import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
+
+import { buildProps } from '/@/utils/props';
+
+export enum ToolbarEnum {
+  SELECT_ALL,
+  UN_SELECT_ALL,
+  EXPAND_ALL,
+  UN_EXPAND_ALL,
+  CHECK_STRICTLY,
+  CHECK_UN_STRICTLY,
+}
+
+export const treeEmits = [
+  'update:expandedKeys',
+  'update:selectedKeys',
+  'update:value',
+  'change',
+  'check',
+  'search',
+  'update:searchValue',
+];
+
+export interface TreeState {
+  expandedKeys: KeyType[];
+  selectedKeys: KeyType[];
+  checkedKeys: CheckKeys;
+  checkStrictly: boolean;
+}
+
+export interface FieldNames {
+  children?: string;
+  title?: string;
+  key?: string;
+}
+
+export type KeyType = string | number;
+
+export type CheckKeys =
+  | KeyType[]
+  | { checked: string[] | number[]; halfChecked: string[] | number[] };
+
+export const treeProps = buildProps({
+  value: {
+    type: [Object, Array] as PropType<KeyType[] | CheckKeys>,
+  },
+
+  renderIcon: {
+    type: Function as PropType<(params: Recordable) => string>,
+  },
+
+  helpMessage: {
+    type: [String, Array] as PropType<string | string[]>,
+    default: '',
+  },
+
+  title: {
+    type: String,
+    default: '',
+  },
+  toolbar: Boolean,
+  search: Boolean,
+  searchValue: {
+    type: String,
+    default: '',
+  },
+  checkStrictly: Boolean,
+  clickRowToExpand: {
+    type: Boolean,
+    default: false,
+  },
+  checkable: Boolean,
+  defaultExpandLevel: {
+    type: [String, Number] as PropType<string | number>,
+    default: '',
+  },
+  defaultExpandAll: Boolean,
+
+  fieldNames: {
+    type: Object as PropType<FieldNames>,
+  },
+
+  treeData: {
+    type: Array as PropType<TreeDataItem[]>,
+  },
+
+  actionList: {
+    type: Array as PropType<TreeActionItem[]>,
+    default: () => [],
+  },
+
+  expandedKeys: {
+    type: Array as PropType<KeyType[]>,
+    default: () => [],
+  },
+
+  selectedKeys: {
+    type: Array as PropType<KeyType[]>,
+    default: () => [],
+  },
+
+  checkedKeys: {
+    type: Array as PropType<CheckKeys>,
+    default: () => [],
+  },
+
+  beforeRightClick: {
+    type: Function as PropType<(...arg: any) => ContextMenuItem[] | ContextMenuOptions>,
+    default: undefined,
+  },
+
+  rightMenuList: {
+    type: Array as PropType<ContextMenuItem[]>,
+  },
+  // 自定义数据过滤判断方法(注: 不是整个过滤方法,而是内置过滤的判断方法,用于增强原本仅能通过title进行过滤的方式)
+  filterFn: {
+    type: Function as PropType<
+      (searchValue: any, node: TreeItem, fieldNames: FieldNames) => boolean
+    >,
+    default: undefined,
+  },
+  // 高亮搜索值,仅高亮具体匹配值(通过title)值为true时使用默认色值,值为#xxx时使用此值替代且高亮开启
+  highlight: {
+    type: [Boolean, String] as PropType<Boolean | String>,
+    default: false,
+  },
+  // 搜索完成时自动展开结果
+  expandOnSearch: Boolean,
+  // 搜索完成自动选中所有结果,当且仅当 checkable===true 时生效
+  checkOnSearch: Boolean,
+  // 搜索完成自动select所有结果
+  selectedOnSearch: Boolean,
+  loading: {
+    type: Boolean,
+    default: false,
+  },
+});
+
+export type TreeProps = ExtractPropTypes<typeof treeProps>;
+
+export interface ContextMenuItem {
+  label: string;
+  icon?: string;
+  hidden?: boolean;
+  disabled?: boolean;
+  handler?: Fn;
+  divider?: boolean;
+  children?: ContextMenuItem[];
+}
+
+export interface ContextMenuOptions {
+  icon?: string;
+  styles?: any;
+  items?: ContextMenuItem[];
+}
+
+export interface TreeItem extends TreeDataItem {
+  icon?: any;
+}
+
+export interface TreeActionItem {
+  render: (record: Recordable) => any;
+  show?: boolean | ((record: Recordable) => boolean);
+}
+
+export interface InsertNodeParams {
+  parentKey: string | null;
+  node: TreeDataItem;
+  list?: TreeDataItem[];
+  push?: 'push' | 'unshift';
+}
+
+export interface TreeActionType {
+  checkAll: (checkAll: boolean) => void;
+  expandAll: (expandAll: boolean) => void;
+  setExpandedKeys: (keys: KeyType[]) => void;
+  getExpandedKeys: () => KeyType[];
+  setSelectedKeys: (keys: KeyType[]) => void;
+  getSelectedKeys: () => KeyType[];
+  setCheckedKeys: (keys: CheckKeys) => void;
+  getCheckedKeys: () => CheckKeys;
+  filterByLevel: (level: number) => void;
+  insertNodeByKey: (opt: InsertNodeParams) => void;
+  insertNodesByKey: (opt: InsertNodeParams) => void;
+  deleteNodeByKey: (key: string) => void;
+  updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void;
+  setSearchValue: (value: string) => void;
+  getSearchValue: () => string;
+  getSelectedNode: (
+    key: KeyType,
+    treeList?: TreeItem[],
+    selectNode?: TreeItem | null,
+  ) => TreeItem | null;
+}

+ 52 - 0
src/components/Tree/style/index.less

@@ -0,0 +1,52 @@
+@tree-prefix-cls: ~'@{namespace}-tree';
+
+.@{tree-prefix-cls} {
+  background-color: @component-background;
+
+  .ant-tree-node-content-wrapper {
+    position: relative;
+
+    .ant-tree-title {
+      position: absolute;
+      left: 0;
+      width: 100%;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+  }
+
+  &__title {
+    position: relative;
+    display: flex;
+    align-items: center;
+    width: 100%;
+    padding-right: 10px;
+
+    &:hover {
+      .@{tree-prefix-cls}__action {
+        visibility: visible;
+      }
+    }
+  }
+
+  &__content {
+    overflow: hidden;
+  }
+
+  &__actions {
+    position: absolute;
+    //top: 2px;
+    right: 3px;
+    display: flex;
+  }
+
+  &__action {
+    margin-left: 4px;
+    visibility: hidden;
+  }
+
+  &-header {
+    border-bottom: 1px solid @border-color-base;
+  }
+}

+ 1 - 0
src/components/Tree/style/index.ts

@@ -0,0 +1 @@
+import './index.less';

+ 5 - 0
src/components/Tree_backup/index.ts

@@ -0,0 +1,5 @@
+import BasicTree from './src/Tree.vue';
+
+export { BasicTree };
+export type { ContextMenuItem } from '/@/hooks/web/useContextMenu';
+export * from './src/typing';

+ 2 - 1
src/components/Tree/src/Tree.vue → src/components/Tree_backup/src/Tree.vue

@@ -107,7 +107,8 @@
         return !getTreeData.value || getTreeData.value.length === 0;
       });
 
-      const { deleteNodeByKey, insertNodeByKey, insertNodesByKey, filterByLevel, updateNodeByKey, getAllKeys, getChildrenKeys, getEnabledKeys } = useTree(treeDataRef, getReplaceFields);
+      const { deleteNodeByKey, insertNodeByKey, insertNodesByKey, filterByLevel, updateNodeByKey, getAllKeys, getChildrenKeys, getEnabledKeys } =
+        useTree(treeDataRef, getReplaceFields);
 
       function getIcon(params: Recordable, icon?: string) {
         if (!icon) {

+ 7 - 1
src/components/Tree/src/TreeHeader.vue → src/components/Tree_backup/src/TreeHeader.vue

@@ -7,7 +7,13 @@
 
     <div class="flex flex-1 justify-end items-center cursor-pointer" v-if="search || toolbar">
       <div :class="getInputSearchCls" v-if="search">
-        <InputSearch :placeholder="t('common.searchText')" size="small" allowClear v-model:value="searchValue" @search="$emit('clickSearch', $event)" />
+        <InputSearch
+          :placeholder="t('common.searchText')"
+          size="small"
+          allowClear
+          v-model:value="searchValue"
+          @search="$emit('clickSearch', $event)"
+        />
       </div>
       <Dropdown @click.prevent v-if="toolbar">
         <Icon icon="ion:ellipsis-vertical" />

+ 17 - 0
src/components/Tree_backup/src/TreeIcon.ts

@@ -0,0 +1,17 @@
+import type { VNode, FunctionalComponent } from 'vue';
+
+import { h } from 'vue';
+import { isString } from '/@/utils/is';
+import { Icon } from '/@/components/Icon';
+
+export interface ComponentProps {
+  icon: VNode | string;
+}
+
+export const TreeIcon: FunctionalComponent = ({ icon }: ComponentProps) => {
+  if (!icon) return null;
+  if (isString(icon)) {
+    return h(Icon, { icon, class: 'mr-1' });
+  }
+  return Icon;
+};

+ 0 - 0
src/components/Tree/src/props.ts → src/components/Tree_backup/src/props.ts


+ 0 - 0
src/components/Tree/src/typing.ts → src/components/Tree_backup/src/typing.ts


+ 0 - 0
src/components/Tree/src/useTree.ts → src/components/Tree_backup/src/useTree.ts


+ 5 - 1
src/components/Upload/src/UploadModal.vue

@@ -96,7 +96,11 @@
 
       const getUploadBtnText = computed(() => {
         const someError = fileListRef.value.some((item) => item.status === UploadResultStatus.ERROR);
-        return isUploadingRef.value ? t('component.upload.uploading') : someError ? t('component.upload.reUploadFailed') : t('component.upload.startUpload');
+        return isUploadingRef.value
+          ? t('component.upload.uploading')
+          : someError
+          ? t('component.upload.reUploadFailed')
+          : t('component.upload.startUpload');
       });
 
       // 上传前校验

+ 8 - 1
src/components/Upload/src/UploadPreviewModal.vue

@@ -1,5 +1,12 @@
 <template>
-  <BasicModal width="800px" :title="t('component.upload.preview')" wrapClassName="upload-preview-modal" v-bind="$attrs" @register="register" :showOkBtn="false">
+  <BasicModal
+    width="800px"
+    :title="t('component.upload.preview')"
+    wrapClassName="upload-preview-modal"
+    v-bind="$attrs"
+    @register="register"
+    :showOkBtn="false"
+  >
     <FileList :dataSource="fileListRef" :columns="columns" :actionColumn="actionColumn" />
   </BasicModal>
 </template>

+ 11 - 1
src/components/Upload/src/useUpload.ts

@@ -1,7 +1,17 @@
 import { Ref, unref, computed } from 'vue';
 import { useI18n } from '/@/hooks/web/useI18n';
 const { t } = useI18n();
-export function useUploadType({ acceptRef, helpTextRef, maxNumberRef, maxSizeRef }: { acceptRef: Ref<string[]>; helpTextRef: Ref<string>; maxNumberRef: Ref<number>; maxSizeRef: Ref<number> }) {
+export function useUploadType({
+  acceptRef,
+  helpTextRef,
+  maxNumberRef,
+  maxSizeRef,
+}: {
+  acceptRef: Ref<string[]>;
+  helpTextRef: Ref<string>;
+  maxNumberRef: Ref<number>;
+  maxSizeRef: Ref<number>;
+}) {
   // 文件类型限制
   const getAccept = computed(() => {
     const accept = unref(acceptRef);

+ 12 - 2
src/components/Verify/src/DragVerify.vue

@@ -249,7 +249,8 @@
           }
           return (
             <div class={cls} onMousedown={handleDragStart} onTouchstart={handleDragStart} style={unref(getActionStyleRef)} ref={actionElRef}>
-              {getSlot(slots, 'actionIcon', isPassing) || (isPassing ? <CheckOutlined class={`darg-verify-action__icon`} /> : <DoubleRightOutlined class={`darg-verify-action__icon`} />)}
+              {getSlot(slots, 'actionIcon', isPassing) ||
+                (isPassing ? <CheckOutlined class={`darg-verify-action__icon`} /> : <DoubleRightOutlined class={`darg-verify-action__icon`} />)}
             </div>
           );
         };
@@ -303,7 +304,16 @@
       top: 0;
       font-size: 12px;
       -webkit-text-size-adjust: none;
-      background-color: -webkit-gradient(linear, left top, right top, color-stop(0, #333), color-stop(0.4, #333), color-stop(0.5, #fff), color-stop(0.6, #333), color-stop(1, #333));
+      background-color: -webkit-gradient(
+        linear,
+        left top,
+        right top,
+        color-stop(0, #333),
+        color-stop(0.4, #333),
+        color-stop(0.5, #fff),
+        color-stop(0.6, #333),
+        color-stop(1, #333)
+      );
       animation: slidetounlock 3s infinite;
       background-clip: text;
       user-select: none;

+ 3 - 1
src/components/Verify/src/ImgRotate.vue

@@ -139,7 +139,9 @@
                 alt="verify"
               />
               {state.showTip && (
-                <span class={[`ir-dv-img__tip`, state.isPassing ? 'success' : 'error']}>{state.isPassing ? t('component.verify.time', { time: time.toFixed(1) }) : t('component.verify.error')}</span>
+                <span class={[`ir-dv-img__tip`, state.isPassing ? 'success' : 'error']}>
+                  {state.isPassing ? t('component.verify.time', { time: time.toFixed(1) }) : t('component.verify.error')}
+                </span>
               )}
               {!state.showTip && !state.draged && <span class={[`ir-dv-img__tip`, 'normal']}>{t('component.verify.redoTip')}</span>}
             </div>

+ 21 - 15
src/components/jeecg/JPrompt/JPrompt.vue

@@ -1,16 +1,18 @@
 <template>
-  <Modal v-bind="getProps">
-    <Spin :spinning="loading">
-      <div style="padding: 20px">
-        <div v-html="options.content" style="margin-bottom: 8px"></div>
-        <BasicForm @register="registerForm">
-          <template #customInput="{ model, field }">
-            <Input ref="inputRef" v-model:value="model[field]" :placeholder="placeholder" @pressEnter="onSubmit" @input="onChange" />
-          </template>
-        </BasicForm>
-      </div>
-    </Spin>
-  </Modal>
+  <ConfigProvider :locale="getAntdLocale">
+    <Modal v-bind="getProps">
+      <Spin :spinning="loading">
+        <div style="padding: 20px;">
+          <div v-html="options.content" style="margin-bottom: 8px"></div>
+          <BasicForm @register="registerForm">
+            <template #customInput="{ model, field }">
+              <Input ref="inputRef" v-model:value="model[field]" :placeholder="placeholder" @pressEnter="onSubmit" @input="onChange" />
+            </template>
+          </BasicForm>
+        </div>
+      </Spin>
+    </Modal>
+  </ConfigProvider>
 </template>
 
 <script lang="ts">
@@ -18,7 +20,8 @@
   import type { ModalProps } from '/@/components/Modal';
   import { ref, defineComponent, computed, unref, onMounted, nextTick } from 'vue';
   import { BasicForm, useForm } from '/@/components/Form';
-  import { Modal, Spin, Input } from 'ant-design-vue';
+  import { Modal, Spin, Input, ConfigProvider } from 'ant-design-vue';
+  import { useLocale } from '/@/locales/useLocale';
 
   export default defineComponent({
     name: 'JPrompt',
@@ -27,10 +30,12 @@
       Spin,
       Input,
       BasicForm,
+      ConfigProvider,
     },
     emits: ['register'],
     setup(props, { emit }) {
       const inputRef = ref();
+      const { getAntdLocale } = useLocale();
       const visible = ref(false);
       // 当前是否正在加载中
       const loading = ref(false);
@@ -109,9 +114,9 @@
       }
 
       function onChange() {
-        validate();
+        validate()
       }
-
+      
       /** 提交表单 */
       async function onSubmit() {
         try {
@@ -144,6 +149,7 @@
         loading,
         options,
         placeholder,
+        getAntdLocale,
         onChange,
         onSubmit,
 

+ 1 - 0
src/components/jeecg/JPrompt/hooks/useJPrompt.ts

@@ -4,6 +4,7 @@ import { error } from '/@/utils/log';
 import JPrompt from '../JPrompt.vue';
 
 export function useJPrompt() {
+
   function createJPrompt(options: JPromptProps) {
     let instance = null;
     const box = document.createElement('div');

+ 28 - 2
src/components/jeecg/OnLine/JPopupOnlReport.vue

@@ -91,8 +91,34 @@
       const tableScroll = ref({ x: true });
       const getBindValue = Object.assign({}, unref(props), unref(attrs));
       const [
-        { visibleChange, loadColumnsInfo, dynamicParamHandler, loadData, loadColumnsAndData, handleChangeInTable, combineRowKey, clickThenCheck, filterUnuseSelect, handleExport },
-        { hrefComponent, visible, rowSelection, checkedKeys, selectRows, pagination, dataSource, columns, loading, title, iSorter, queryInfo, queryParam, dictOptions },
+        {
+          visibleChange,
+          loadColumnsInfo,
+          dynamicParamHandler,
+          loadData,
+          loadColumnsAndData,
+          handleChangeInTable,
+          combineRowKey,
+          clickThenCheck,
+          filterUnuseSelect,
+          handleExport,
+        },
+        {
+          hrefComponent,
+          visible,
+          rowSelection,
+          checkedKeys,
+          selectRows,
+          pagination,
+          dataSource,
+          columns,
+          loading,
+          title,
+          iSorter,
+          queryInfo,
+          queryParam,
+          dictOptions,
+        },
       ] = usePopBiz(getBindValue);
 
       const showSearchFlag = computed(() => unref(queryInfo) && unref(queryInfo).length > 0);

+ 63 - 11
src/components/jeecg/OnLine/SearchFormItem.vue

@@ -4,12 +4,29 @@
       <span :title="item.label" class="label-text">{{ item.label }}</span>
     </template>
     <template v-if="single_mode === item.mode">
-      <a-date-picker :showTime="false" valueFormat="YYYY-MM-DD" :placeholder="'请选择' + item.label" v-model:value="queryParam[item.field]"></a-date-picker>
+      <a-date-picker
+        :showTime="false"
+        valueFormat="YYYY-MM-DD"
+        :placeholder="'请选择' + item.label"
+        v-model:value="queryParam[item.field]"
+      ></a-date-picker>
     </template>
     <template v-else>
-      <a-date-picker :showTime="false" valueFormat="YYYY-MM-DD" placeholder="开始日期" v-model:value="queryParam[item.field + '_begin']" style="width: calc(50% - 15px)"></a-date-picker>
+      <a-date-picker
+        :showTime="false"
+        valueFormat="YYYY-MM-DD"
+        placeholder="开始日期"
+        v-model:value="queryParam[item.field + '_begin']"
+        style="width: calc(50% - 15px)"
+      ></a-date-picker>
       <span class="group-query-strig">~</span>
-      <a-date-picker :showTime="false" valueFormat="YYYY-MM-DD" placeholder="结束日期" v-model:value="queryParam[item.field + '_end']" style="width: calc(50% - 15px)"></a-date-picker>
+      <a-date-picker
+        :showTime="false"
+        valueFormat="YYYY-MM-DD"
+        placeholder="结束日期"
+        v-model:value="queryParam[item.field + '_end']"
+        style="width: calc(50% - 15px)"
+      ></a-date-picker>
     </template>
   </a-form-item>
 
@@ -18,7 +35,12 @@
       <span :title="item.label" class="label-text">{{ item.label }}</span>
     </template>
     <template v-if="single_mode === item.mode">
-      <a-date-picker :placeholder="'请选择' + item.label" :show-time="true" valueFormat="YYYY-MM-DD HH:mm:ss" v-model:value="queryParam[item.field]"></a-date-picker>
+      <a-date-picker
+        :placeholder="'请选择' + item.label"
+        :show-time="true"
+        valueFormat="YYYY-MM-DD HH:mm:ss"
+        v-model:value="queryParam[item.field]"
+      ></a-date-picker>
     </template>
     <template v-else>
       <a-date-picker
@@ -47,17 +69,34 @@
       <a-date-picker :placeholder="'请选择' + item.label" mode="time" valueFormat="HH:mm:ss" v-model:value="queryParam[item.field]"></a-date-picker>
     </template>
     <template v-else>
-      <a-date-picker placeholder="请选择开始时间" mode="time" valueFormat="HH:mm:ss" v-model:value="queryParam[item.field + '_begin']" style="width: calc(50% - 15px)"></a-date-picker>
+      <a-date-picker
+        placeholder="请选择开始时间"
+        mode="time"
+        valueFormat="HH:mm:ss"
+        v-model:value="queryParam[item.field + '_begin']"
+        style="width: calc(50% - 15px)"
+      ></a-date-picker>
       <span class="group-query-strig">~</span>
-      <a-date-picker placeholder="请选择结束时间" mode="time" valueFormat="HH:mm:ss" v-model:value="queryParam[item.field + '_end']" style="width: calc(50% - 15px)"></a-date-picker>
+      <a-date-picker
+        placeholder="请选择结束时间"
+        mode="time"
+        valueFormat="HH:mm:ss"
+        v-model:value="queryParam[item.field + '_end']"
+        style="width: calc(50% - 15px)"
+      ></a-date-picker>
     </template>
   </a-form-item>
 
-  <a-form-item v-else-if="item.view === CompTypeEnum.List || item.view === CompTypeEnum.Radio || item.view === CompTypeEnum.Switch" :labelCol="labelCol" :class="'jeecg-online-search'">
+  <a-form-item
+    v-else-if="item.view === CompTypeEnum.List || item.view === CompTypeEnum.Radio || item.view === CompTypeEnum.Switch"
+    :labelCol="labelCol"
+    :class="'jeecg-online-search'"
+  >
     <template #label>
       <span :title="item.label" class="label-text">{{ item.label }}</span>
     </template>
-    <JDictSelectTag v-if="item.config === '1'" :placeholder="'请选择' + item.label" v-model="queryParam[item.field]" :dictCode="getDictCode(item)"> </JDictSelectTag>
+    <JDictSelectTag v-if="item.config === '1'" :placeholder="'请选择' + item.label" v-model="queryParam[item.field]" :dictCode="getDictCode(item)">
+    </JDictSelectTag>
     <a-select v-else :placeholder="'请选择' + item.label" v-model:value="queryParam[item.field]">
       <template v-for="(obj, index) in dictOptions[getDictOptionKey(item)]" :key="index">
         <a-select-option :value="obj.value"> {{ obj.text }}</a-select-option>
@@ -92,7 +131,8 @@
     <template #label>
       <span :title="item.label" class="label-text">{{ item.label }}</span>
     </template>
-    <JDictSelectTag v-if="item.config === '1'" v-model:value="queryParam[item.field]" :placeholder="'请选择' + item.label" :dict="getDictCode(item)"> </JDictSelectTag>
+    <JDictSelectTag v-if="item.config === '1'" v-model:value="queryParam[item.field]" :placeholder="'请选择' + item.label" :dict="getDictCode(item)">
+    </JDictSelectTag>
     <!--TODO 新需要的组件-->
     <!-- <j-online-search-select
                 v-else
@@ -121,7 +161,14 @@
     <template #label>
       <span :title="item.label" class="label-text">{{ item.label }}</span>
     </template>
-    <JPopup :placeholder="'请选择' + item.label" v-model:value="queryParam[item.field]" :formElRef="formElRef" :code="item.dictTable" :field-config="item.dictCode" :multi="true" />
+    <JPopup
+      :placeholder="'请选择' + item.label"
+      v-model:value="queryParam[item.field]"
+      :formElRef="formElRef"
+      :code="item.dictTable"
+      :field-config="item.dictCode"
+      :multi="true"
+    />
   </a-form-item>
 
   <a-form-item v-else-if="item.view === CompTypeEnum.Pca" :labelCol="labelCol" :class="'jeecg-online-search'">
@@ -131,7 +178,12 @@
     <JAreaLinkage :placeholder="'请选择' + item.label" v-model:value="queryParam[item.field]" />
   </a-form-item>
   <!--TODO 缺少的组件-->
-  <a-form-item v-else-if="item.view === CompTypeEnum.Checkbox || item.view === CompTypeEnum.ListMulti" :labelCol="labelCol" :label="item.label" :class="'jeecg-online-search'">
+  <a-form-item
+    v-else-if="item.view === CompTypeEnum.Checkbox || item.view === CompTypeEnum.ListMulti"
+    :labelCol="labelCol"
+    :label="item.label"
+    :class="'jeecg-online-search'"
+  >
     <!-- <j-select-multiple
                 v-if="item.config==='1'"
                 :placeholder=" '请选择'+item.label "

+ 1 - 1
src/components/jeecg/OnLine/hooks/usePopBiz.ts

@@ -290,7 +290,7 @@ export function usePopBiz(props, tableRef?) {
       if (column.isTotal === '1') {
         arr.push(column.dataIndex!);
       }
-      // 【VUEN-1569】【online报表】合计无效
+        // 【VUEN-1569】【online报表】合计无效
       if (column.children && column.children.length > 0) {
         let subArray = getNeedSumColumns(column.children);
         if (subArray.length > 0) {

+ 6 - 0
src/components/jeecg/OnLine/types/onlineConfig.ts

@@ -32,6 +32,12 @@ interface OnlineColumn {
   slots?: ScopedSlots;
   //超过宽度将自动省略,暂不支持和排序筛选一起使用。
   ellipsis?: boolean;
+  // 是否固定列
+  fixed?: boolean | 'left' | 'right';
+  //字段类型 int/string 
+  dbType?:string;
+  //他表字段用
+  linkField?:string;
 }
 
 export { OnlineColumn, HrefSlots };

+ 0 - 401
src/components/jeecg/super/superquery/SuperQuery.vue

@@ -1,401 +0,0 @@
-<template>
-  <!-- 按钮区域 -->
-  <div class="j-super-query-button">
-    <a-tooltip v-if="superQueryFlag" :mouseLeaveDelay="0.2">
-      <template #title>
-        <span>已有高级查询条件生效</span>
-        <divider type="vertical" style="background-color: #fff" />
-        <a @click="handleReset">清空</a>
-      </template>
-      <a-button-group>
-        <a-button type="primary" @click="handleOpen">
-          <AppstoreTwoTone :spin="true" />
-          <span>高级查询</span>
-        </a-button>
-      </a-button-group>
-    </a-tooltip>
-    <a-button v-else type="primary" preIcon="ant-design:filter-outlined" @click="handleOpen"> 高级查询 </a-button>
-  </div>
-
-  <!-- 高级查询弹框 -->
-  <teleport to="body">
-    <BasicModal title="高级查询构造器" :canFullscreen="false" :width="1050" @register="registerFormModal" @ok="handleSubmit">
-      <template #footer>
-        <div style="float: left">
-          <a-button :loading="loading" @click="handleReset">重置</a-button>
-          <a-button :loading="loading" @click="handleSave">保存查询条件</a-button>
-        </div>
-
-        <a-button key="submit" type="primary" @click="handleSubmit">确定</a-button>
-        <a-button key="back" @click="handleCancel">关闭</a-button>
-      </template>
-
-      <a-empty v-if="dynamicRowValues.values.length == 0">
-        <div slot="description">
-          <span>没有任何查询条件</span>
-          <a-divider type="vertical" />
-          <a @click="addOne(-1)">点击新增</a>
-        </div>
-      </a-empty>
-
-      <a-row :class="'j-super-query-modal-content'">
-        <a-col :sm="24" :md="18">
-          <a-row v-show="dynamicRowValues.values.length > 0">
-            <a-col :md="12" :xs="24">
-              <a-form-item label="过滤条件匹配" :labelCol="{ md: 6, xs: 24 }" :wrapperCol="{ md: 18, xs: 24 }" style="width: 100%">
-                <a-select v-model:value="matchType" :getPopupContainer="(node) => node.parentNode" style="width: 100%">
-                  <a-select-option value="and">AND(所有条件都要求匹配)</a-select-option>
-                  <a-select-option value="or">OR(条件中的任意一个匹配)</a-select-option>
-                </a-select>
-              </a-form-item>
-            </a-col>
-          </a-row>
-
-          <a-form v-show="dynamicRowValues.values.length > 0" ref="formRef" :class="'jee-super-query-form'" :model="dynamicRowValues" @finish="onFinish">
-            <a-space v-for="(item, index) in dynamicRowValues.values" :key="item.key" style="display: flex; margin-bottom: 8px" align="baseline">
-              <a-form-item :name="['values', index, 'field']" style="width: 180px">
-                <a-tree-select
-                  style="width: 100%"
-                  placeholder="请选择字段"
-                  v-model:value="item.field"
-                  show-search
-                  tree-node-filter-prop="title"
-                  allow-clear
-                  tree-default-expand-all
-                  :dropdown-style="{ maxHeight: '180px', overflow: 'auto' }"
-                  @change="handleChangeField(item)"
-                  :tree-data="fieldTreeData"
-                >
-                </a-tree-select>
-              </a-form-item>
-              <a-form-item :name="['values', index, 'rule']" style="width: 180px">
-                <a-select style="width: 100%" placeholder="请选择匹配规则" v-model:value="item.rule">
-                  <a-select-option value="eq">等于</a-select-option>
-                  <a-select-option value="like">模糊</a-select-option>
-                  <a-select-option value="right_like">以..开始</a-select-option>
-                  <a-select-option value="left_like">以..结尾</a-select-option>
-                  <a-select-option value="in">在...中</a-select-option>
-                  <a-select-option value="ne">不等于</a-select-option>
-                  <a-select-option value="gt">大于</a-select-option>
-                  <a-select-option value="ge">大于等于</a-select-option>
-                  <a-select-option value="lt">小于</a-select-option>
-                  <a-select-option value="le">小于等于</a-select-option>
-                </a-select>
-              </a-form-item>
-              <a-form-item :name="['values', index, 'val']" style="width: 280px">
-                <online-super-query-val-component
-                  style="width: 100%"
-                  :schema="getSchema(item, index)"
-                  :formModel="item"
-                  :setFormModel="
-                    (key, value) => {
-                      setFormModel(key, value, item);
-                    }
-                  "
-                />
-              </a-form-item>
-
-              <a-form-item>
-                <a-button @click="addOne(index)" style="margin-right: 6px">
-                  <PlusOutlined #icon />
-                </a-button>
-                <a-button @click="removeOne(item)">
-                  <MinusCircleOutlined #icon />
-                </a-button>
-              </a-form-item>
-            </a-space>
-          </a-form>
-        </a-col>
-
-        <a-col :sm="24" :md="6">
-          <!-- 查询记录 -->
-          <a-card class="j-super-query-history-card" :bordered="true">
-            <template #title><div>保存的查询</div></template>
-            <a-empty v-if="saveTreeData.length === 0" class="j-super-query-history-empty" description="没有保存任何查询" />
-            <a-tree v-else class="j-super-query-history-tree" :treeData="saveTreeData" :selectedKeys="[]" :show-icon="true" @select="handleTreeSelect">
-              <template #title="{ title }">
-                <div>
-                  <span :title="title">{{ title.length > 10 ? title.substring(0, 10) + '...' : title }}</span>
-                  <span class="icon-cancle"><close-circle-outlined @click="handleRemoveSaveInfo(title)" /></span>
-                </div>
-              </template>
-              <!-- antd-2是这么写的 升级到3会也许会改变写法 -->
-              <template #custom>
-                <file-text-outlined />
-              </template>
-            </a-tree>
-          </a-card>
-        </a-col>
-      </a-row>
-    </BasicModal>
-  </teleport>
-
-  <!-- 保存信息弹框 -->
-  <a-modal title="请输入保存的名称" :visible="saveInfo.visible" @cancel="saveInfo.visible = false" @ok="doSaveQueryInfo">
-    <div style="height: 80px; line-height: 75px; width: 100%; text-align: center">
-      <a-input v-model:value="saveInfo.title" style="width: 90%" placeholder="请输入保存的名称"></a-input>
-    </div>
-  </a-modal>
-</template>
-
-<script lang="ts">
-  import { ref, watch } from 'vue';
-  import { BasicModal, useModal } from '/@/components/Modal';
-  import { useSuperQuery } from './useSuperQuery';
-  import OnlineSuperQueryValComponent from './SuperQueryValComponent.vue';
-  import { MinusCircleOutlined, PlusOutlined, FileTextOutlined, CloseCircleOutlined, AppstoreTwoTone } from '@ant-design/icons-vue';
-  import { Divider } from 'ant-design-vue';
-  import { useMessage } from '/@/hooks/web/useMessage';
-
-  export default {
-    name: 'OnlineSuperQuery',
-    props: {
-      config: {
-        type: Object,
-        default: [],
-      },
-      status: {
-        type: Boolean,
-        default: false,
-      },
-    },
-    components: {
-      BasicModal,
-      MinusCircleOutlined,
-      PlusOutlined,
-      OnlineSuperQueryValComponent,
-      FileTextOutlined,
-      CloseCircleOutlined,
-      AppstoreTwoTone,
-      Divider,
-    },
-    emits: ['search'],
-    setup(props, { emit }) {
-      const [registerFormModal, formModal] = useModal();
-      const { createMessage: $message } = useMessage();
-      /**
-       * 关闭按钮事件
-       */
-      function handleCancel() {
-        formModal.closeModal();
-      }
-
-      /**
-       * 确认按钮事件
-       */
-      function handleSubmit() {
-        //console.log('handleSubmit', dynamicRowValues.values)
-        let dataArray = getQueryInfo(true);
-        if (dataArray && dataArray.length > 0) {
-          let result = getSuperQueryParams(dataArray);
-          //console.log('查询数据1', dataArray)
-          //console.log('查询数据2', result)
-          emit('search', result);
-        } else {
-          $message.warning('空条件无法查询!');
-        }
-      }
-
-      function getSuperQueryParams(dataArray) {
-        let arr: any = [];
-        for (let item of dataArray) {
-          let field = item.field;
-          let val = item.val;
-          if (val instanceof Array) {
-            val = val.join(',');
-          }
-          arr.push({
-            ...item,
-            field,
-            val,
-          });
-        }
-        if (arr.length > 0) {
-          superQueryFlag.value = true;
-        } else {
-          superQueryFlag.value = false;
-        }
-        let result = {
-          superQueryMatchType: matchType.value,
-          superQueryParams: encodeURI(JSON.stringify(arr)),
-        };
-        return result;
-      }
-
-      /**
-       * 重置按钮事件
-       */
-      function handleReset() {
-        dynamicRowValues.values = [];
-        addOne(false);
-        let result = getSuperQueryParams([]);
-        emit('search', result);
-      }
-
-      const {
-        formRef,
-        init,
-        dynamicRowValues,
-        matchType,
-        registerModal,
-
-        handleSave,
-        doSaveQueryInfo,
-        saveInfo,
-        saveTreeData,
-        handleTreeSelect,
-        handleRemoveSaveInfo,
-        fieldTreeData,
-        addOne,
-        removeOne,
-        setFormModel,
-        getSchema,
-        loading,
-        getQueryInfo,
-        initDefaultValues,
-      } = useSuperQuery();
-
-      /*--------------------按钮区域-beign------------------*/
-      const superQueryFlag = ref(false);
-      watch(
-        () => props.status,
-        (val) => {
-          superQueryFlag.value = val;
-        },
-        { immediate: true }
-      );
-
-      function handleOpen() {
-        formModal.openModal();
-        addOne(true);
-      }
-      /*--------------------按钮区域-end------------------*/
-
-      function getPopupContainer() {
-        return document.getElementsByClassName('jee-super-query-form')[0];
-      }
-      function onFinish(a) {
-        console.log('onfinish', a);
-      }
-
-      function handleChangeField(item) {
-        item['val'] = '';
-      }
-
-      watch(
-        () => props.config,
-        (val) => {
-          if (val) {
-            console.log('123', val);
-            console.log('123', val);
-            console.log('123', val);
-            Object.keys(val).map((k) => {
-              console.log(k, val[k]);
-            });
-            console.log('123', val);
-            console.log('123', val);
-            console.log('123', val);
-            init(val);
-          }
-        },
-        { immediate: true }
-      );
-
-      return {
-        formRef,
-        registerFormModal,
-        init,
-        handleChangeField,
-        dynamicRowValues,
-        matchType,
-        registerModal,
-        handleSubmit,
-        handleCancel,
-        handleSave,
-        handleReset,
-        doSaveQueryInfo,
-        saveInfo,
-        saveTreeData,
-        handleTreeSelect,
-        handleRemoveSaveInfo,
-        fieldTreeData,
-        addOne,
-        removeOne,
-        setFormModel,
-        getSchema,
-        loading,
-        onFinish,
-        getPopupContainer,
-        superQueryFlag,
-        handleOpen,
-        initDefaultValues,
-      };
-    },
-  };
-</script>
-
-<style scoped lang="less">
-  .jee-super-query-form .ant-form-item {
-    margin-bottom: 9px;
-  }
-  .j-super-query-history-tree {
-    ::v-deep .ant-tree-switcher {
-      width: 0px;
-    }
-    ::v-deep .ant-tree-node-content-wrapper {
-      width: 100%;
-      &:hover {
-        background-color: #e6f7ff !important;
-        border-radius: 0;
-      }
-    }
-    ::v-deep .ant-tree-treenode-switcher-close {
-      .ant-tree-title {
-        display: inline-block;
-        width: calc(100% - 30px);
-        > div {
-          display: flex;
-          justify-content: space-between;
-          .icon-cancle {
-            display: none;
-            color: #666666;
-            &:hover {
-              color: black;
-            }
-          }
-        }
-      }
-      &:hover {
-        .icon-cancle {
-          display: inline-block !important;
-        }
-      }
-    }
-    ::v-deep .ant-card-body {
-      padding: 0;
-    }
-  }
-  .j-super-query-history-card {
-    ::v-deep .ant-card-body,
-    ::v-deep.ant-card-head-title {
-      padding: 0;
-    }
-  }
-
-  /*VUEN-1087 【移动端】高级查询显示不全 */
-  @media only screen and(max-width: 1050px) {
-    ::v-deep .jee-super-query-form {
-      .ant-space {
-        flex-direction: column;
-        gap: 0 !important;
-        margin-bottom: 16px !important;
-      }
-      .ant-space-item {
-        width: 100%;
-      }
-      .ant-form-item {
-        width: 100% !important;
-      }
-    }
-  }
-</style>

+ 0 - 98
src/components/jeecg/super/superquery/SuperQueryValComponent.vue

@@ -1,98 +0,0 @@
-<script lang="tsx">
-  import { computed, defineComponent, PropType, unref } from 'vue';
-  import { FormSchema } from '/@/components/Form';
-  import { upperFirst } from 'lodash-es';
-  import { componentMap } from '/@/components/Form/src/componentMap';
-  import { createPlaceholderMessage } from '/@/components/Form/src/helper';
-  import { isFunction } from '/@/utils/is';
-
-  export default defineComponent({
-    name: 'OnlineSuperQueryValComponent',
-    inheritAttrs: false,
-    props: {
-      schema: {
-        type: Object as PropType<FormSchema>,
-        default: () => ({}),
-      },
-      formModel: {
-        type: Object as PropType<Recordable>,
-        default: () => ({}),
-      },
-      setFormModel: {
-        type: Function as PropType<(key: string, value: any) => void>,
-        default: null,
-      },
-    },
-    setup(props) {
-      const getComponentsProps = computed(() => {
-        const { schema, formModel } = props;
-        let { componentProps = {} } = schema;
-        if (isFunction(componentProps)) {
-          componentProps = componentProps({ schema, formModel }) ?? {};
-        }
-        return componentProps as Recordable;
-      });
-
-      const getValues = computed(() => {
-        const { formModel, schema } = props;
-        let obj = {
-          field: schema.field,
-          model: formModel,
-          values: {
-            ...formModel,
-          } as Recordable,
-          schema: schema,
-        };
-        return obj;
-      });
-
-      function renderComponent() {
-        const { component, changeEvent = 'change', valueField } = props.schema;
-        const field = 'val';
-        const isCheck = component && ['Switch', 'Checkbox'].includes(component);
-        const eventKey = `on${upperFirst(changeEvent)}`;
-        const on = {
-          [eventKey]: (...args: Nullable<Recordable>[]) => {
-            const [e] = args;
-            if (propsData[eventKey]) {
-              propsData[eventKey](...args);
-            }
-            const target = e ? e.target : null;
-            const value = target ? (isCheck ? target.checked : target.value) : e;
-            props.setFormModel(field, value);
-          },
-        };
-        const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;
-
-        const propsData: Recordable = {
-          allowClear: true,
-          getPopupContainer: (trigger: Element) => trigger.parentNode,
-          ...unref(getComponentsProps),
-        };
-
-        const isCreatePlaceholder = !propsData.disabled;
-
-        // RangePicker place是一个数组
-        if (isCreatePlaceholder && component !== 'RangePicker' && component) {
-          //自动设置placeholder
-          propsData.placeholder = unref(getComponentsProps)?.placeholder || createPlaceholderMessage(component) + props.schema.label;
-        }
-        propsData.codeField = field;
-        propsData.formValues = unref(getValues);
-        const bindValue: Recordable = {
-          [valueField || (isCheck ? 'checked' : 'value')]: props.formModel[field],
-        };
-        const compAttr: Recordable = {
-          ...propsData,
-          ...on,
-          ...bindValue,
-          allowClear: true,
-        };
-        return <Comp {...compAttr} />;
-      }
-      return () => {
-        return <div style="width:100%">{renderComponent()}</div>;
-      };
-    },
-  });
-</script>

+ 0 - 524
src/components/jeecg/super/superquery/useSuperQuery.ts

@@ -1,524 +0,0 @@
-import { useModalInner } from '/@/components/Modal';
-import { randomString } from '/@/utils/common/compUtils';
-import { reactive, ref, toRaw, watch } from 'vue';
-import { useMessage } from '/@/hooks/web/useMessage';
-import { Modal } from 'ant-design-vue';
-import { createLocalStorage } from '/@/utils/cache';
-import { useRoute } from 'vue-router';
-
-/**
- * 表单类型转换成查询类型
- * 普通查询和高级查询组件区别 :高级查询不支持联动组件
- */
-const FORM_VIEW_TO_QUERY_VIEW = {
-  password: 'text',
-  file: 'text',
-  image: 'text',
-  textarea: 'text',
-  umeditor: 'text',
-  markdown: 'text',
-  checkbox: 'list_multi',
-  radio: 'list',
-};
-
-// 查询条件存储编码前缀
-const SAVE_CODE_PRE = 'JSuperQuerySaved_';
-
-/**
- * 查询项
- * */
-interface SuperQueryItem {
-  field: string | undefined;
-  rule: string | undefined;
-  val: string | number;
-  key: string;
-}
-/**
- * 查询项-第一个控件树model
- * */
-interface TreeModel {
-  title: string;
-  value: string;
-  isLeaf?: boolean;
-  disabled?: boolean;
-  children?: TreeModel[];
-  order?: number;
-}
-
-/**
- * 查询信息保存结构
- * */
-interface SaveModel {
-  title: string;
-  content: string;
-  type: string;
-}
-
-export function useSuperQuery() {
-  const { createMessage: $message } = useMessage();
-  /** 表单ref*/
-  const formRef = ref<any>();
-
-  /** 数据*/
-  const dynamicRowValues = reactive<{ values: SuperQueryItem[] }>({
-    values: [],
-  });
-  /** and/or */
-  const matchType = ref('and');
-
-  // 弹框显示
-  const [registerModal, { setModalProps }] = useModalInner(() => {
-    setModalProps({ confirmLoading: false });
-  });
-
-  // 高级查询类型不支持联动组件,需要额外设置联动组件的view为text
-  const view2QueryViewMap = Object.assign({}, { link_down: 'text' }, FORM_VIEW_TO_QUERY_VIEW);
-
-  /**
-   * 确认按钮事件
-   */
-  function handleSubmit() {
-    console.log('handleSubmit', dynamicRowValues.values);
-  }
-
-  /**
-   * 关闭按钮事件
-   */
-  function handleCancel() {
-    //closeModal();
-  }
-
-  /**
-   * val组件赋值
-   */
-  function setFormModel(key: string, value: any, item: any) {
-    console.log('setFormModel', key, value);
-    // formModel[key] = value;
-    item['val'] = value;
-  }
-
-  // 字段-Properties
-  const fieldProperties = ref<any>({});
-  // 字段-左侧查询项-树控件数据
-  const fieldTreeData = ref<any>([]);
-
-  /**
-   * 初始化数据-最开始的方法
-   * 1.获取 表名@字段名-->配置 这样的一个map
-   * 2.获取树形结构的数据 显示:文本; 存储:表名@字段名
-   * 当树改变时,及时获取配置更新表单
-   * @param json
-   */
-  function init(json) {
-    let { allFields, treeData } = getAllFields(json);
-    fieldProperties.value = allFields;
-    fieldTreeData.value = treeData;
-  }
-
-  /**
-   * 左侧查询项 添加一行
-   * @param index
-   */
-  function addOne(index) {
-    let item = {
-      field: undefined,
-      rule: 'eq',
-      val: '',
-      key: randomString(16),
-    };
-    if (index === false) {
-      // 重置后需要调用
-      dynamicRowValues.values = [];
-      dynamicRowValues.values.push(item);
-    } else if (index === true) {
-      // 打开弹框是需要调用
-      if (dynamicRowValues.values.length == 0) {
-        dynamicRowValues.values.push(item);
-      }
-    } else {
-      // 其余就是 正常的点击加号增加行
-      dynamicRowValues.values.splice(++index, 0, item);
-    }
-  }
-
-  /**
-   * 左侧查询项 删除一行
-   */
-  function removeOne(item: SuperQueryItem) {
-    let arr = toRaw(dynamicRowValues.values);
-    let index = -1;
-    for (let i = 0; i < arr.length; i++) {
-      if (item.key == arr[i].key) {
-        index = i;
-        break;
-      }
-    }
-    if (index != -1) {
-      dynamicRowValues.values.splice(index, 1);
-    }
-  }
-
-  // 默认的输入框
-  const defaultInput = {
-    field: 'val',
-    label: '测试',
-    component: 'Input',
-  };
-
-  /**
-   * 左侧查询项 val组件 schema获取, 替代左侧字段树的change事件
-   * @param item
-   * @param index
-   */
-  function getSchema(item, index) {
-    let map = fieldProperties.value;
-    let prop = map[item.field];
-    if (!prop) {
-      return defaultInput;
-    }
-    if (view2QueryViewMap[prop.view]) {
-      // 如果出现查询条件联动组件出来的场景,请跟踪此处
-      prop.view = view2QueryViewMap[prop.view];
-    }
-    let temp;
-    // temp.setFormRef(formRef)
-    temp.noChange();
-    // 查询条件中的 下拉框popContainer为parentNode
-    temp.asSearchForm();
-    temp.updateField(item.field + index);
-    const setFieldValue = (values) => {
-      item['val'] = values[item.field];
-    };
-    temp.setFunctionForFieldValue(setFieldValue);
-    let schema = temp.getFormItemSchema();
-    //schema['valueField'] = 'val'
-    return schema;
-  }
-
-  /*-----------------------右侧保存信息相关-begin---------------------------*/
-
-  /**
-   * 右侧树 的 数据
-   */
-  const saveTreeData = ref<any>('');
-  // 本地缓存
-  const $ls = createLocalStorage();
-  //需要保存的信息(一条)
-  const saveInfo = reactive({
-    visible: false,
-    title: '',
-    content: '',
-    saveCode: '',
-  });
-  //按钮loading
-  const loading = ref(false);
-
-  // 当前页面路由
-  const route = useRoute();
-  // 监听路由信息,路由发生改变,则重新获取保存的查询信息-->currentPageSavedArray
-  watch(
-    () => route.fullPath,
-    (val) => {
-      console.log('fullpath', val);
-      initSaveQueryInfoCode();
-    }
-  );
-
-  // 当前页面存储的 查询信息
-  const currentPageSavedArray = ref<SaveModel[]>([]);
-  // 监听当前页面是否有新的数据保存了,然后更新右侧数据->saveTreeData
-  watch(
-    () => currentPageSavedArray.value,
-    (val) => {
-      let temp: any[] = [];
-      if (val && val.length > 0) {
-        val.map((item) => {
-          let key = randomString(16);
-          temp.push({
-            title: item.title,
-            slots: { icon: 'custom' },
-            value: key,
-          });
-        });
-      }
-      saveTreeData.value = temp;
-    },
-    { immediate: true, deep: true }
-  );
-
-  // 重新获取保存的查询信息
-  function initSaveQueryInfoCode() {
-    let code = SAVE_CODE_PRE + route.fullPath;
-    saveInfo.saveCode = code;
-    let list = $ls.get(code);
-    if (list && list instanceof Array) {
-      currentPageSavedArray.value = list;
-    }
-  }
-
-  // 执行一次 获取保存的查询信息
-  initSaveQueryInfoCode();
-
-  /**
-   * 保存按钮事件
-   */
-  function handleSave() {
-    // 获取实际数据转成字符串
-    let fieldArray = getQueryInfo();
-    if (!fieldArray) {
-      $message.warning('空条件不能保存');
-      return;
-    }
-    let content = JSON.stringify(fieldArray);
-    openSaveInfoModal(content);
-  }
-
-  // 输入保存标题 弹框显示
-  function openSaveInfoModal(content) {
-    saveInfo.visible = true;
-    saveInfo.title = '';
-    saveInfo.content = content;
-  }
-
-  /**
-   * 确认保存查询信息
-   */
-  function doSaveQueryInfo() {
-    let { title, content, saveCode } = saveInfo;
-    let index = getTitleIndex(title);
-    if (index >= 0) {
-      // 已存在是否覆盖
-      Modal.confirm({
-        title: '提示',
-        content: `${title} 已存在,是否覆盖?`,
-        okText: '确认',
-        cancelText: '取消',
-        onOk: () => {
-          currentPageSavedArray.value.splice(index, 1, {
-            content,
-            title,
-            type: matchType.value,
-          });
-          $ls.set(saveCode, currentPageSavedArray.value);
-          saveInfo.visible = false;
-        },
-      });
-    } else {
-      currentPageSavedArray.value.push({
-        content,
-        title,
-        type: matchType.value,
-      });
-      $ls.set(saveCode, currentPageSavedArray.value);
-      saveInfo.visible = false;
-    }
-  }
-
-  // 根据填入的 title找本地存储的信息,如果有需要询问是否覆盖
-  function getTitleIndex(title) {
-    let savedArray = currentPageSavedArray.value;
-    let index = -1;
-    for (let i = 0; i < savedArray.length; i++) {
-      if (savedArray[i].title == title) {
-        index = i;
-        break;
-      }
-    }
-    return index;
-  }
-
-  /**
-   * 获取左侧所有查询条件,如果没有/或者条件无效则返回false
-   */
-  function getQueryInfo(isEmit = false) {
-    let arr = dynamicRowValues.values;
-    if (!arr || arr.length == 0) {
-      return false;
-    }
-    let fieldArray: any = [];
-    let fieldProps = fieldProperties.value;
-    for (let item of arr) {
-      if (item.field && (item.val || item.val === 0) && item.rule) {
-        let tempVal: any = toRaw(item.val);
-        if (tempVal instanceof Array) {
-          tempVal = tempVal.join(',');
-        }
-        let fieldName = getRealFieldName(item);
-        let obj = {
-          field: fieldName,
-          rule: item.rule,
-          val: tempVal,
-        };
-        if (isEmit === true) {
-          //如果当前数据用于emit事件,需要设置dbtype和type
-          let prop = fieldProps[item.field];
-          if (prop) {
-            obj['type'] = prop.view;
-            obj['dbType'] = prop.type;
-          }
-        }
-        fieldArray.push(obj);
-      }
-    }
-    if (fieldArray.length == 0) {
-      return false;
-    }
-    return fieldArray;
-  }
-
-  //update-begin-author:taoyan date:2022-5-31 for: VUEN-1148 主子联动下,高级查询查子表数据,无效
-  /**
-   * 高级查询参数 字段名
-   * 获取后台需要的 字段名格式:表名,字段名
-   * @param item
-   */
-  function getRealFieldName(item) {
-    let fieldName = item.field;
-    if (fieldName.indexOf('@') > 0) {
-      fieldName = fieldName.replace('@', ',');
-    }
-    return fieldName;
-  }
-  //update-end-author:taoyan date:2022-5-31 for: VUEN-1148 主子联动下,高级查询查子表数据,无效
-
-  /**
-   * 右侧数据 点击事件,重新将数据显示到左侧
-   * @param key
-   * @param node
-   */
-  function handleTreeSelect(key, { node }) {
-    console.log(key, node);
-    let title = node.dataRef.title;
-    let arr = currentPageSavedArray.value.filter((item) => item.title == title);
-    if (arr && arr.length > 0) {
-      // 拿到数据渲染
-      let { content, type } = arr[0];
-      let data = JSON.parse(content);
-      let rowsValues: SuperQueryItem[] = [];
-      for (let item of data) {
-        rowsValues.push(Object.assign({}, { key: randomString(16) }, item));
-      }
-      dynamicRowValues.values = rowsValues;
-      matchType.value = type;
-    }
-  }
-
-  /**
-   * 右侧数据 删除事件
-   */
-  function handleRemoveSaveInfo(title) {
-    console.log(title);
-    let index = getTitleIndex(title);
-    if (index >= 0) {
-      currentPageSavedArray.value.splice(index, 1);
-      $ls.set(saveInfo.saveCode, currentPageSavedArray.value);
-    }
-  }
-
-  /*-----------------------右侧保存信息相关-end---------------------------*/
-
-  // 获取所有字段配置信息
-  function getAllFields(properties) {
-    // 获取所有配置 查询字段 是否联合查询
-    // const {properties, table, title } = json;
-    let allFields = {};
-    let order = 1;
-    let treeData: TreeModel[] = [];
-    /*   let mainNode:TreeModel = {
-      title,
-      value: table,
-      disabled: true,
-      children: []
-    };*/
-    //treeData.push(mainNode)
-    Object.keys(properties).map((field) => {
-      let item = properties[field];
-      if (item.view == 'table') {
-        // 子表字段
-        // 联合查询开启才需要子表字段作为查询条件
-        let subProps = item['properties'] || item['fields'];
-        let subTableOrder = order * 100;
-        let subNode: TreeModel = {
-          title: item.title,
-          value: field,
-          disabled: true,
-          children: [],
-          order: subTableOrder,
-        };
-        Object.keys(subProps).map((subField) => {
-          let subItem = subProps[subField];
-          // 保证排序统一
-          subItem['order'] = subTableOrder + subItem['order'];
-          let subFieldKey = field + '@' + subField;
-          allFields[subFieldKey] = subItem;
-          subNode.children!.push({
-            title: subItem.title,
-            value: subFieldKey,
-            isLeaf: true,
-            order: subItem['order'],
-          });
-        });
-        orderField(subNode);
-        treeData.push(subNode);
-        order++;
-      } else {
-        // 主表字段
-        //let fieldKey = table+'@'+field
-        let fieldKey = field;
-        allFields[fieldKey] = item;
-        treeData.push({
-          title: item.title,
-          value: fieldKey,
-          isLeaf: true,
-          order: item.order,
-        });
-      }
-    });
-    orderField(treeData);
-    return { allFields, treeData };
-  }
-
-  //根据字段的order重新排序
-  function orderField(data) {
-    let arr = data.children || data;
-    arr.sort(function (a, b) {
-      return a.order - b.order;
-    });
-  }
-
-  function initDefaultValues(values) {
-    const { params, matchType } = values;
-    if (params) {
-      let rowsValues: SuperQueryItem[] = [];
-      for (let item of params) {
-        rowsValues.push(Object.assign({}, { key: randomString(16) }, item));
-      }
-      dynamicRowValues.values = rowsValues;
-      matchType.value = matchType;
-    }
-  }
-
-  return {
-    formRef,
-    init,
-    dynamicRowValues,
-    matchType,
-    registerModal,
-    handleSubmit,
-    handleCancel,
-    handleSave,
-    doSaveQueryInfo,
-    saveInfo,
-    saveTreeData,
-    handleRemoveSaveInfo,
-    handleTreeSelect,
-    fieldTreeData,
-    addOne,
-    removeOne,
-    setFormModel,
-    getSchema,
-    loading,
-    getQueryInfo,
-    initDefaultValues,
-  };
-}

+ 6 - 1
src/components/jeecg/thirdApp/JThirdAppButton.vue

@@ -105,7 +105,12 @@
                     if (res.success) {
                       nodes = [...successInfo, h('br'), `无失败信息!`];
                     } else {
-                      nodes = [`失败信息如下:`, renderTextarea(h, res.result.failInfo.map((v, i) => `${i + 1}. ${v}`).join('\n')), h('br'), ...successInfo];
+                      nodes = [
+                        `失败信息如下:`,
+                        renderTextarea(h, res.result.failInfo.map((v, i) => `${i + 1}. ${v}`).join('\n')),
+                        h('br'),
+                        ...successInfo,
+                      ];
                     }
                     return nodes;
                   },

+ 3 - 1
src/components/jeecg/thirdApp/JThirdAppDropdown.vue

@@ -8,7 +8,9 @@
       </a-menu>
     </template>
   </a-dropdown>
-  <a-button v-else-if="syncToApp" type="primary" preIcon="ant-design:sync-outlined" @click="handleMenuClick({ key: 'to-app' })">同步{{ name }}</a-button>
+  <a-button v-else-if="syncToApp" type="primary" preIcon="ant-design:sync-outlined" @click="handleMenuClick({ key: 'to-app' })"
+    >同步{{ name }}</a-button
+  >
   <a-button v-else type="primary" preIcon="ant-design:sync-outlined" @click="handleMenuClick({ key: 'to-local' })">同步{{ name }}到本地</a-button>
 </template>
 

+ 4 - 0
src/components/registerGlobComp.ts

@@ -9,6 +9,8 @@ import {
   Alert,
   Checkbox,
   DatePicker,
+  TimePicker,
+  Calendar,
   Radio,
   Switch,
   Card,
@@ -66,6 +68,8 @@ export function registerGlobComp(app: App) {
     .use(Breadcrumb)
     .use(Checkbox)
     .use(DatePicker)
+    .use(TimePicker)
+    .use(Calendar)
     .use(Radio)
     .use(Switch)
     .use(Card)

+ 15 - 0
src/hooks/web/useTabs.ts

@@ -89,6 +89,20 @@ export function useTabs(_router?: Router) {
     }
   }
 
+  /**
+   * 关闭相同的路由
+   * @param path
+   */
+  function closeSameRoute(path) {
+    if(path.indexOf('?')>0){
+      path = path.split('?')[0];
+    }
+    let tab = tabStore.getTabList.find((item) => item.path.indexOf(path)>=0)!;
+    if(tab){
+      tabStore.closeTab(tab, router);
+    }
+  }
+
   return {
     refreshPage: () => handleTabAction(TableActionEnum.REFRESH),
     closeAll: () => handleTabAction(TableActionEnum.CLOSE_ALL),
@@ -99,5 +113,6 @@ export function useTabs(_router?: Router) {
     close: (tab?: RouteLocationNormalized) => handleTabAction(TableActionEnum.CLOSE, tab),
     setTitle: (title: string, tab?: RouteLocationNormalized) => updateTabTitle(title, tab),
     updatePath: (fullPath: string, tab?: RouteLocationNormalized) => updateTabPath(fullPath, tab),
+    closeSameRoute
   };
 }

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

@@ -106,9 +106,11 @@
     if (unref(isMultiTenant) && unref(isMultiDepart)) {
       currTitle.value = '切换租户和部门';
     } else if (unref(isMultiTenant)) {
-      currTitle.value = unref(currentTenantName) && unref(currentTenantName).length > 0 ? `租户切换(当前租户 :${unref(currentTenantName)})` : props.title;
+      currTitle.value =
+        unref(currentTenantName) && unref(currentTenantName).length > 0 ? `租户切换(当前租户 :${unref(currentTenantName)})` : props.title;
     } else if (unref(isMultiDepart)) {
-      currTitle.value = unref(currentDepartName) && unref(currentDepartName).length > 0 ? `部门切换(当前部门 :${unref(currentDepartName)})` : props.title;
+      currTitle.value =
+        unref(currentDepartName) && unref(currentDepartName).length > 0 ? `部门切换(当前部门 :${unref(currentDepartName)})` : props.title;
     }
     //model显隐
     if (unref(isMultiTenant) || unref(isMultiDepart)) {

+ 16 - 2
src/layouts/default/header/index.vue

@@ -4,7 +4,11 @@
     <div :class="`${prefixCls}-left`">
       <!-- logo -->
       <AppLogo v-if="getShowHeaderLogo || getIsMobile" :class="`${prefixCls}-logo`" :theme="getHeaderTheme" :style="getLogoWidth" />
-      <LayoutTrigger v-if="(getShowContent && getShowHeaderTrigger && !getSplit && !getIsMixSidebar) || getIsMobile" :theme="getHeaderTheme" :sider="false" />
+      <LayoutTrigger
+        v-if="(getShowContent && getShowHeaderTrigger && !getSplit && !getIsMixSidebar) || getIsMobile"
+        :theme="getHeaderTheme"
+        :sider="false"
+      />
       <LayoutBreadcrumb v-if="getShowContent && getShowBread" :theme="getHeaderTheme" />
     </div>
     <!-- left end -->
@@ -95,7 +99,17 @@
       const { getShowTopMenu, getShowHeaderTrigger, getSplit, getIsMixMode, getMenuWidth, getIsMixSidebar } = useMenuSetting();
       const { getUseErrorHandle, getShowSettingButton, getSettingButtonPosition } = useRootSetting();
 
-      const { getHeaderTheme, getShowFullScreen, getShowNotice, getShowContent, getShowBread, getShowHeaderLogo, getShowHeader, getShowSearch, getUseLockPage } = useHeaderSetting();
+      const {
+        getHeaderTheme,
+        getShowFullScreen,
+        getShowNotice,
+        getShowContent,
+        getShowBread,
+        getShowHeaderLogo,
+        getShowHeader,
+        getShowSearch,
+        getUseLockPage,
+      } = useHeaderSetting();
 
       const { getShowLocalePicker } = useLocale();
 

+ 23 - 3
src/layouts/default/menu/index.vue

@@ -41,7 +41,17 @@
     setup(props) {
       const go = useGo();
 
-      const { getMenuMode, getMenuType, getMenuTheme, getCollapsed, getCollapsedShowTitle, getAccordion, getIsHorizontal, getIsSidebarType, getSplit } = useMenuSetting();
+      const {
+        getMenuMode,
+        getMenuType,
+        getMenuTheme,
+        getCollapsed,
+        getCollapsedShowTitle,
+        getAccordion,
+        getIsHorizontal,
+        getIsSidebarType,
+        getSplit,
+      } = useMenuSetting();
       const { getShowLogo } = useRootSetting();
 
       const { prefixCls } = useDesign('layout-menu');
@@ -57,7 +67,10 @@
       const getIsShowLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType));
 
       const getUseScroll = computed(() => {
-        return !unref(getIsHorizontal) && (unref(getIsSidebarType) || props.splitType === MenuSplitTyeEnum.LEFT || props.splitType === MenuSplitTyeEnum.NONE);
+        return (
+          !unref(getIsHorizontal) &&
+          (unref(getIsSidebarType) || props.splitType === MenuSplitTyeEnum.LEFT || props.splitType === MenuSplitTyeEnum.NONE)
+        );
       });
 
       const getWrapperStyle = computed((): CSSProperties => {
@@ -128,7 +141,14 @@
         return !props.isHorizontal ? (
           <SimpleMenu {...menuProps} isSplitMenu={unref(getSplit)} items={menus} />
         ) : (
-          <BasicMenu {...(menuProps as any)} isHorizontal={props.isHorizontal} type={unref(getMenuType)} showLogo={unref(getIsShowLogo)} mode={unref(getComputedMenuMode as any)} items={menus} />
+          <BasicMenu
+            {...(menuProps as any)}
+            isHorizontal={props.isHorizontal}
+            type={unref(getMenuType)}
+            showLogo={unref(getIsShowLogo)}
+            mode={unref(getComputedMenuMode as any)}
+            items={menus}
+          />
         );
       }
 

+ 48 - 8
src/layouts/default/setting/SettingDrawer.tsx

@@ -16,7 +16,16 @@ import { useI18n } from '/@/hooks/web/useI18n';
 
 import { baseHandler } from './handler';
 
-import { HandlerEnum, contentModeOptions, topMenuAlignOptions, getMenuTriggerOptions, routerTransitionOptions, menuTypeList, mixSidebarTriggerOptions, tabsThemeOptions } from './enum';
+import {
+  HandlerEnum,
+  contentModeOptions,
+  topMenuAlignOptions,
+  getMenuTriggerOptions,
+  routerTransitionOptions,
+  menuTypeList,
+  mixSidebarTriggerOptions,
+  tabsThemeOptions,
+} from './enum';
 
 import { HEADER_PRESET_BG_COLOR_LIST, SIDE_BAR_BG_COLOR_LIST, APP_PRESET_COLOR_LIST } from '/@/settings/designSetting';
 
@@ -25,8 +34,19 @@ const { t } = useI18n();
 export default defineComponent({
   name: 'SettingDrawer',
   setup(_, { attrs }) {
-    const { getContentMode, getShowFooter, getShowBreadCrumb, getShowBreadCrumbIcon, getShowLogo, getFullContent, getColorWeak, getGrayMode, getLockTime, getShowDarkModeToggle, getThemeColor } =
-      useRootSetting();
+    const {
+      getContentMode,
+      getShowFooter,
+      getShowBreadCrumb,
+      getShowBreadCrumbIcon,
+      getShowLogo,
+      getFullContent,
+      getColorWeak,
+      getGrayMode,
+      getLockTime,
+      getShowDarkModeToggle,
+      getThemeColor,
+    } = useRootSetting();
 
     const { getOpenPageLoading, getBasicTransition, getEnableTransition, getOpenNProgress } = useTransitionSetting();
 
@@ -103,7 +123,12 @@ export default defineComponent({
 
       return (
         <>
-          <SwitchItem title={t('layout.setting.splitMenu')} event={HandlerEnum.MENU_SPLIT} def={unref(getSplit)} disabled={!unref(getShowMenuRef) || unref(getMenuType) !== MenuTypeEnum.MIX} />
+          <SwitchItem
+            title={t('layout.setting.splitMenu')}
+            event={HandlerEnum.MENU_SPLIT}
+            def={unref(getSplit)}
+            disabled={!unref(getShowMenuRef) || unref(getMenuType) !== MenuTypeEnum.MIX}
+          />
           {/*<SwitchItem*/}
           {/*  title={t('layout.setting.mixSidebarFixed')}*/}
           {/*  event={HandlerEnum.MENU_FIXED_MIX_SIDEBAR}*/}
@@ -171,7 +196,12 @@ export default defineComponent({
             options={triggerOptions}
             disabled={!unref(getShowMenuRef) || unref(getIsMixSidebar)}
           />
-          <SelectItem title={t('layout.setting.contentMode')} event={HandlerEnum.CONTENT_MODE} def={unref(getContentMode)} options={contentModeOptions} />
+          <SelectItem
+            title={t('layout.setting.contentMode')}
+            event={HandlerEnum.CONTENT_MODE}
+            def={unref(getContentMode)}
+            options={contentModeOptions}
+          />
           <InputNumberItem
             title={t('layout.setting.autoScreenLock')}
             min={0}
@@ -198,7 +228,12 @@ export default defineComponent({
     function renderContent() {
       return (
         <>
-          <SwitchItem title={t('layout.setting.menuDrag')} event={HandlerEnum.MENU_HAS_DRAG} def={unref(getCanDrag)} disabled={!unref(getShowMenuRef)} />
+          <SwitchItem
+            title={t('layout.setting.menuDrag')}
+            event={HandlerEnum.MENU_HAS_DRAG}
+            def={unref(getCanDrag)}
+            disabled={!unref(getShowMenuRef)}
+          />
           <SwitchItem
             title={t('layout.setting.collapseMenuDisplayName')}
             event={HandlerEnum.MENU_COLLAPSED_SHOW_TITLE}
@@ -206,7 +241,12 @@ export default defineComponent({
             disabled={!unref(getShowMenuRef) || !unref(getCollapsed) || unref(getIsMixSidebar)}
           />
           <SwitchItem title={t('layout.setting.tabs')} event={HandlerEnum.TABS_SHOW} def={unref(getShowMultipleTab)} />
-          <SwitchItem title={t('layout.setting.breadcrumb')} event={HandlerEnum.SHOW_BREADCRUMB} def={unref(getShowBreadCrumb)} disabled={!unref(getShowHeader)} />
+          <SwitchItem
+            title={t('layout.setting.breadcrumb')}
+            event={HandlerEnum.SHOW_BREADCRUMB}
+            def={unref(getShowBreadCrumb)}
+            disabled={!unref(getShowHeader)}
+          />
 
           {/*<SwitchItem*/}
           {/*  title={t('layout.setting.breadcrumbIcon')}*/}
@@ -287,7 +327,7 @@ export default defineComponent({
     }
 
     return () => (
-      <BasicDrawer {...attrs} title={t('layout.setting.drawerTitle')} width={330} wrapClassName="setting-drawer">
+      <BasicDrawer {...attrs} title={t('layout.setting.drawerTitle')} width={330} class="setting-drawer">
         {unref(getShowDarkModeToggle) && <Divider>{() => t('layout.setting.darkMode')}</Divider>}
         {unref(getShowDarkModeToggle) && <AppDarkModeToggle class="mx-auto" />}
         <Divider>{() => t('layout.setting.navMode')}</Divider>

+ 7 - 1
src/layouts/default/setting/components/SwitchItem.vue

@@ -1,7 +1,13 @@
 <template>
   <div :class="prefixCls">
     <span> {{ title }}</span>
-    <Switch v-bind="getBindValue" @change="handleChange" :disabled="disabled" :checkedChildren="t('layout.setting.on')" :unCheckedChildren="t('layout.setting.off')" />
+    <Switch
+      v-bind="getBindValue"
+      @change="handleChange"
+      :disabled="disabled"
+      :checkedChildren="t('layout.setting.on')"
+      :unCheckedChildren="t('layout.setting.off')"
+    />
   </div>
 </template>
 <script lang="ts">

Деякі файли не було показано, через те що забагато файлів було змінено