mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-25 16:16:20 +08:00
chore(upload): use uid && fix handleDelete (#3872)
* chore(upload): 重构组件,添加key作为标识 * fix(upload): 显式传入handleDelete * update case
This commit is contained in:
@@ -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>) {
|
||||
|
@@ -71,7 +71,7 @@
|
||||
const props = defineProps({
|
||||
...basicProps,
|
||||
previewFileList: {
|
||||
type: Array as PropType<string[]>,
|
||||
type: Array as PropType<string[] | any[]>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
@@ -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) {
|
||||
|
@@ -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'),
|
||||
|
@@ -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: {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user