index.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <template>
  2. <BsSubTableInput
  3. :table-options="tableOptions"
  4. :value="value"
  5. :is-show-add-btn="false"
  6. :is-show-batch-delete-btn="false"
  7. :opt-btn-slots="batchTableOptBtnSlots"
  8. :is-edit-row="false"
  9. @sub-table-input-change="handleTableChange"
  10. ref="subTableRef"
  11. >
  12. <template #custom1>
  13. <div class="w-full flex items-center justify-center">
  14. <FileUpload listType="text" :showUploadList="false" @change="handleChange" class="w-full file-btn">
  15. <template #customUploadBtnSlot>
  16. <a-button type="text" style="width: 100%">
  17. <template #icon>
  18. <upload-outlined />
  19. </template>
  20. <span>上传文件</span></a-button
  21. >
  22. </template>
  23. </FileUpload>
  24. </div>
  25. </template>
  26. <template #custom2>
  27. <a-button type="text" style="width: 100%" :disabled="!isSelectedData" @click="handleDeleteBatch">
  28. <template #icon> <DeleteOutlined /></template>
  29. <span>批量删除</span>
  30. </a-button>
  31. </template>
  32. </BsSubTableInput>
  33. </template>
  34. <script setup>
  35. import { computed, h, ref, watch } from 'vue';
  36. import { BsSubTableInput, useBsTable } from '/@/components/BsUi/index.js';
  37. import { DISPLAY_STATE } from '/@/components/BsUi/constant.js';
  38. import FileUpload from '/@/components/support/file-upload/index.vue';
  39. import { cloneDeep, isEmpty, uniqBy } from 'lodash';
  40. import { DeleteOutlined } from '@ant-design/icons-vue';
  41. const props = defineProps({
  42. value: {
  43. type: Array,
  44. default: () => [],
  45. },
  46. });
  47. const subTableRef = ref(null);
  48. const isSelectedData = computed(() => {
  49. return isEmpty(subTableRef.value?.selectedData) ? false : subTableRef.value?.selectedData.length !== 0;
  50. });
  51. const rowKey = computed(() => {
  52. return tableOptions.gridOptions?.rowKey || 'fileId';
  53. });
  54. const emits = defineEmits(['change', 'update:value']);
  55. const batchTableOptBtnSlots = ref(['custom1', 'custom2']);
  56. const { tableOptions, refreshTable } = useBsTable({
  57. tableOptions: {
  58. gridOptions: {
  59. loading: false,
  60. columns: [
  61. {
  62. field: 'fileId',
  63. width: '80px',
  64. align: 'center',
  65. title: 'ID',
  66. },
  67. {
  68. field: 'fileName',
  69. title: '文件名称',
  70. },
  71. {
  72. field: 'fileType',
  73. width: '100px',
  74. title: '文件类型',
  75. cellRender: {
  76. name: 'CellFileType',
  77. },
  78. },
  79. {
  80. field: 'fileSize',
  81. width: '150px',
  82. title: '文件大小(M)',
  83. align: 'center',
  84. slots: {
  85. default: ({ row }) => (Number(row.fileSize) / (1024 * 1024)).toFixed(3),
  86. },
  87. },
  88. {
  89. fixed: 'right',
  90. cellRender: {
  91. name: 'CellOption',
  92. extraProps: {
  93. buttons: [
  94. {
  95. title: '预览',
  96. code: 'view',
  97. display: ({ row }) => {
  98. return DISPLAY_STATE.VISIBLE;
  99. },
  100. disabled({ row }) {
  101. return false;
  102. },
  103. onClick({ row }) {},
  104. extraProps: {},
  105. },
  106. {
  107. title: '删除',
  108. code: 'delete',
  109. display: ({ row }) => {
  110. return DISPLAY_STATE.VISIBLE;
  111. },
  112. disabled({ row }) {
  113. return false;
  114. },
  115. onClick({ row }) {
  116. handleDelete(row);
  117. },
  118. extraProps: {
  119. danger: true,
  120. },
  121. },
  122. ],
  123. },
  124. },
  125. },
  126. ],
  127. data: [],
  128. rowKey: 'fileId',
  129. },
  130. request: loadTable,
  131. },
  132. });
  133. const handleChange = (files) => {
  134. const oldFiles = [...files, ...props.value];
  135. const newFiles = cloneDeep(uniqBy(oldFiles, rowKey.value));
  136. emits('update:value', newFiles);
  137. emits('change', newFiles);
  138. };
  139. async function loadTable() {
  140. return {
  141. list: props.value,
  142. total: props.value.length,
  143. };
  144. }
  145. const handleDeleteBatch = () => {
  146. subTableRef.value.deleteBatch();
  147. };
  148. function handleDelete(row) {
  149. const delIndex = props.value.findIndex((v) => v[rowKey.value] === row[rowKey.value]);
  150. subTableRef.value.deleteRow(delIndex);
  151. }
  152. function handleTableChange(value) {
  153. emits('update:value', value);
  154. emits('change', value);
  155. }
  156. watch(
  157. () => props.value,
  158. (newVal) => {
  159. refreshTable();
  160. },
  161. {
  162. immediate: true,
  163. }
  164. );
  165. </script>
  166. <style scoped lang="scss">
  167. .file-btn {
  168. :deep(.ant-upload-wrapper) {
  169. display: block;
  170. flex: 1;
  171. }
  172. :deep(.ant-upload) {
  173. display: block;
  174. flex: 1;
  175. }
  176. }
  177. </style>