Bladeren bron

Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin

# Conflicts:
#	src/components/SimpleMenu/src/SimpleSubMenu.vue
shizhongming 2 jaren geleden
bovenliggende
commit
fa8ada29bb
46 gewijzigde bestanden met toevoegingen van 236 en 174 verwijderingen
  1. 1 2
      .vscode/extensions.json
  2. 4 4
      apps/test-server/service/FileService.ts
  3. 0 12
      index.html
  4. 7 7
      src/components/Application/src/search/useMenuSearch.ts
  5. 8 8
      src/components/Container/src/collapse/CollapseContainer.vue
  6. 4 4
      src/components/Container/src/collapse/CollapseHeader.vue
  7. 14 14
      src/components/Description/src/Description.vue
  8. 1 1
      src/components/Form/src/BasicForm.vue
  9. 1 1
      src/components/Form/src/components/ApiCascader.vue
  10. 1 1
      src/components/Form/src/components/ApiRadioGroup.vue
  11. 3 3
      src/components/Form/src/components/ApiSelect.vue
  12. 1 1
      src/components/Form/src/components/ApiTransfer.vue
  13. 8 9
      src/components/Form/src/hooks/useFormEvents.ts
  14. 3 3
      src/components/Form/src/hooks/useFormValues.ts
  15. 1 1
      src/components/Menu/src/components/MenuItemContent.vue
  16. 29 23
      src/components/Scrollbar/src/bar.ts
  17. 0 1
      src/components/SimpleMenu/src/SimpleSubMenu.vue
  18. 0 1
      src/components/Table/src/components/HeaderCell.vue
  19. 1 1
      src/components/Table/src/components/TableImg.vue
  20. 21 19
      src/components/Table/src/components/editable/EditableCell.vue
  21. 6 6
      src/components/Table/src/components/settings/ColumnSetting.vue
  22. 1 1
      src/components/Table/src/hooks/useDataSource.ts
  23. 3 3
      src/components/Table/src/hooks/usePagination.ts
  24. 1 1
      src/components/Table/src/hooks/useTableExpand.ts
  25. 33 1
      src/components/VirtualScroll/src/VirtualScroll.vue
  26. 6 6
      src/components/VxeTable/src/components/common.tsx
  27. 5 0
      src/components/VxeTable/src/const.ts
  28. 13 0
      src/components/VxeTable/src/helper.ts
  29. 1 2
      src/design/ant/input.less
  30. 1 1
      src/enums/appEnum.ts
  31. 0 1
      src/hooks/web/usePermission.ts
  32. 2 2
      src/layouts/default/header/components/Breadcrumb.vue
  33. 1 1
      src/layouts/default/sider/MixSider.vue
  34. 2 2
      src/router/helper/menuHelper.ts
  35. 1 1
      src/utils/helper/tsxHelper.ts
  36. 1 1
      src/utils/index.ts
  37. 3 3
      src/views/demo/comp/qrcode/index.vue
  38. 25 3
      src/views/demo/comp/scroll/VirtualScroll.vue
  39. 6 6
      src/views/demo/page/account/center/index.vue
  40. 2 2
      src/views/demo/page/account/setting/AccountBind.vue
  41. 7 7
      src/views/demo/page/account/setting/BaseSetting.vue
  42. 2 2
      src/views/demo/page/account/setting/MsgNotify.vue
  43. 2 2
      src/views/demo/page/account/setting/SecureSetting.vue
  44. 2 2
      src/views/form-design/components/VFormDesign/components/ComponentProps.vue
  45. 1 1
      src/views/form-design/components/VFormDesign/components/FormProps.vue
  46. 1 1
      src/views/form-design/components/VFormDesign/config/componentPropsConfig.ts

+ 1 - 2
.vscode/extensions.json

@@ -9,6 +9,5 @@
     "antfu.iconify",
     "antfu.unocss",
     "mikestead.dotenv",
-    "vue.vscode-typescript-vue-plugin"
   ]
-}
+}

+ 4 - 4
apps/test-server/service/FileService.ts

