Pārlūkot izejas kodu

fix: BsUi-新增多文件附件上传组件

hanxiaohui 2 mēneši atpakaļ
vecāks
revīzija
673dd7912f

+ 189 - 0
src/components/BsUi/FileUploadSubTable/index.vue

@@ -0,0 +1,189 @@
+<template>
+  <BsSubTableInput
+    :table-options="tableOptions"
+    :value="value"
+    :is-show-add-btn="false"
+    :is-show-batch-delete-btn="false"
+    :opt-btn-slots="batchTableOptBtnSlots"
+    :is-edit-row="false"
+    @sub-table-input-change="handleTableChange"
+    ref="subTableRef"
+  >
+    <template #custom1>
+      <div class="w-full flex items-center justify-center">
+        <FileUpload listType="text" :showUploadList="false" @change="handleChange" class="w-full file-btn">
+          <template #customUploadBtnSlot>
+            <a-button type="text" style="width: 100%">
+              <template #icon>
+                <upload-outlined />
+              </template>
+              <span>上传文件</span></a-button
+            >
+          </template>
+        </FileUpload>
+      </div>
+    </template>
+    <template #custom2>
+      <a-button type="text" style="width: 100%" :disabled="!isSelectedData" @click="handleDeleteBatch">
+        <template #icon> <DeleteOutlined /></template>
+        <span>批量删除</span>
+      </a-button>
+    </template>
+  </BsSubTableInput>
+</template>
+
+<script setup>
+  import { computed, h, ref, watch } from 'vue';
+  import { BsSubTableInput, useBsTable } from '/@/components/BsUi/index.js';
+  import { DISPLAY_STATE } from '/@/components/BsUi/constant.js';
+  import FileUpload from '/@/components/support/file-upload/index.vue';
+  import { cloneDeep, isEmpty, uniqBy } from 'lodash';
+  import { DeleteOutlined } from '@ant-design/icons-vue';
+
+  const props = defineProps({
+    value: {
+      type: Array,
+      default: () => [],
+    },
+  });
+
+  const subTableRef = ref(null);
+
+  const isSelectedData = computed(() => {
+    return isEmpty(subTableRef.value?.selectedData) ? false : subTableRef.value?.selectedData.length !== 0;
+  });
+
+  const rowKey = computed(() => {
+    return tableOptions.gridOptions?.rowKey || 'fileId';
+  });
+
+  const emits = defineEmits(['change', 'update:value']);
+
+  const batchTableOptBtnSlots = ref(['custom1', 'custom2']);
+
+  const { tableOptions, refreshTable } = useBsTable({
+    tableOptions: {
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            field: 'fileId',
+            width: '80px',
+            align: 'center',
+            title: 'ID',
+          },
+          {
+            field: 'fileName',
+            title: '文件名称',
+          },
+          {
+            field: 'fileType',
+            width: '150px',
+            title: '文件类型',
+          },
+          {
+            field: 'fileSize',
+            width: '150px',
+            title: '文件大小(M)',
+            align: 'center',
+            slots: {
+              default: ({ row }) => (Number(row.fileSize) / (1024 * 1024)).toFixed(3),
+            },
+          },
+          {
+            fixed: 'right',
+            cellRender: {
+              name: 'CellOption',
+              extraProps: {
+                buttons: [
+                  {
+                    title: '预览',
+                    code: 'view',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {},
+                    extraProps: {},
+                  },
+                  {
+                    title: '删除',
+                    code: 'delete',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {
+                      handleDelete(row);
+                    },
+                    extraProps: {
+                      danger: true,
+                    },
+                  },
+                ],
+              },
+            },
+          },
+        ],
+        data: [],
+        rowKey: 'fileId',
+      },
+      request: loadTable,
+    },
+  });
+
+  const handleChange = (files) => {
+    const oldFiles = [...files, ...props.value];
+    const newFiles = cloneDeep(uniqBy(oldFiles, rowKey.value));
+    emits('update:value', newFiles);
+    emits('change', newFiles);
+  };
+
+  async function loadTable() {
+    return {
+      list: props.value,
+      total: props.value.length,
+    };
+  }
+
+  const handleDeleteBatch = () => {
+    subTableRef.value.deleteBatch();
+  };
+
+  function handleDelete(row) {
+    const delIndex = props.value.findIndex((v) => v[rowKey.value] === row[rowKey.value]);
+    subTableRef.value.deleteRow(delIndex);
+  }
+
+  function handleTableChange(value) {
+    emits('update:value', value);
+    emits('change', value);
+  }
+
+  watch(
+    () => props.value,
+    (newVal) => {
+      refreshTable();
+    },
+    {
+      immediate: true,
+    }
+  );
+</script>
+
+<style scoped lang="scss">
+  .file-btn {
+    :deep(.ant-upload-wrapper) {
+      display: block;
+      flex: 1;
+    }
+    :deep(.ant-upload) {
+      display: block;
+      flex: 1;
+    }
+  }
+</style>

+ 25 - 17
src/components/BsUi/SubTableInput/index.vue

@@ -66,7 +66,7 @@
   });
 
   const rowKey = computed(() => {
-    return props.tableOptions.rowKey;
+    return props.tableOptions.gridOptions.rowKey;
   });
 
   const emits = defineEmits('change', 'update:value', 'add');
