Procházet zdrojové kódy

feat(Upload): file list add drag func (#3227). resolve #3179

bowen před 1 rokem
rodič
revize
beed7f2e11

+ 2 - 0
src/components/Upload/src/BasicUpload.vue

@@ -22,6 +22,8 @@
     <UploadModal
       v-bind="bindValue"
       :previewFileList="fileList"
+      :fileListOpenDrag="fileListOpenDrag"
+      :fileListDragOptions="fileListDragOptions"
       @register="registerUploadModal"
       @change="handleChange"
       @delete="handleDelete"

+ 36 - 4
src/components/Upload/src/FileList.vue

@@ -1,14 +1,17 @@
 <script lang="tsx">
-  import { defineComponent, CSSProperties, watch, nextTick } from 'vue';
   import { fileListProps } from './props';
-  import { isFunction } from '/@/utils/is';
+  import { isFunction, isDef } from '/@/utils/is';
+  import { useSortable } from '/@/hooks/web/useSortable';
   import { useModalContext } from '/@/components/Modal/src/hooks/useModalContext';
+  import { defineComponent, CSSProperties, watch, nextTick, ref, onMounted } from 'vue';
 
   export default defineComponent({
     name: 'FileList',
     props: fileListProps,
-    setup(props) {
+    setup(props, { emit }) {
       const modalFn = useModalContext();
+      const sortableContainer = ref<HTMLTableSectionElement>();
+
       watch(
         () => props.dataSource,
         () => {
@@ -17,6 +20,35 @@
           });
         },
       );
+
+      if (props.openDrag) {
+        onMounted(() =>
+          useSortable(sortableContainer, {
+            ...props.dragOptions,
+            onEnd: ({ oldIndex, newIndex }) => {
+              // position unchanged
+              if (oldIndex === newIndex) {
+                return;
+              }
+              const { onAfterEnd } = props.dragOptions;
+
+              if (isDef(oldIndex) && isDef(newIndex)) {
+                const data = [...props.dataSource];
+
+                const [oldItem] = data.splice(oldIndex, 1);
+                data.splice(newIndex, 0, oldItem);
+
+                nextTick(() => {
+                  emit('update:dataSource', data);
+
+                  isFunction(onAfterEnd) && onAfterEnd(data);
+                });
+              }
+            },
+          }).initSortable(),
+        );
+      }
+
       return () => {
         const { columns, actionColumn, dataSource } = props;
         const columnList = [...columns, actionColumn];
@@ -46,7 +78,7 @@
                   })}
                 </tr>
               </thead>
-              <tbody>
+              <tbody ref={sortableContainer}>
                 {dataSource.map((record = {}, index) => {
                   return (
                     <tr class="file-table-tr" key={`${index + record.name || ''}`}>

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

@@ -39,7 +39,13 @@
         </a-button>
       </Upload>
     </div>
-    <FileList :dataSource="fileListRef" :columns="columns" :actionColumn="actionColumn" />
+    <FileList
+      v-model:dataSource="fileListRef"
+      :columns="columns"
+      :actionColumn="actionColumn"
+      :openDrag="fileListOpenDrag"
+      :dragOptions="fileListDragOptions"
+    />
   </BasicModal>
 </template>
 <script lang="ts">

+ 29 - 0
src/components/Upload/src/props.ts

@@ -1,6 +1,18 @@
 import type { PropType } from 'vue';
 import { FileBasicColumn } from './typing';
 
+import type { Options } from 'sortablejs';
+
+import { Merge } from '/@/utils/types';
+
+type SortableOptions = Merge<
+  Omit<Options, 'onEnd'>,
+  {
+    onAfterEnd?: <T = any, R = any>(params: T) => R;
+    // ...可扩展
+  }
+>;
+
 export const basicProps = {
   helpText: {
     type: String as PropType<string>,
@@ -42,6 +54,15 @@ export const basicProps = {
     type: String as PropType<string>,
     default: null,
   },
+  fileListOpenDrag: {
+    type: Boolean,
+    default: true,
+  },
+
+  fileListDragOptions: {
+    type: Object as PropType<SortableOptions>,
+    default: () => ({}),
+  },
 };
 
 export const uploadContainerProps = {
@@ -80,4 +101,12 @@ export const fileListProps = {
     type: Array as PropType<any[]>,
     default: null,
   },
+  openDrag: {
+    type: Boolean,
+    default: false,
+  },
+  dragOptions: {
+    type: Object as PropType<SortableOptions>,
+    default: () => ({}),
+  },
 };

+ 5 - 3
src/hooks/web/useSortable.ts

@@ -2,14 +2,16 @@ import { nextTick, unref } from 'vue';
 import type { Ref } from 'vue';
 import type { Options } from 'sortablejs';
 
-export function useSortable(el: HTMLElement | Ref<HTMLElement>, options?: Options) {
+export function useSortable(el?: HTMLElement | Ref<HTMLElement | undefined>, options?: Options) {
   function initSortable() {
     nextTick(async () => {
+      el = unref(el);
+
       if (!el) return;
 
       const Sortable = (await import('sortablejs')).default;
-      Sortable.create(unref(el), {
-        animation: 500,
+      Sortable.create(el, {
+        animation: 100,
         delay: 400,
         delayOnTouchOnly: true,
         ...options,

+ 1 - 1
src/utils/types.ts

@@ -41,7 +41,7 @@ export type StyleValue = string | CSSProperties | Array<StyleValue>;
 
 export type Mutable<T> = { -readonly [P in keyof T]: T[P] };
 
-type Merge<O extends object, T extends object> = {
+export type Merge<O extends object, T extends object> = {
   [K in keyof O | keyof T]: K extends keyof T ? T[K] : K extends keyof O ? O[K] : never;
 };