Browse Source

feat(upload->previewColumns): Adapt functions && chore upload demo (#3799)

* feat(upload->previewColumns): add function capability with delete fileList

* chore(upload->demo): split file && add previewColumns fn demo

* type(upload): optional  handleRemove

* type(upload): remove  optional  handleRemove

* feat(upload->previewCol): add enhanceing  handleAdd && handleDownload

* chore(upload->preview): remove  previewColumns
Electrolux 1 năm trước cách đây
mục cha
commit
29ef0d3915

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

@@ -36,6 +36,7 @@
 
     <UploadPreviewModal
       :value="fileList"
+      :max-number="bindValue.maxNumber"
       @register="registerPreviewModal"
       @list-change="handlePreviewChange"
       @delete="handlePreviewDelete"

+ 22 - 6
src/components/Upload/src/components/UploadPreviewModal.vue

@@ -21,6 +21,9 @@
   import { useI18n } from '@/hooks/web/useI18n';
   import { isArray } from '@/utils/is';
   import { BasicColumn } from '@/components/Table';
+  import { isFunction } from '@/utils/is';
+  import { useMessage } from '@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
 
   const props = defineProps(previewProps);
 
@@ -36,9 +39,11 @@
   watch(
     () => props.previewColumns,
     () => {
-      if (props.previewColumns.length) {
+      if (Array.isArray(props.previewColumns) && props.previewColumns.length) {
         columns = props.previewColumns;
         actionColumn = null;
+      } else if (isFunction(props.previewColumns)) {
+        columns = props.previewColumns({ handleRemove, handleAdd });
       } else {
         columns = createPreviewColumns();
         actionColumn = createPreviewActionColumn({ handleRemove, handleDownload });
@@ -74,18 +79,29 @@
   );
 
   // 删除
-  function handleRemove(record: PreviewFileItem) {
-    const index = fileListRef.value.findIndex((item) => item.url === record.url);
+  function handleRemove(record: PreviewFileItem | Record<string, any>, urlKey = 'url') {
+    const index = fileListRef.value.findIndex((item) => item[urlKey] === record[urlKey]);
     if (index !== -1) {
       const removed = fileListRef.value.splice(index, 1);
-      emit('delete', removed[0].url);
+      emit('delete', removed[0][urlKey]);
       emit(
         'list-change',
-        fileListRef.value.map((item) => item.url),
+        fileListRef.value.map((item) => item[urlKey]),
       );
     }
   }
-
+  // 添加
+  function handleAdd(record: PreviewFileItem | Record<string, any>, urlKey = 'url') {
+    const { maxNumber } = props;
+    if (fileListRef.value.length + fileListRef.value.length > maxNumber) {
+      return createMessage.warning(t('component.upload.maxNumber', [maxNumber]));
+    }
+    fileListRef.value = [...fileListRef.value, record];
+    emit(
+      'list-change',
+      fileListRef.value.map((item) => item[urlKey]),
+    );
+  }
   // 下载
   function handleDownload(record: PreviewFileItem) {
     const { url = '' } = record;

+ 9 - 3
src/components/Upload/src/props.ts

@@ -14,11 +14,13 @@ type SortableOptions = Merge<
     // ...可扩展
   }
 >;
-
+type previewColumnsFnType = {
+  handleRemove:(record:Record<string,any>,key:string)=>any,
+  handleAdd:(record:Record<string,any>,key:string)=>any,
+}
 export const previewType = {
   previewColumns: {
-    type: Array as PropType<BasicColumn[] | FileBasicColumn[]>,
-    default: [],
+    type: [Array,Function] as PropType<BasicColumn[] | ((arg:previewColumnsFnType) => BasicColumn[])>,
     required: false,
   },
   beforePreviewData: {
@@ -111,6 +113,10 @@ export const previewProps = {
     type: Array as PropType<string[]>,
     default: () => [],
   },
+  maxNumber: {
+    type: Number as PropType<number>,
+    default: 1,
+  },
   ...previewType,
 };
 

+ 28 - 0
src/views/demo/comp/upload/Upload1.vue

@@ -0,0 +1,28 @@
+<template>
+  
+    <Alert message="基础示例" />
+    <BasicUpload
+      :maxSize="20"
+      :maxNumber="10"
+      @change="handleChange"
+      :api="uploadApi"
+      class="my-5"
+      :accept="['image/*']"
+    />
+
+</template>
+
+<script setup lang="ts">
+  import { BasicUpload } from '@/components/Upload';
+  import { uploadApi } from '@/api/sys/upload';
+  import { useMessage } from '@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+  import { Alert } from 'ant-design-vue';
+  function handleChange(list: string[]) {
+    createMessage.success(`已上传文件${JSON.stringify(list)}`);
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 56 - 0
src/views/demo/comp/upload/Upload2.vue

@@ -0,0 +1,56 @@
+<template>
+  <Alert message="嵌入表单,加入表单校验" />
+  <BasicForm @register="registerValiate" class="my-5" />
+</template>
+
+<script setup lang="ts">
+  import { uploadApi } from '@/api/sys/upload';
+  import { useMessage } from '@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+  import { BasicForm, FormSchema, useForm } from '@/components/Form';
+  import { Alert } from 'ant-design-vue';
+  const schemasValiate: FormSchema[] = [
+    {
+      field: 'field1',
+      component: 'Upload',
+      label: '字段1',
+      rules: [{ required: true, message: '请选择上传文件' }],
+      componentProps: {
+        api: uploadApi,
+      },
+    },
+    {
+      field: 'field2',
+      component: 'ImageUpload',
+      label: '字段2(ImageUpload)',
+      colProps: {
+        span: 8,
+      },
+      componentProps: {
+        api: uploadApi,
+      },
+    },
+  ];
+  const [registerValiate, { getFieldsValue: getFieldsValueValiate, validate }] = useForm({
+    labelWidth: 160,
+    schemas: schemasValiate,
+    actionColOptions: {
+      span: 18,
+    },
+    submitFunc: () => {
+      return new Promise((resolve) => {
+        validate()
+          .then(() => {
+            resolve();
+            console.log(getFieldsValueValiate());
+            createMessage.success(`请到控制台查看结果`);
+          })
+          .catch(() => {
+            createMessage.error(`请输入必填项`);
+          });
+      });
+    },
+  });
+</script>
+
+<style scoped></style>

+ 73 - 0
src/views/demo/comp/upload/Upload3.vue

@@ -0,0 +1,73 @@
+<template>
+  <Alert message="嵌入表单,加入resultFiled自定义返回值" />
+    <BasicForm @register="registerCustom" class="my-5" />
+</template>
+
+<script setup lang="ts">
+  import { uploadApi } from '@/api/sys/upload';
+  import { useMessage } from '@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+  import { BasicForm, FormSchema, useForm } from '@/components/Form';
+  import { Alert } from 'ant-design-vue';
+  const schemasCustom: FormSchema[] = [
+    {
+      field: 'field3',
+      component: 'Upload',
+      label: '字段3',
+      componentProps: {
+        resultField: 'data3.url',
+        api: (file, progress) => {
+          return new Promise((resolve) => {
+            uploadApi(file, progress).then((uploadApiResponse) => {
+              resolve({
+                code: 200,
+                data3: {
+                  url: uploadApiResponse.data.url,
+                },
+              });
+            });
+          });
+        },
+      },
+    },
+    {
+      field: 'field4',
+      component: 'ImageUpload',
+      label: '字段4(ImageUpload)',
+      colProps: {
+        span: 8,
+      },
+      componentProps: {
+        resultField: 'data4.url',
+        api: (file, progress) => {
+          return new Promise((resolve) => {
+            uploadApi(file, progress).then((uploadApiResponse) => {
+              resolve({
+                code: 200,
+                data4: {
+                  url: uploadApiResponse.data.url,
+                },
+              });
+            });
+          });
+        },
+      },
+    },
+  ];
+  const [registerCustom, { getFieldsValue: getFieldsValueCustom }] = useForm({
+    labelWidth: 160,
+    schemas: schemasCustom,
+    actionColOptions: {
+      span: 18,
+    },
+    submitFunc: () => {
+      return new Promise((resolve) => {
+        console.log(getFieldsValueCustom());
+        resolve();
+        createMessage.success(`请到控制台查看结果`);
+      });
+    },
+  });
+</script>
+
+<style scoped></style>

+ 180 - 0
src/views/demo/comp/upload/Upload4.vue

@@ -0,0 +1,180 @@
+<template>
+  <Alert message="嵌入表单,自定义预览内容" />
+  <BasicForm @register="registerPreview" class="my-5" />
+</template>
+
+<script setup lang="ts">
+  import { uploadApi } from '@/api/sys/upload';
+  import { useMessage } from '@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+  import { BasicForm, FormSchema, useForm } from '@/components/Form';
+  import { Alert, Button } from 'ant-design-vue';
+  import { createVNode } from 'vue';
+  const schemasPreview: FormSchema[] = [
+    {
+      field: 'field5',
+      component: 'Upload',
+      label: '字段5',
+      componentProps: {
+        previewColumns: [
+          {
+            title: 'url5',
+            dataIndex: 'url5',
+          },
+          {
+            title: 'type5',
+            dataIndex: 'type5',
+          },
+          {
+            title: 'name5',
+            dataIndex: 'name5',
+          },
+          {
+            title: 'operation',
+            dataIndex: '',
+            customRender: ({ record }) => {
+              return createVNode(
+                Button,
+                {
+                  onclick: () => {
+                    console.log(record);
+                    createMessage.success(`请到控制台查看该行输出结果`);
+                  },
+                },
+                () => '点我输出该行信息',
+              );
+            },
+          },
+        ],
+        beforePreviewData: (arg) => {
+          let data = arg
+            .filter((item) => !!item)
+            .map((item) => {
+              if (typeof item !== 'string') {
+                console.error('return value should be string');
+                return;
+              }
+              return {
+                url5: item,
+                type5: item.split('.').pop() || '',
+                name5: item.split('/').pop() || '',
+              };
+            });
+          return data;
+        },
+        resultField: 'data5.url',
+        api: (file, progress) => {
+          return new Promise((resolve) => {
+            uploadApi(file, progress).then((uploadApiResponse) => {
+              resolve({
+                code: 200,
+                data5: {
+                  url: uploadApiResponse.data.url,
+                },
+              });
+            });
+          });
+        },
+      },
+    },
+    {
+      field: 'field6',
+      component: 'Upload',
+      label: '字段6',
+      componentProps: {
+        maxNumber:2,
+        previewColumns: ({ handleRemove, handleAdd}) => {
+          return [
+            {
+              title: 'url6',
+              dataIndex: 'url6',
+            },
+            {
+              title: 'type6',
+              dataIndex: 'type6',
+            },
+            {
+              title: '操作1',
+              dataIndex: 'operation',
+              customRender: ({ record }) => {
+                return createVNode('div', {}, [
+                  createVNode(
+                    Button,
+                    {
+                      type:"primary",
+                      style:"margin:4px",
+                      onclick: () => {
+                        handleAdd(
+                          { url6: 'https://vebn.oss-cn-beijing.aliyuncs.com/vben/logo.png' },
+                          'url6',
+                        );
+                      },
+                    },
+                    () => '点我新增',
+                  ),
+                  createVNode(
+                    Button,
+                    {
+                      danger:true,
+                      onclick: () => {
+                        handleRemove({ url6: record.url6 }, 'url6');
+                      },
+                    },
+                    () => '点我删除',
+                  ),
+                ]);
+              },
+            },
+            
+          ];
+        },
+        beforePreviewData: (arg) => {
+          let data = arg
+            .filter((item) => !!item)
+            .map((item) => {
+              if (typeof item !== 'string') {
+                console.error('return value should be string');
+                return;
+              }
+              return {
+                url6: item,
+                type6: item.split('.').pop() || '',
+                name6: item.split('/').pop() || '',
+              };
+            });
+          return data;
+        },
+        resultField: 'data6.url',
+        api: (file, progress) => {
+          return new Promise((resolve) => {
+            uploadApi(file, progress).then((uploadApiResponse) => {
+              resolve({
+                code: 200,
+                data6: {
+                  url: uploadApiResponse.data.url,
+                },
+              });
+            });
+          });
+        },
+      },
+      
+    },
+  ];
+  const [registerPreview, { getFieldsValue: getFieldsValuePreview }] = useForm({
+    labelWidth: 160,
+    schemas: schemasPreview,
+    actionColOptions: {
+      span: 18,
+    },
+    submitFunc: () => {
+      return new Promise((resolve) => {
+        console.log(getFieldsValuePreview());
+        resolve();
+        createMessage.success(`请到控制台查看结果`);
+      });
+    },
+  });
+</script>
+
+<style scoped></style>

+ 8 - 219
src/views/demo/comp/upload/index.vue

@@ -1,226 +1,15 @@
 <template>
   <PageWrapper title="上传组件示例">
-    <Alert message="基础示例" />
-    <BasicUpload
-      :maxSize="20"
-      :maxNumber="10"
-      @change="handleChange"
-      :api="uploadApi"
-      class="my-5"
-      :accept="['image/*']"
-    />
-
-    <Alert message="嵌入表单,加入表单校验" />
-
-    <BasicForm @register="registerValiate" class="my-5" />
-
-    <Alert message="嵌入表单,加入resultFiled自定义返回值" />
-    <BasicForm @register="registerCustom" class="my-5" />
-
-    <Alert message="嵌入表单,自定义预览内容" />
-    <BasicForm @register="registerPreview" class="my-5" />
+    <Upload1></Upload1>
+    <Upload2></Upload2>
+    <Upload3></Upload3>
+    <Upload4></Upload4>
   </PageWrapper>
 </template>
 <script lang="ts" setup>
-  import { BasicUpload } from '@/components/Upload';
-  import { useMessage } from '@/hooks/web/useMessage';
-  import { BasicForm, FormSchema, useForm } from '@/components/Form';
+  import Upload1 from './Upload1.vue';
+  import Upload2 from './Upload2.vue';
+  import Upload3 from './Upload3.vue';
+  import Upload4 from './Upload4.vue';
   import { PageWrapper } from '@/components/Page';
-  import { Alert, Button } from 'ant-design-vue';
-  import { uploadApi } from '@/api/sys/upload';
-  import { createVNode } from 'vue';
-
-  const schemasValiate: FormSchema[] = [
-    {
-      field: 'field1',
-      component: 'Upload',
-      label: '字段1',
-      rules: [{ required: true, message: '请选择上传文件' }],
-      componentProps: {
-        api: uploadApi,
-      },
-    },
-    {
-      field: 'field2',
-      component: 'ImageUpload',
-      label: '字段2(ImageUpload)',
-      colProps: {
-        span: 8,
-      },
-      componentProps: {
-        api: uploadApi,
-      },
-    },
-  ];
-  const schemasCustom: FormSchema[] = [
-    {
-      field: 'field3',
-      component: 'Upload',
-      label: '字段3',
-      componentProps: {
-        resultField: 'data3.url',
-        api: (file, progress) => {
-          return new Promise((resolve) => {
-            uploadApi(file, progress).then((uploadApiResponse) => {
-              resolve({
-                code: 200,
-                data3: {
-                  url: uploadApiResponse.data.url,
-                },
-              });
-            });
-          });
-        },
-      },
-    },
-    {
-      field: 'field4',
-      component: 'ImageUpload',
-      label: '字段4(ImageUpload)',
-      colProps: {
-        span: 8,
-      },
-      componentProps: {
-        resultField: 'data4.url',
-        api: (file, progress) => {
-          return new Promise((resolve) => {
-            uploadApi(file, progress).then((uploadApiResponse) => {
-              resolve({
-                code: 200,
-                data4: {
-                  url: uploadApiResponse.data.url,
-                },
-              });
-            });
-          });
-        },
-      },
-    },
-  ];
-  const schemasPreview: FormSchema[] = [
-    {
-      field: 'field5',
-      component: 'Upload',
-      label: '字段5',
-      componentProps: {
-        previewColumns: [
-          {
-            title: 'url5',
-            dataIndex: 'url5',
-          },
-          {
-            title: 'type5',
-            dataIndex: 'type5',
-          },
-          {
-            title: 'name5',
-            dataIndex: 'name5',
-          },
-          {
-            title: 'operation',
-            dataIndex: '',
-            customRender: ({ record }) => {
-              return createVNode(
-                Button,
-                {
-                  onclick: () => {
-                    console.log(record);
-                    createMessage.success(`请到控制台查看该行输出结果`);
-                  },
-                },
-                '点我',
-              );
-            },
-          },
-        ],
-        beforePreviewData: (arg) => {
-          let data = arg
-            .filter((item) => !!item)
-            .map((item) => {
-              if (typeof item !== 'string') {
-                console.error('return value should be string');
-                return;
-              }
-              return {
-                url5: item,
-                type5: item.split('.').pop() || '',
-                name5: item.split('/').pop() || '',
-              };
-            });
-          return data;
-        },
-        resultField: 'data5.url',
-        api: (file, progress) => {
-          return new Promise((resolve) => {
-            uploadApi(file, progress).then((uploadApiResponse) => {
-              resolve({
-                code: 200,
-                data5: {
-                  url: uploadApiResponse.data.url,
-                },
-              });
-            });
-          });
-        },
-      },
-    },
-  ];
-
-  const { createMessage } = useMessage();
-
-  function handleChange(list: string[]) {
-    createMessage.success(`已上传文件${JSON.stringify(list)}`);
-  }
-  const [registerValiate, { getFieldsValue: getFieldsValueValiate, validate }] = useForm({
-    labelWidth: 160,
-    schemas: schemasValiate,
-    actionColOptions: {
-      span: 18,
-    },
-    submitFunc: () => {
-      return new Promise((resolve) => {
-        validate()
-          .then(() => {
-            resolve();
-            console.log(getFieldsValueValiate());
-            createMessage.success(`请到控制台查看结果`);
-          })
-          .catch(() => {
-            createMessage.error(`请输入必填项`);
-          });
-      });
-    },
-  });
-
-  // resultFields 字段示例
-  const [registerCustom, { getFieldsValue: getFieldsValueCustom }] = useForm({
-    labelWidth: 160,
-    schemas: schemasCustom,
-    actionColOptions: {
-      span: 18,
-    },
-    submitFunc: () => {
-      return new Promise((resolve) => {
-        console.log(getFieldsValueCustom());
-        resolve();
-        createMessage.success(`请到控制台查看结果`);
-      });
-    },
-  });
-
-  // registerPreview
-  const [registerPreview, { getFieldsValue: getFieldsValuePreview }] = useForm({
-    labelWidth: 160,
-    schemas: schemasPreview,
-    actionColOptions: {
-      span: 18,
-    },
-    submitFunc: () => {
-      return new Promise((resolve) => {
-        console.log(getFieldsValuePreview());
-        resolve();
-        createMessage.success(`请到控制台查看结果`);
-      });
-    },
-  });
 </script>