@@ -97,16 +97,7 @@
                   icon={h(DeleteOutlined)}
                   type='text'
                   onClick={() => {
-                    const tableData = props.value;
-                    const deepCloneData = cloneDeep(tableData);
-                    deepCloneData.splice(rowIndex, 1);
-                    emits('update:value', deepCloneData);
-                    emits('change', deepCloneData);
-                    emits('delete-row', {
-                      gridRef: getGridRef(),
-                      bsTable: bsTableBean,
-                      tableData: deepCloneData,
-                    });
+                    handleDeleteRow(rowIndex);
                   }}
                 ></a-button>
               </a-tooltip>
@@ -182,8 +173,7 @@
             item[column.field] = row[column.field];
           }
         });
-        emits('update:value', tableData);
-        emits('change', tableData);
+        updateTable(tableData);
       },
     };
     if (props.isEditRow) {
@@ -216,8 +206,7 @@
     newData.push({
       [ROW_KEY_FIELD]: getUUID(),
     });
-    emits('update:value', newData);
-    emits('change', newData);
+    updateTable(newData);
     emits('add', {
       gridRef: getGridRef(),
       bsTable: bsTableBean,
@@ -237,8 +226,7 @@
           }
         }) === -1
     );
-    emits('update:value', newTableData);
-    emits('change', newTableData);
+    updateTable(newTableData);
     selectedData.value = [];
     emits('delete-rows', {
       gridRef: getGridRef(),
@@ -247,6 +235,23 @@
     });
   };
 
+  function handleDeleteRow(index) {
+    const deepCloneData = cloneDeep(props.value);
+    deepCloneData.splice(index, 1);
+    updateTable(deepCloneData);
+    emits('delete-row', {
+      gridRef: getGridRef(),
+      bsTable: bsTableBean,
+      tableData: deepCloneData,
+    });
+  }
+
+  function updateTable(tableData) {
+    emits('update:value', tableData);
+    emits('change', tableData);
+    emits('sub-table-input-change', tableData);
+  }
+
   watch(
     () => props.value,
     (val) => {
@@ -268,6 +273,9 @@
         }
       });
     },
+    deleteBatch: handleBatchDelete,
+    deleteRow: handleDeleteRow,
+    selectedData,
   });
 </script>
 

+ 17 - 14
src/components/BsUi/index.js

@@ -7,17 +7,18 @@ import BsTable, { useBsTable } from './Table/index.js';
 import BsDrawer, { useBsDrawer } from './Drawer/index.js';
 import BsForm, { useBsForm } from './Form/index.js';
 import BsModalTableSelector from './ModalTableSelector/index.vue';
-import BsDescriptions from "./Descriptions/index.vue"
-import BsContentsWrapper from "./ContentsWrapper/index.vue"
-import BsTabs from "./Tabs/index.vue"
-import BsEllipsisText from "./EllipsisText/index.vue"
-import BsDicTag from "./DicTag/index.vue"
-import BsLink from "./Link/index.vue"
-import BsCatalog from "./Catalog/index.vue"
-import BsSvgIcon from "./SvgIcon/index.vue"
-import BsTabBar from "/@/components/BsUi/TabBar/index.vue"
-import BsPageWrapper from "./PageWrapper/index.vue"
-import BsTableSelector from './TableSelector/index.vue'
+import BsDescriptions from './Descriptions/index.vue';
+import BsContentsWrapper from './ContentsWrapper/index.vue';
+import BsTabs from './Tabs/index.vue';
+import BsEllipsisText from './EllipsisText/index.vue';
+import BsDicTag from './DicTag/index.vue';
+import BsLink from './Link/index.vue';
+import BsCatalog from './Catalog/index.vue';
+import BsSvgIcon from './SvgIcon/index.vue';
+import BsTabBar from '/@/components/BsUi/TabBar/index.vue';
+import BsPageWrapper from './PageWrapper/index.vue';
+import BsTableSelector from './TableSelector/index.vue';
+import BsFileUploadSubTable from './FileUploadSubTable/index.vue';
 
 const BsUi = {
   install(app) {
@@ -28,8 +29,8 @@ const BsUi = {
     app.component('BsTable', BsTable);
     app.component('BsDrawer', BsDrawer);
     app.component('BsModalTableSelector', BsModalTableSelector);
-    app.component('BsDescriptions', BsDescriptions)
-    app.component('BsContentsWrapper', BsContentsWrapper)
+    app.component('BsDescriptions', BsDescriptions);
+    app.component('BsContentsWrapper', BsContentsWrapper);
     app.component('BsTabs', BsTabs);
     app.component('BsEllipsisText', BsEllipsisText);
     app.component('BsDicTag', BsDicTag);
@@ -39,6 +40,7 @@ const BsUi = {
     app.component('BsTabBar', BsTabBar);
     app.component('BsPageWrapper', BsPageWrapper);
     app.component('BsTableSelector', BsTableSelector);
+    app.component('BsFileUploadSubTable', BsFileUploadSubTable);
   },
 };
 
@@ -68,5 +70,6 @@ export {
   BsSvgIcon,
   BsTabBar,
   BsPageWrapper,
-  BsTableSelector
+  BsTableSelector,
+  BsFileUploadSubTable,
 };