@@ -10,8 +10,8 @@ export default class FileService {
     let fileReader, fileResource, writeStream;
 
     const fileFunc = function (file) {
-      fileReader = fs.createReadStream(file.path);
-      fileResource = filePath + `/${file.name}`;
+      fileReader = fs.createReadStream(file.filepath);
+      fileResource = filePath + `/${file.originalFilename}`;
       console.log(fileResource);
 
       writeStream = fs.createWriteStream(fileResource);
@@ -22,7 +22,7 @@ export default class FileService {
       if (flag) {
         let url = '';
         for (let i = 0; i < files.length; i++) {
-          url += uploadUrl + `/${files[i].name},`;
+          url += uploadUrl + `/${files[i].originalFilename},`;
         }
         url = url.replace(/,$/gi, '');
         ctx.body = {
@@ -32,7 +32,7 @@ export default class FileService {
         };
       } else {
         ctx.body = {
-          url: uploadUrl + `/${files.name}`,
+          url: uploadUrl + `/${files.originalFilename}`,
           code: 0,
           message: 'upload Success!',
         };

+ 0 - 12
index.html

@@ -125,18 +125,6 @@
           }
         }
 
-        @keyframes ant-rotate {
-          to {
-            transform: rotate(405deg);
-          }
-        }
-
-        @keyframes ant-spin-move {
-          to {
-            opacity: 1;
-          }
-        }
-
         @keyframes ant-spin-move {
           to {
             opacity: 1;

+ 7 - 7
src/components/Application/src/search/useMenuSearch.ts

@@ -1,13 +1,13 @@
-import { type Menu } from '@/router/types';
-import { type AnyFunction } from '@vben/types';
-import { ref, onBeforeMount, unref, Ref, nextTick } from 'vue';
+import { useI18n } from '@/hooks/web/useI18n';
+import { useGo } from '@/hooks/web/usePage';
 import { getMenus } from '@/router/menus';
-import { cloneDeep } from 'lodash-es';
+import { type Menu } from '@/router/types';
 import { filter, forEach } from '@/utils/helper/treeHelper';
-import { useGo } from '@/hooks/web/usePage';
 import { useScrollTo } from '@vben/hooks';
+import { type AnyFunction } from '@vben/types';
 import { onKeyStroke, useDebounceFn } from '@vueuse/core';
-import { useI18n } from '@/hooks/web/useI18n';
+import { cloneDeep } from 'lodash-es';
+import { Ref, nextTick, onBeforeMount, ref, unref } from 'vue';
 
 export interface SearchResult {
   name: string;
@@ -42,7 +42,7 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref, emit: A
     const list = await getMenus();
     menuList = cloneDeep(list);
     forEach(menuList, (item) => {
-      item.name = t(item.name);
+      item.name = t(item.meta?.title || item.name);
     });
   });
 

+ 8 - 8
src/components/Container/src/collapse/CollapseContainer.vue

@@ -1,12 +1,12 @@
 <script lang="tsx">
-  import { ref, unref, defineComponent, type PropType, type ExtractPropTypes } from 'vue';
-  import { isNil } from 'lodash-es';
-  import { Skeleton } from 'ant-design-vue';
-  import { useTimeoutFn } from '@vben/hooks';
   import { CollapseTransition } from '@/components/Transition';
-  import CollapseHeader from './CollapseHeader.vue';
-  import { triggerWindowResize } from '@/utils/event';
   import { useDesign } from '@/hooks/web/useDesign';
+  import { triggerWindowResize } from '@/utils/event';
+  import { useTimeoutFn } from '@vben/hooks';
+  import { Skeleton } from 'ant-design-vue';
+  import { isNil } from 'lodash-es';
+  import { defineComponent, ref, unref, type ExtractPropTypes, type PropType } from 'vue';
+  import CollapseHeader from './CollapseHeader.vue';
 
   const collapseContainerProps = {
     title: { type: String, default: '' },
@@ -14,7 +14,7 @@
     /**
      *  Can it be expanded
      */
-    canExpan: { type: Boolean, default: true },
+    canExpand: { type: Boolean, default: true },
     /**
      * Warm reminder on the right side of the title
      */
@@ -69,7 +69,7 @@
           />
 
           <div class="p-2">
-            <CollapseTransition enable={props.canExpan}>
+            <CollapseTransition enable={props.canExpand}>
               {props.loading ? (
                 <Skeleton active={props.loading} />
               ) : (

+ 4 - 4
src/components/Container/src/collapse/CollapseHeader.vue

@@ -1,13 +1,13 @@
 <script lang="tsx">
-  import { defineComponent, computed, unref, type ExtractPropTypes, PropType } from 'vue';
-  import { useDesign } from '@/hooks/web/useDesign';
   import { BasicArrow, BasicTitle } from '@/components/Basic';
+  import { useDesign } from '@/hooks/web/useDesign';
+  import { PropType, computed, defineComponent, unref, type ExtractPropTypes } from 'vue';
 
   const collapseHeaderProps = {
     prefixCls: String,
     title: String,
     show: Boolean,
-    canExpan: Boolean,
+    canExpand: Boolean,
     helpMessage: {
       type: [Array, String] as PropType<string[] | string>,
       default: '',
@@ -33,7 +33,7 @@
           <div class={`${unref(_prefixCls)}__action`}>
             {slots.action
               ? slots.action({ expand: props.show, onClick: () => emit('expand') })
-              : props.canExpan && (
+              : props.canExpand && (
                   <BasicArrow up expand={props.show} onClick={() => emit('expand')} />
                 )}
           </div>

+ 14 - 14
src/components/Description/src/Description.vue

@@ -1,23 +1,23 @@
 <script lang="tsx">
-  import type { DescriptionProps, DescInstance, DescItem } from './typing';
-  import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
   import type { CollapseContainerOptions } from '@/components/Container';
+  import { CollapseContainer } from '@/components/Container';
+  import { useDesign } from '@/hooks/web/useDesign';
+  import { getSlot } from '@/utils/helper/tsxHelper';
+  import { isFunction } from '@/utils/is';
+  import { useAttrs } from '@vben/hooks';
+  import { Descriptions } from 'ant-design-vue';
+  import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
+  import { get } from 'lodash-es';
   import {
-    type CSSProperties,
-    type PropType,
-    defineComponent,
     computed,
+    defineComponent,
     ref,
-    unref,
     toRefs,
+    unref,
+    type CSSProperties,
+    type PropType,
   } from 'vue';
-  import { get } from 'lodash-es';
-  import { Descriptions } from 'ant-design-vue';
-  import { CollapseContainer } from '@/components/Container';
-  import { useDesign } from '@/hooks/web/useDesign';
-  import { isFunction } from '@/utils/is';
-  import { getSlot } from '@/utils/helper/tsxHelper';
-  import { useAttrs } from '@vben/hooks';
+  import type { DescInstance, DescItem, DescriptionProps } from './typing';
 
   const props = {
     useCollapse: { type: Boolean, default: true },
@@ -175,7 +175,7 @@
         const { title } = unref(getMergeProps);
 
         return (
-          <CollapseContainer title={title} canExpan={canExpand} helpMessage={helpMessage}>
+          <CollapseContainer title={title} canExpand={canExpand} helpMessage={helpMessage}>
             {{
               default: () => content,
               action: () => getSlot(slots, 'action'),

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

@@ -285,7 +285,7 @@
     if (!autoSubmitOnEnter) return;
     if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) {
       const target: HTMLElement = e.target as HTMLElement;
-      if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') {
+      if (target && target.tagName && target.tagName.toUpperCase() === 'INPUT') {
         handleSubmit();
       }
     }

+ 1 - 1
src/components/Form/src/components/ApiCascader.vue

@@ -46,7 +46,7 @@
       type: Array,
     },
     api: {
-      type: Function as PropType<(arg?: any) => Promise<Option[]>>,
+      type: Function as PropType<(arg?: any) => Promise<Option[] | Recordable<any>>>,
       default: null,
     },
     numberToString: propTypes.bool,

+ 1 - 1
src/components/Form/src/components/ApiRadioGroup.vue

@@ -38,7 +38,7 @@
 
   const props = defineProps({
     api: {
-      type: Function as PropType<(arg?: any) => Promise<OptionsItem[]>>,
+      type: Function as PropType<(arg?: any) => Promise<OptionsItem[] | Recordable<any>>>,
       default: null,
     },
     params: {

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

@@ -39,7 +39,7 @@
     value: { type: [Array, Object, String, Number] as PropType<SelectValue> },
     numberToString: propTypes.bool,
     api: {
-      type: Function as PropType<(arg?: any) => Promise<OptionsItem[]>>,
+      type: Function as PropType<(arg?: any) => Promise<OptionsItem[] | Recordable<any>>>,
       default: null,
     },
     // api params
@@ -121,10 +121,10 @@
       emitChange();
     } catch (error) {
       console.warn(error);
-    } finally {
-      loading.value = false;
       // reset status
       isFirstLoaded.value = false;
+    } finally {
+      loading.value = false;
     }
   }
 

+ 1 - 1
src/components/Form/src/components/ApiTransfer.vue

@@ -25,7 +25,7 @@
   const props = defineProps({
     value: { type: Array as PropType<Array<string>> },
     api: {
-      type: Function as PropType<(arg) => Promise<TransferItem[]>>,
+      type: Function as PropType<(arg) => Promise<TransferItem[] | Recordable<any>>>,
       default: null,
     },
     params: { type: Object },

+ 8 - 9
src/components/Form/src/hooks/useFormEvents.ts

@@ -87,7 +87,7 @@ export function useFormEvents({
       const defaultValueObj = schema?.defaultValueObj;
       const fieldKeys = Object.keys(defaultValueObj || {});
       if (fieldKeys.length) {
-        fieldKeys.map((field) => {
+        fieldKeys.forEach((field) => {
           formModel[field] = defaultValueObj![field];
         });
       }
@@ -135,6 +135,9 @@ export function useFormEvents({
       }
 
       const constructValue = tryConstructArray(key, values) || tryConstructObject(key, values);
+      const setDateFieldValue = (v) => {
+        return v ? (_props?.valueFormat ? v : dateUtil(v)) : null;
+      };
 
       // 0| '' is allow
       if (hasKey || !!constructValue) {
@@ -144,15 +147,11 @@ export function useFormEvents({
           if (Array.isArray(fieldValue)) {
             const arr: any[] = [];
             for (const ele of fieldValue) {
-              arr.push(ele ? dateUtil(ele) : null);
+              arr.push(setDateFieldValue(ele));
             }
             unref(formModel)[key] = arr;
           } else {
-            unref(formModel)[key] = fieldValue
-              ? _props?.valueFormat
-                ? fieldValue
-                : dateUtil(fieldValue)
-              : null;
+            unref(formModel)[key] = setDateFieldValue(fieldValue);
           }
         } else {
           unref(formModel)[key] = fieldValue;
@@ -195,7 +194,7 @@ export function useFormEvents({
       fieldList = [fields];
     }
     for (const field of fieldList) {
-      _removeSchemaByFeild(field, schemaList);
+      _removeSchemaByField(field, schemaList);
     }
     schemaRef.value = schemaList;
   }
@@ -203,7 +202,7 @@ export function useFormEvents({
   /**
    * @description: Delete based on field name
    */
-  function _removeSchemaByFeild(field: string, schemaList: FormSchema[]): void {
+  function _removeSchemaByField(field: string, schemaList: FormSchema[]): void {
     if (isString(field)) {
       const index = schemaList.findIndex((schema) => schema.field === field);
       if (index !== -1) {

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

@@ -13,7 +13,7 @@ interface UseFormValuesContext {
 }
 
 /**
- * @desription deconstruct array-link key. This method will mutate the target.
+ * @description deconstruct array-link key. This method will mutate the target.
  */
 function tryDeconstructArray(key: string, value: any, target: Recordable) {
   const pattern = /^\[(.+)\]$/;
@@ -31,7 +31,7 @@ function tryDeconstructArray(key: string, value: any, target: Recordable) {
 }
 
 /**
- * @desription deconstruct object-link key. This method will mutate the target.
+ * @description deconstruct object-link key. This method will mutate the target.
  */
 function tryDeconstructObject(key: string, value: any, target: Recordable) {
   const pattern = /^\{(.+)\}$/;
@@ -138,7 +138,7 @@ export function useFormValues({
       const { defaultValue, defaultValueObj } = item;
       const fieldKeys = Object.keys(defaultValueObj || {});
       if (fieldKeys.length) {
-        fieldKeys.map((field) => {
+        fieldKeys.forEach((field) => {
           obj[field] = defaultValueObj![field];
           if (formModel[field] === undefined) {
             formModel[field] = defaultValueObj![field];

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

@@ -19,7 +19,7 @@
   const { t } = useI18n();
   const { prefixCls } = useDesign('basic-menu-item-content');
 
-  const getI18nName = computed(() => t(props.item?.name));
+  const getI18nName = computed(() => t(props.item?.meta?.title || props.item?.name));
   const getIcon = computed(() => (props.item?.img ? undefined : props.item?.icon));
   const getImg = computed(() => props.item?.img);
 </script>

+ 29 - 23
src/components/Scrollbar/src/bar.ts

@@ -28,6 +28,35 @@ export default defineComponent({
     });
     const barStore = ref<Recordable>({});
     const cursorDown = ref();
+
+    const mouseMoveDocumentHandler = (e: any) => {
+      if (cursorDown.value === false) {
+        return;
+      }
+      const prevPage = barStore.value[bar.value.axis];
+
+      if (!prevPage) {
+        return;
+      }
+
+      const offset =
+        (instance?.vnode.el?.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) *
+        -1;
+      const thumbClickPosition = thumb.value[bar.value.offset] - prevPage;
+      const thumbPositionPercentage =
+        ((offset - thumbClickPosition) * 100) / instance?.vnode.el?.[bar.value.offset];
+      wrap.value[bar.value.scroll] =
+        (thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100;
+    };
+
+    const startDrag = (e: any) => {
+      e.stopImmediatePropagation();
+      cursorDown.value = true;
+      on(document, 'mousemove', mouseMoveDocumentHandler);
+      on(document, 'mouseup', mouseUpDocumentHandler);
+      document.onselectstart = () => false;
+    };
+
     const clickThumbHandler = (e: any) => {
       // prevent click event of right button
       if (e.ctrlKey || e.button === 2) {
@@ -51,29 +80,6 @@ export default defineComponent({
       wrap.value[bar.value.scroll] =
         (thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100;
     };
-    const startDrag = (e: any) => {
-      e.stopImmediatePropagation();
-      cursorDown.value = true;
-      on(document, 'mousemove', mouseMoveDocumentHandler);
-      on(document, 'mouseup', mouseUpDocumentHandler);
-      document.onselectstart = () => false;
-    };
-
-    const mouseMoveDocumentHandler = (e: any) => {
-      if (cursorDown.value === false) return;
-      const prevPage = barStore.value[bar.value.axis];
-
-      if (!prevPage) return;
-
-      const offset =
-        (instance?.vnode.el?.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) *
-        -1;
-      const thumbClickPosition = thumb.value[bar.value.offset] - prevPage;
-      const thumbPositionPercentage =
-        ((offset - thumbClickPosition) * 100) / instance?.vnode.el?.[bar.value.offset];
-      wrap.value[bar.value.scroll] =
-        (thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100;
-    };
 
     function mouseUpDocumentHandler() {
       cursorDown.value = false;

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

@@ -78,7 +78,6 @@
   const getShowMenu = computed(() => !props.item?.meta?.hideMenu);
   const getIcon = computed(() => (props.item?.img ? undefined : props.item?.icon));
   const getImg = computed(() => props.item?.img);
-  // const getI18nName = computed(() => t(props.item?.name));
   const getI18nName = computed(() => {
     const locales = props.item.meta && props.item.meta.locales;
     const title = props.item.meta && props.item.meta.title;

+ 0 - 1
src/components/Table/src/components/HeaderCell.vue

@@ -55,7 +55,6 @@
   .@{prefix-cls} {
     &__help {
       margin-left: 8px;
-      color: rgb(0 0 0 / 65%) !important;
     }
   }
 </style>

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

@@ -5,7 +5,7 @@
     v-if="imgList && imgList.length"
     :style="getWrapStyle"
   >
-    <Badge :count="!showBadge || imgList.length == 1 ? 0 : imgList.length" v-if="simpleShow">
+    <Badge :count="!showBadge || imgList.length === 1 ? 0 : imgList.length" v-if="simpleShow">
       <div class="img-div">
         <Image.PreviewGroup>
           <template v-for="(img, index) in imgList" :key="img">

+ 21 - 19
src/components/Table/src/components/editable/EditableCell.vue

@@ -65,6 +65,19 @@
         return ['Checkbox', 'Switch'].includes(component);
       });
 
+      const getDisable = computed(() => {
+        const { editDynamicDisabled } = props.column;
+        let disabled = false;
+        if (isBoolean(editDynamicDisabled)) {
+          disabled = editDynamicDisabled;
+        }
+        if (isFunction(editDynamicDisabled)) {
+          const { record } = props;
+          disabled = editDynamicDisabled({ record, currentValue: currentValueRef.value });
+        }
+        return disabled;
+      });
+
       const getComponentProps = computed(() => {
         const isCheckValue = unref(getIsCheckComp);
         let compProps = props.column?.editComponentProps ?? ({} as any);
@@ -117,18 +130,7 @@
         const dataKey = (dataIndex || key) as string;
         set(record, dataKey, value);
       }
-      const getDisable = computed(() => {
-        const { editDynamicDisabled } = props.column;
-        let disabled = false;
-        if (isBoolean(editDynamicDisabled)) {
-          disabled = editDynamicDisabled;
-        }
-        if (isFunction(editDynamicDisabled)) {
-          const { record } = props;
-          disabled = editDynamicDisabled({ record, currentValue: currentValueRef.value });
-        }
-        return disabled;
-      });
+
       const getValues = computed(() => {
         const { editValueMap } = props.column;
 
@@ -149,6 +151,11 @@
         return option?.label ?? value;
       });
 
+      const getRowEditable = computed(() => {
+        const { editable } = props.record || {};
+        return !!editable;
+      });
+
       const getWrapperStyle = computed((): CSSProperties => {
         if (unref(getIsCheckComp) || unref(getRowEditable)) {
           return {};
@@ -163,11 +170,6 @@
         return `edit-cell-align-${align}`;
       });
 
-      const getRowEditable = computed(() => {
-        const { editable } = props.record || {};
-        return !!editable;
-      });
-
       watchEffect(() => {
         // defaultValueRef.value = props.value;
         currentValueRef.value = props.value;
@@ -191,7 +193,7 @@
         });
       }
 
-      async function handleChange(e: any) {
+      async function handleChange(e: any, ...rest: any[]) {
         const component = unref(getComponent);
         if (!e) {
           currentValueRef.value = e;
@@ -205,7 +207,7 @@
           currentValueRef.value = e;
         }
         const onChange = unref(getComponentProps)?.onChangeTemp;
-        if (onChange && isFunction(onChange)) onChange(...arguments);
+        if (onChange && isFunction(onChange)) onChange(e, ...rest);
 
         table.emit?.('edit-change', {
           column: props.column,

+ 6 - 6
src/components/Table/src/components/settings/ColumnSetting.vue

@@ -149,6 +149,12 @@
     return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer();
   };
 
+  // 默认值
+  let defaultIsIndexColumnShow: boolean = false;
+  let defaultIsRowSelectionShow: boolean = false;
+  let defaultRowSelection: TableRowSelection<Recordable<any>>;
+  let defaultColumnOptions: ColumnOptionsType[] = [];
+
   // 是否已经从缓存恢复
   let isRestored = false;
   let isInnerChange = false;
@@ -518,12 +524,6 @@
     tableColumnsUpdate();
   };
 
-  // 默认值
-  let defaultIsIndexColumnShow: boolean = false;
-  let defaultIsRowSelectionShow: boolean = false;
-  let defaultRowSelection: TableRowSelection<Recordable<any>>;
-  let defaultColumnOptions: ColumnOptionsType[] = [];
-
   const init = async () => {
     if (!isRestored) {
       // 获取数据列

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

@@ -211,7 +211,7 @@ export function useDataSource(
   }
 
   function findTableDataRecord(keyValue: Key) {
-    if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
+    if (!dataSourceRef.value || dataSourceRef.value.length === 0) return;
     const { childrenColumnName = 'children' } = unref(propsRef);
 
     const findRow = (array: any[]) => {

+ 3 - 3
src/components/Table/src/hooks/usePagination.tsx → src/components/Table/src/hooks/usePagination.ts

@@ -1,6 +1,6 @@
 import type { PaginationProps } from '../types/pagination';
 import type { BasicTableProps } from '../types/table';
-import { computed, unref, ref, ComputedRef, watch } from 'vue';
+import { computed, unref, ref, ComputedRef, watch, h } from 'vue';
 import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
 import { isBoolean } from '@/utils/is';
 import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const';
@@ -14,9 +14,9 @@ interface ItemRender {
 
 function itemRender({ page, type, originalElement }: ItemRender) {
   if (type === 'prev') {
-    return page === 0 ? null : <LeftOutlined />;
+    return page === 0 ? null : h(LeftOutlined);
   } else if (type === 'next') {
-    return page === 1 ? null : <RightOutlined />;
+    return page === 1 ? null : h(RightOutlined);
   }
   return originalElement;
 }

+ 1 - 1
src/components/Table/src/hooks/useTableExpand.ts

@@ -105,7 +105,7 @@ export function useTableExpand(
   }
 
   // 监听展开事件,用于支持手风琴展开效果
-  function handleTableExpand(expanded, record) {
+  function handleTableExpand(expanded: boolean, record: Recordable) {
     // 手风琴开关
     // isTreeTable 或 expandRowByClick 时支持
     // 展开操作

+ 33 - 1
src/components/VirtualScroll/src/VirtualScroll.vue

@@ -52,7 +52,7 @@
   export default defineComponent({
     name: 'VirtualScroll',
     props,
-    setup(props, { slots }) {
+    setup(props, { slots, expose }) {
       const wrapElRef = ref<HTMLDivElement | null>(null);
       const state = reactive({
         first: 0,
@@ -128,6 +128,31 @@
         state.last = getLast(state.first);
       }
 
+      function scrollToTop() {
+        const wrapEl = unref(wrapElRef);
+        if (!wrapEl) {
+          return;
+        }
+        wrapEl.scrollTop = 0;
+      }
+
+      function scrollToBottom() {
+        const wrapEl = unref(wrapElRef);
+        if (!wrapEl) {
+          return;
+        }
+        wrapEl.scrollTop = wrapEl.scrollHeight;
+      }
+
+      function scrollToItem(index: number) {
+        const wrapEl = unref(wrapElRef);
+        if (!wrapEl) {
+          return;
+        }
+        const i = index - 1 > 0 ? index - 1 : 0;
+        wrapEl.scrollTop = i * unref(getItemHeightRef);
+      }
+
       function renderChildren() {
         const { items = [] } = props;
         return items.slice(unref(getFirstToRenderRef), unref(getLastToRenderRef)).map(genChild);
@@ -143,6 +168,13 @@
         );
       }
 
+      expose({
+        wrapElRef,
+        scrollToTop,
+        scrollToItem,
+        scrollToBottom,
+      });
+
       onMounted(() => {
         state.last = getLast(0);
         nextTick(() => {

+ 6 - 6
src/components/VxeTable/src/components/common.tsx

@@ -7,7 +7,7 @@ import {
 import XEUtils from 'xe-utils';
 import { componentMap } from '../componentMap';
 import { ComponentType } from '../componentType';
-import { createPlaceholderMessage } from '../helper';
+import { createPlaceholderMessage, sanitizeInputWhitespace } from '../helper';
 
 /**
  * @description: 获取组件
@@ -102,13 +102,13 @@ export function createEvents(
     };
   });
   if (inputFunc) {
-    ons[getOnName(modelEvent)] = function (targetEvnt: any) {
-      inputFunc(targetEvnt);
+    ons[getOnName(modelEvent)] = function (targetEvent: any) {
+      inputFunc(targetEvent);
       if (events && events[modelEvent]) {
-        events[modelEvent](params, targetEvnt);
+        events[modelEvent](params, targetEvent);
       }
       if (isSameEvent && changeFunc) {
-        changeFunc(targetEvnt);
+        changeFunc(targetEvent);
       }
     };
   }
@@ -323,7 +323,7 @@ export function createFormItemRender(
           params,
           (value: any) => {
             // 处理 model 值双向绑定
-            XEUtils.set(data, property, value);
+            XEUtils.set(data, property, sanitizeInputWhitespace(name as ComponentType, value));
           },
           () => {
             // 处理 change 事件相关逻辑

+ 5 - 0
src/components/VxeTable/src/const.ts

@@ -2,3 +2,8 @@
  * @description: 传给vxe-table 时需要忽略的prop
  */
 export const ignorePropKeys = ['tableClass', 'tableStyle'];
+
+/**
+ * @description: 需要忽略内容首尾空格的输入组件列表
+ */
+export const ignoreTrimInputComponents = ['AInput', 'ATextarea'];

+ 13 - 0
src/components/VxeTable/src/helper.ts

@@ -1,5 +1,7 @@
 import { ComponentType } from './componentType';
 import { useI18n } from '@/hooks/web/useI18n';
+import XEUtils from 'xe-utils';
+import { ignoreTrimInputComponents } from './const';
 
 const { t } = useI18n();
 
@@ -17,3 +19,14 @@ export function createPlaceholderMessage(component: ComponentType) {
     return t('common.chooseText');
   }
 }
+
+/**
+ *
+ * @description: 对输入值进行首尾空格的清理
+ */
+export function sanitizeInputWhitespace(component: ComponentType, value: string) {
+  if (ignoreTrimInputComponents.includes(component)) {
+    return XEUtils.trim(value);
+  }
+  return value;
+}

+ 1 - 2
src/design/ant/input.less

@@ -4,8 +4,7 @@
 .ant-input {
   &-number,
   &-number-group-wrapper {
-    width: 100% !important;
-    min-width: 110px;
+    width: 100%;
     max-width: 100%;
   }
 }

+ 1 - 1
src/enums/appEnum.ts

@@ -32,7 +32,7 @@ export enum PermissionModeEnum {
   // role
   // 角色权限
   ROLE = 'ROLE',
-  // black
+  // back
   // 后端
   BACK = 'BACK',
   // route mapping

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

@@ -41,7 +41,6 @@ export function usePermission() {
   /**
    * Reset and regain authority resource information
    * 重置和重新获得权限资源信息
-   * @param id
    */
   async function resume() {
     const tabStore = useMultipleTabStore();

+ 2 - 2
src/layouts/default/header/components/Breadcrumb.vue

@@ -4,10 +4,10 @@
       <template #itemRender="{ route, routes: routesMatched, paths }">
         <Icon :icon="getIcon(route)" v-if="getShowBreadCrumbIcon && getIcon(route)" />
         <span v-if="!hasRedirect(routesMatched, route)">
-          {{ t(route.name || route.meta.title) }}
+          {{ t(route.meta.title || route.name) }}
         </span>
         <router-link v-else to="" @click="handleClick(route, paths, $event as Event)">
-          {{ t(route.name || route.meta.title) }}
+          {{ t(route.meta.title || route.name) }}
         </router-link>
       </template>
     </a-breadcrumb>

+ 1 - 1
src/layouts/default/sider/MixSider.vue

@@ -43,7 +43,7 @@
             :icon="item.icon || (item.meta && item.meta.icon)"
           />
           <p :class="`${prefixCls}-module__name`">
-            {{ t(item.name) }}
+            {{ t(item?.meta?.title || item.name) }}
           </p>
         </li>
       </ul>

+ 2 - 2
src/router/helper/menuHelper.ts

@@ -61,12 +61,12 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
   // 提取树指定结构
   const list = treeMap(routeList, {
     conversion: (node: AppRouteRecordRaw) => {
-      const { meta: { title, hideMenu = false } = {} } = node;
+      const { meta: { hideMenu = false } = {}, name } = node;
 
       return {
         ...(node.meta || {}),
         meta: node.meta,
-        name: title,
+        name,
         hideMenu,
         path: node.path,
         ...(node.redirect ? { redirect: node.redirect } : {}),

+ 1 - 1
src/utils/helper/tsxHelper.tsx → src/utils/helper/tsxHelper.ts

@@ -27,7 +27,7 @@ export function getSlot(slots: Slots, slot = 'default', data?: any, opts?: Rende
 export function extendSlots(slots: Slots, excludeKeys: string[] = []) {
   const slotKeys = Object.keys(slots);
   const ret: any = {};
-  slotKeys.map((key) => {
+  slotKeys.forEach((key) => {
     if (excludeKeys.includes(key)) {
       return null;
     }

+ 1 - 1
src/utils/index.ts

@@ -97,7 +97,7 @@ export function openWindow(
 export function getDynamicProps<T extends Record<string, unknown>, U>(props: T): Partial<U> {
   const ret: Recordable = {};
 
-  Object.keys(props).map((key) => {
+  Object.keys(props).forEach((key) => {
     ret[key] = unref((props as Recordable)[key]);
   });
 

+ 3 - 3
src/views/demo/comp/qrcode/index.vue

@@ -1,7 +1,7 @@
 <template>
   <PageWrapper title="二维码组件使用示例">
     <div class="flex flex-wrap">
-      <CollapseContainer title="基础示例" :canExpan="true" class="text-center mb-6 w-1/5 mr-6">
+      <CollapseContainer title="基础示例" :canExpand="true" class="text-center mb-6 w-1/5 mr-6">
         <QrCode :value="qrCodeUrl" />
       </CollapseContainer>
 
@@ -79,13 +79,13 @@
   </PageWrapper>
 </template>
 <script lang="ts" setup>
-  import { ref, unref } from 'vue';
-  import { QrCode, QrCodeActionType } from '@/components/Qrcode';
   import LogoImg from '@/assets/images/logo.png';
   import { CollapseContainer } from '@/components/Container';
   import { PageWrapper } from '@/components/Page';
+  import { QrCode, QrCodeActionType } from '@/components/Qrcode';
   import { type Nullable } from '@vben/types';
   import { QRCode } from 'ant-design-vue';
+  import { ref, unref } from 'vue';
 
   const qrCodeUrl = 'https://www.vvbin.cn';
   const qrRef = ref<Nullable<QrCodeActionType>>(null);

+ 25 - 3
src/views/demo/comp/scroll/VirtualScroll.vue

@@ -1,8 +1,26 @@
 <template>
   <PageWrapper class="virtual-scroll-demo">
     <Divider>基础滚动示例</Divider>
+    <div class="text-center mb-4">
+      <a-button @click="vScrollRef?.scrollToTop()">滚动到顶部</a-button>
+      <a-button @click="vScrollRef?.scrollToBottom()" class="mx-2">滚动到底部</a-button>
+      <a-button @click="vScrollRef?.scrollToItem(scrollToItemIndex)"
+        >滚动到第
+        <input-number
+          v-model:value="scrollToItemIndex"
+          class="!w-60px mx-1"
+          :min="1"
+          :max="data.length"
+          :precision="0"
+          size="small"
+          :controls="false"
+          @keydown.enter="vScrollRef?.scrollToItem(scrollToItemIndex)"
+        />
+        条
+      </a-button>
+    </div>
     <div class="virtual-scroll-demo-wrap">
-      <VScroll :itemHeight="41" :items="data" :height="300" :width="300">
+      <VScroll :itemHeight="41" :items="data" :height="300" :width="300" ref="vScrollRef">
         <template #default="{ item }">
           <div class="virtual-scroll-demo__item">
             {{ item.title }}
@@ -24,9 +42,13 @@
   </PageWrapper>
 </template>
 <script lang="ts" setup>
-  import { VScroll } from '@/components/VirtualScroll';
-  import { Divider } from 'ant-design-vue';
   import { PageWrapper } from '@/components/Page';
+  import { VScroll } from '@/components/VirtualScroll';
+  import { Divider, InputNumber } from 'ant-design-vue';
+  import { ref } from 'vue';
+
+  const vScrollRef = ref<typeof VScroll>();
+  const scrollToItemIndex = ref(1000);
 
   const data = (() => {
     const arr: any[] = [];

+ 6 - 6
src/views/demo/page/account/center/index.vue

@@ -23,7 +23,7 @@
         </Row>
       </Col>
       <Col :span="7" :class="`${prefixCls}-col`">
-        <CollapseContainer title="标签" :canExpan="false">
+        <CollapseContainer title="标签" :canExpand="false">
           <template v-for="tag in tags" :key="tag">
             <Tag class="mb-2">
               {{ tag }}
@@ -32,7 +32,7 @@
         </CollapseContainer>
       </Col>
       <Col :span="8" :class="`${prefixCls}-col`">
-        <CollapseContainer :class="`${prefixCls}-top__team`" title="团队" :canExpan="false">
+        <CollapseContainer :class="`${prefixCls}-top__team`" title="团队" :canExpand="false">
           <div v-for="(team, index) in teams" :key="index" :class="`${prefixCls}-top__team-item`">
             <Icon :icon="team.icon" :color="team.color" />
             <span>{{ team.title }}</span>
@@ -53,17 +53,17 @@
 </template>
 
 <script lang="ts" setup>
-  import { Tag, Tabs, Row, Col } from 'ant-design-vue';
-  import { computed } from 'vue';
   import { CollapseContainer } from '@/components/Container';
   import Icon from '@/components/Icon/Icon.vue';
-  import Article from './Article.vue';
+  import { Col, Row, Tabs, Tag } from 'ant-design-vue';
+  import { computed } from 'vue';
   import Application from './Application.vue';
+  import Article from './Article.vue';
   import Project from './Project.vue';
 
   import headerImg from '@/assets/images/header.jpg';
-  import { tags, teams, details, achieveList } from './data';
   import { useUserStore } from '@/store/modules/user';
+  import { achieveList, details, tags, teams } from './data';
 
   const userStore = useUserStore();
   const TabPane = Tabs.TabPane;

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

@@ -1,5 +1,5 @@
 <template>
-  <CollapseContainer title="账号绑定" :canExpan="false">
+  <CollapseContainer title="账号绑定" :canExpand="false">
     <List>
       <template v-for="item in accountBindList" :key="item.key">
         <ListItem>
@@ -23,9 +23,9 @@
   </CollapseContainer>
 </template>
 <script lang="ts" setup>
-  import { List } from 'ant-design-vue';
   import { CollapseContainer } from '@/components/Container';
   import Icon from '@/components/Icon/Icon.vue';
+  import { List } from 'ant-design-vue';
 
   import { accountBindList } from './data';
 

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

@@ -1,5 +1,5 @@
 <template>
-  <CollapseContainer title="基本设置" :canExpan="false">
+  <CollapseContainer title="基本设置" :canExpand="false">
     <Row :gutter="24">
       <Col :span="14">
         <BasicForm @register="register" />
@@ -22,19 +22,19 @@
   </CollapseContainer>
 </template>
 <script lang="ts" setup>
-  import { Row, Col } from 'ant-design-vue';
-  import { computed, onMounted } from 'vue';
-  import { BasicForm, useForm } from '@/components/Form';
   import { CollapseContainer } from '@/components/Container';
   import { CropperAvatar } from '@/components/Cropper';
+  import { BasicForm, useForm } from '@/components/Form';
+  import { Col, Row } from 'ant-design-vue';
+  import { computed, onMounted } from 'vue';
 
   import { useMessage } from '@/hooks/web/useMessage';
 
-  import headerImg from '@/assets/images/header.jpg';
   import { accountInfoApi } from '@/api/demo/account';
-  import { baseSetschemas } from './data';
-  import { useUserStore } from '@/store/modules/user';
   import { uploadApi } from '@/api/sys/upload';
+  import headerImg from '@/assets/images/header.jpg';
+  import { useUserStore } from '@/store/modules/user';
+  import { baseSetschemas } from './data';
 
   const { createMessage } = useMessage();
   const userStore = useUserStore();

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

@@ -1,5 +1,5 @@
 <template>
-  <CollapseContainer title="新消息通知" :canExpan="false">
+  <CollapseContainer title="新消息通知" :canExpand="false">
     <List>
       <template v-for="item in msgNotifyList" :key="item.key">
         <ListItem>
@@ -23,8 +23,8 @@
   </CollapseContainer>
 </template>
 <script lang="ts" setup>
-  import { List, Switch } from 'ant-design-vue';
   import { CollapseContainer } from '@/components/Container';
+  import { List, Switch } from 'ant-design-vue';
   import { msgNotifyList } from './data';
 
   const ListItem = List.Item;

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

@@ -1,5 +1,5 @@
 <template>
-  <CollapseContainer title="安全设置" :canExpan="false">
+  <CollapseContainer title="安全设置" :canExpand="false">
     <List>
       <template v-for="item in secureSettingList" :key="item.key">
         <ListItem>
@@ -23,8 +23,8 @@
   </CollapseContainer>
 </template>
 <script lang="ts" setup>
-  import { List } from 'ant-design-vue';
   import { CollapseContainer } from '@/components/Container';
+  import { List } from 'ant-design-vue';
   import { secureSettingList } from './data';
 
   const ListItem = List.Item;

+ 2 - 2
src/views/form-design/components/VFormDesign/components/ComponentProps.vue

@@ -199,14 +199,14 @@
       // 控制性的选项
       const controlOptions = computed(() => {
         return allOptions.value.filter((item) => {
-          return item.category == 'control';
+          return item.category === 'control';
         });
       });
 
       // 非控制性选择
       const inputOptions = computed(() => {
         return allOptions.value.filter((item) => {
-          return item.category == 'input';
+          return item.category === 'input';
         });
       });
 

+ 1 - 1
src/views/form-design/components/VFormDesign/components/FormProps.vue

@@ -65,7 +65,7 @@
       </div>
       <FormItem label="表单属性">
         <Col
-          ><Checkbox v-model:checked="formConfig.colon" v-if="formConfig.layout == 'horizontal'"
+          ><Checkbox v-model:checked="formConfig.colon" v-if="formConfig.layout === 'horizontal'"
             >label后面显示冒号</Checkbox
           ></Col
         >

+ 1 - 1
src/views/form-design/components/VFormDesign/config/componentPropsConfig.ts

@@ -1099,7 +1099,7 @@ const componentAttrs: IBaseComponentProps = {
 
 function deleteProps(list: Omit<IBaseFormAttrs, 'tag'>[], key: string) {
   list.forEach((element, index) => {
-    if (element.name == key) {
+    if (element.name === key) {
       list.splice(index, 1);
     }
   });