From d9cdf3f034680f18156d6e6804b5c3ac5e6a4336 Mon Sep 17 00:00:00 2001 From: Electrolux <59329360+electroluxcode@users.noreply.github.com> Date: Fri, 31 May 2024 09:54:05 +0800 Subject: [PATCH] chore(upload): use uid && fix handleDelete (#3872) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(upload): 重构组件,添加key作为标识 * fix(upload): 显式传入handleDelete * update case --- src/components/Upload/src/BasicUpload.vue | 66 +++++++++++++++---- .../Upload/src/components/UploadModal.vue | 2 +- .../src/components/UploadPreviewModal.vue | 41 ++++++------ src/components/Upload/src/components/data.tsx | 15 ++++- src/components/Upload/src/props.ts | 13 ++-- src/components/Upload/src/types/typing.ts | 5 ++ src/views/demo/comp/upload/Upload4.vue | 29 +++++--- 7 files changed, 116 insertions(+), 55 deletions(-) diff --git a/src/components/Upload/src/BasicUpload.vue b/src/components/Upload/src/BasicUpload.vue index fa5eabfda..06fe69b10 100644 --- a/src/components/Upload/src/BasicUpload.vue +++ b/src/components/Upload/src/BasicUpload.vue @@ -54,10 +54,11 @@ import { uploadContainerProps } from './props'; import { omit } from 'lodash-es'; import { useI18n } from '@/hooks/web/useI18n'; - import { isArray } from '@/utils/is'; + import { isArray, isObject, isString } from '@/utils/is'; import UploadModal from './components/UploadModal.vue'; import UploadPreviewModal from './components/UploadPreviewModal.vue'; - + import { BaseFileItem } from './types/typing'; + import { buildUUID } from '@/utils/uuid'; defineOptions({ name: 'BasicUpload' }); const props = defineProps(uploadContainerProps); @@ -72,7 +73,7 @@ // 预览modal const [registerPreviewModal, { openModal: openPreviewModal }] = useModal(); - const fileList = ref([]); + const fileList = ref([]); const showPreview = computed(() => { const { emptyHidePreview } = props; @@ -84,27 +85,64 @@ const value = { ...attrs, ...props }; return omit(value, 'onChange'); }); - + function getValue(valueKey="url") { + const list = (fileList.value || []).map((item: any) => { + return item[valueKey]; + }); + return list; + } + function genFileListByUrls(urls: string[]) { + const list = urls.map((e) => { + return { + uid: buildUUID(), + url: e, + }; + }); + return list; + } watch( () => props.value, - (value = []) => { - fileList.value = isArray(value) ? value : []; + (v = []) => { + let values: string[] = []; + if (v) { + if (isArray(v)) { + values = v; + } else if (typeof v == 'string') { + values.push(v); + } + fileList.value = values.map((item,i) => { + if (item && isString(item)) { + return { + uid: buildUUID(), + url: item, + }; + } else if (item && isObject(item)) { + return item; + } else { + return; + } + }) as any; + } + emit('update:value', values); + emit('change', values); }, { immediate: true }, ); // 上传modal保存操作 - function handleChange(urls: string[]) { - fileList.value = [...unref(fileList), ...(urls || [])]; - emit('update:value', fileList.value); - emit('change', fileList.value); + function handleChange(urls: string[],valueKey:string) { + fileList.value = [...unref(fileList), ...(genFileListByUrls(urls) || [])]; + const values = getValue(valueKey); + emit('update:value', values); + emit('change', values); } // 预览modal保存操作 - function handlePreviewChange(urls: string[]) { - fileList.value = [...(urls || [])]; - emit('update:value', fileList.value); - emit('change', fileList.value); + function handlePreviewChange(fileItems: string[],valueKey:string) { + fileList.value = [...(fileItems || [])]; + const values = getValue(valueKey); + emit('update:value', values); + emit('change', values); } function handleDelete(record: Recordable) { diff --git a/src/components/Upload/src/components/UploadModal.vue b/src/components/Upload/src/components/UploadModal.vue index 8491d927f..698d9307a 100644 --- a/src/components/Upload/src/components/UploadModal.vue +++ b/src/components/Upload/src/components/UploadModal.vue @@ -71,7 +71,7 @@ const props = defineProps({ ...basicProps, previewFileList: { - type: Array as PropType, + type: Array as PropType, default: () => [], }, }); diff --git a/src/components/Upload/src/components/UploadPreviewModal.vue b/src/components/Upload/src/components/UploadPreviewModal.vue index 5ecd9623f..bfc940d41 100644 --- a/src/components/Upload/src/components/UploadPreviewModal.vue +++ b/src/components/Upload/src/components/UploadPreviewModal.vue @@ -14,15 +14,15 @@ import { watch, ref } from 'vue'; import FileList from './FileList.vue'; import { BasicModal, useModalInner } from '@/components/Modal'; - import { previewProps } from '../props'; - import { FileBasicColumn, PreviewFileItem } from '../types/typing'; + import { handleFnKey, previewProps } from '../props'; + import { BaseFileItem, FileBasicColumn, PreviewFileItem } from '../types/typing'; import { downloadByUrl } from '@/utils/file/download'; import { createPreviewColumns, createPreviewActionColumn } from './data'; import { useI18n } from '@/hooks/web/useI18n'; import { isArray, isFunction } from '@/utils/is'; import { BasicColumn } from '@/components/Table'; import { useMessage } from '@/hooks/web/useMessage'; - + import { buildUUID } from '@/utils/uuid'; const { createMessage } = useMessage(); const props = defineProps(previewProps); @@ -35,7 +35,7 @@ const [register] = useModalInner(); const { t } = useI18n(); - const fileListRef = ref>([]); + const fileListRef = ref>([]); watch( () => props.previewColumns, () => { @@ -64,14 +64,15 @@ fileListRef.value = value .filter((item) => !!item) .map((item) => { - if (typeof item != 'string') { - console.error('return value should be string'); + if (typeof item != 'object') { + console.error('return value should be object'); return; } return { - url: item, - type: item.split('.').pop() || '', - name: item.split('/').pop() || '', + uid: item?.uid, + url: item?.url, + type: item?.url?.split('.').pop() || '', + name: item?.url?.split('/').pop() || '', }; }); }, @@ -79,28 +80,26 @@ ); // 删除 - function handleRemove(record: PreviewFileItem | Record, urlKey = 'url') { - const index = fileListRef.value.findIndex((item) => item[urlKey] === record[urlKey]); + function handleRemove(obj: Record) { + let { record = {}, valueKey = 'url', uidKey = 'uid' } = obj; + const index = fileListRef.value.findIndex((item) => item[uidKey] === record[uidKey]); if (index !== -1) { const removed = fileListRef.value.splice(index, 1); - emit('delete', removed[0][urlKey]); - emit( - 'list-change', - fileListRef.value.map((item) => item[urlKey]), - ); + emit('delete', removed[0][uidKey]); + emit('list-change', fileListRef.value, valueKey); } } // 添加 - function handleAdd(record: PreviewFileItem | Record, urlKey = 'url') { + function handleAdd(obj: Record) { + let { record = {}, valueKey = 'url', uidKey = 'uid' } = obj; const { maxNumber } = props; if (fileListRef.value.length + fileListRef.value.length > maxNumber) { return createMessage.warning(t('component.upload.maxNumber', [maxNumber])); } + record[uidKey] = record[uidKey] ?? buildUUID(); + record[valueKey] = record[valueKey]; fileListRef.value = [...fileListRef.value, record]; - emit( - 'list-change', - fileListRef.value.map((item) => item[urlKey]), - ); + emit('list-change', fileListRef.value, valueKey); } // 下载 function handleDownload(record: PreviewFileItem) { diff --git a/src/components/Upload/src/components/data.tsx b/src/components/Upload/src/components/data.tsx index 7c6415dfa..17606619b 100644 --- a/src/components/Upload/src/components/data.tsx +++ b/src/components/Upload/src/components/data.tsx @@ -5,6 +5,7 @@ import { Progress, Tag } from 'ant-design-vue'; import TableAction from '@/components/Table/src/components/TableAction.vue'; import ThumbUrl from './ThumbUrl.vue'; import { useI18n } from '@/hooks/web/useI18n'; +import { previewColumnsFnType } from '../props'; const { t } = useI18n(); @@ -81,7 +82,11 @@ export function createActionColumn(handleRemove: Function): FileBasicColumn { { label: t('component.upload.del'), color: 'error', - onClick: handleRemove.bind(null, record), + onClick: handleRemove.bind(null, { + record, + uidKey: 'uid', + valueKey: 'url', + }), }, ]; return ; @@ -112,7 +117,7 @@ export function createPreviewActionColumn({ handleRemove, handleDownload, }: { - handleRemove: Fn; + handleRemove: previewColumnsFnType['handleRemove']; handleDownload: Fn; }): BasicColumn { return { @@ -125,7 +130,11 @@ export function createPreviewActionColumn({ { label: t('component.upload.del'), color: 'error', - onClick: handleRemove.bind(null, record), + onClick: handleRemove.bind(null, { + record, + uidKey: 'uid', + valueKey: 'url', + }), }, { label: t('component.upload.download'), diff --git a/src/components/Upload/src/props.ts b/src/components/Upload/src/props.ts index 3d4d480c8..d26ca54fd 100644 --- a/src/components/Upload/src/props.ts +++ b/src/components/Upload/src/props.ts @@ -1,5 +1,5 @@ import type { PropType } from 'vue'; -import { FileBasicColumn } from './types/typing'; +import { BaseFileItem, FileBasicColumn } from './types/typing'; import type { Options } from 'sortablejs'; @@ -14,9 +14,10 @@ type SortableOptions = Merge< // ...可扩展 } >; -type previewColumnsFnType = { - handleRemove: (record: Record, key: string) => any; - handleAdd: (record: Record, key: string) => any; +export type handleFnKey = "record" | "valueKey" | "uidKey" +export type previewColumnsFnType = { + handleRemove: (record: Record) => any; + handleAdd: (record: Record) => any; }; export const previewType = { previewColumns: { @@ -26,7 +27,7 @@ export const previewType = { required: false, }, beforePreviewData: { - type: Function as PropType<(arg: string[]) => Recordable>, + type: Function as PropType<(arg: BaseFileItem[] | any) => Recordable>, default: null, required: false, }, @@ -112,7 +113,7 @@ export const uploadContainerProps = { export const previewProps = { value: { - type: Array as PropType, + type: Array as PropType, default: () => [], }, maxNumber: { diff --git a/src/components/Upload/src/types/typing.ts b/src/components/Upload/src/types/typing.ts index ad70489d7..e7983e7f6 100644 --- a/src/components/Upload/src/types/typing.ts +++ b/src/components/Upload/src/types/typing.ts @@ -20,6 +20,11 @@ export interface FileItem { uuid: string; } +export interface BaseFileItem { + uid: string | number; + url: string; + name?: string; +} export interface PreviewFileItem { url: string; name: string; diff --git a/src/views/demo/comp/upload/Upload4.vue b/src/views/demo/comp/upload/Upload4.vue index 430db4af4..1909592d3 100644 --- a/src/views/demo/comp/upload/Upload4.vue +++ b/src/views/demo/comp/upload/Upload4.vue @@ -106,10 +106,14 @@ type: 'primary', style: 'margin:4px', onclick: () => { - handleAdd( - { url6: 'https://vebn.oss-cn-beijing.aliyuncs.com/vben/logo.png' }, - 'url6', - ); + handleAdd({ + record: { + id6: new Date().getTime(), + url6: 'https://vebn.oss-cn-beijing.aliyuncs.com/vben/logo.png', + }, + uidKey: 'id6', + valueKey: 'url6', + }); }, }, () => '点我新增', @@ -119,7 +123,11 @@ { danger: true, onclick: () => { - handleRemove({ url6: record.url6 }, 'url6'); + handleRemove({ + record: { url6: record.url6 }, + uidKey: 'url6', + valueKey: 'url6', + }); }, }, () => '点我删除', @@ -133,14 +141,15 @@ let data = arg .filter((item) => !!item) .map((item) => { - if (typeof item !== 'string') { - console.error('return value should be string'); + if (typeof item !== 'object') { + console.error('return value should be object'); return; } return { - url6: item, - type6: item.split('.').pop() || '', - name6: item.split('/').pop() || '', + uid: item?.uid, + url6: item?.url, + type6: item?.url?.split('.').pop() || '', + name6: item?.url?.split('/').pop() || '', }; }); return data;