chore(upload): use uid && fix handleDelete (#3872)

* chore(upload): 重构组件,添加key作为标识

* fix(upload): 显式传入handleDelete

* update case
This commit is contained in:
Electrolux
2024-05-31 09:54:05 +08:00
committed by GitHub
parent 0bc01d8528
commit d9cdf3f034
7 changed files with 116 additions and 55 deletions

View File

@@ -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<string[]>([]);
const fileList = ref<BaseFileItem[] | any[]>([]);
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<any>) {

View File

@@ -71,7 +71,7 @@
const props = defineProps({
...basicProps,
previewFileList: {
type: Array as PropType<string[]>,
type: Array as PropType<string[] | any[]>,
default: () => [],
},
});

View File

@@ -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<PreviewFileItem[] | Array<any>>([]);
const fileListRef = ref<BaseFileItem[] | Array<any>>([]);
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<string, any>, urlKey = 'url') {
const index = fileListRef.value.findIndex((item) => item[urlKey] === record[urlKey]);
function handleRemove(obj: Record<handleFnKey, any>) {
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<string, any>, urlKey = 'url') {
function handleAdd(obj: Record<handleFnKey, any>) {
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) {

View File

@@ -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 <TableAction actions={actions} outside={true} />;
@@ -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'),

View File

@@ -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<string, any>, key: string) => any;
handleAdd: (record: Record<string, any>, key: string) => any;
export type handleFnKey = "record" | "valueKey" | "uidKey"
export type previewColumnsFnType = {
handleRemove: (record: Record<handleFnKey, any>) => any;
handleAdd: (record: Record<handleFnKey, any>) => any;
};
export const previewType = {
previewColumns: {
@@ -26,7 +27,7 @@ export const previewType = {
required: false,
},
beforePreviewData: {
type: Function as PropType<(arg: string[]) => Recordable<any>>,
type: Function as PropType<(arg: BaseFileItem[] | any) => Recordable<any>>,
default: null,
required: false,
},
@@ -112,7 +113,7 @@ export const uploadContainerProps = {
export const previewProps = {
value: {
type: Array as PropType<string[]>,
type: Array as PropType<BaseFileItem[] | any[]>,
default: () => [],
},
maxNumber: {

View File

@@ -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;

View File

@@ -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;