chore(upload): use uid && fix handleDelete (#3872)
* chore(upload): 重构组件,添加key作为标识 * fix(upload): 显式传入handleDelete * update case
This commit is contained in:
parent
0bc01d8528
commit
d9cdf3f034
|
|
@ -54,10 +54,11 @@
|
||||||
import { uploadContainerProps } from './props';
|
import { uploadContainerProps } from './props';
|
||||||
import { omit } from 'lodash-es';
|
import { omit } from 'lodash-es';
|
||||||
import { useI18n } from '@/hooks/web/useI18n';
|
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 UploadModal from './components/UploadModal.vue';
|
||||||
import UploadPreviewModal from './components/UploadPreviewModal.vue';
|
import UploadPreviewModal from './components/UploadPreviewModal.vue';
|
||||||
|
import { BaseFileItem } from './types/typing';
|
||||||
|
import { buildUUID } from '@/utils/uuid';
|
||||||
defineOptions({ name: 'BasicUpload' });
|
defineOptions({ name: 'BasicUpload' });
|
||||||
|
|
||||||
const props = defineProps(uploadContainerProps);
|
const props = defineProps(uploadContainerProps);
|
||||||
|
|
@ -72,7 +73,7 @@
|
||||||
// 预览modal
|
// 预览modal
|
||||||
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal();
|
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal();
|
||||||
|
|
||||||
const fileList = ref<string[]>([]);
|
const fileList = ref<BaseFileItem[] | any[]>([]);
|
||||||
|
|
||||||
const showPreview = computed(() => {
|
const showPreview = computed(() => {
|
||||||
const { emptyHidePreview } = props;
|
const { emptyHidePreview } = props;
|
||||||
|
|
@ -84,27 +85,64 @@
|
||||||
const value = { ...attrs, ...props };
|
const value = { ...attrs, ...props };
|
||||||
return omit(value, 'onChange');
|
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(
|
watch(
|
||||||
() => props.value,
|
() => props.value,
|
||||||
(value = []) => {
|
(v = []) => {
|
||||||
fileList.value = isArray(value) ? value : [];
|
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 },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
// 上传modal保存操作
|
// 上传modal保存操作
|
||||||
function handleChange(urls: string[]) {
|
function handleChange(urls: string[],valueKey:string) {
|
||||||
fileList.value = [...unref(fileList), ...(urls || [])];
|
fileList.value = [...unref(fileList), ...(genFileListByUrls(urls) || [])];
|
||||||
emit('update:value', fileList.value);
|
const values = getValue(valueKey);
|
||||||
emit('change', fileList.value);
|
emit('update:value', values);
|
||||||
|
emit('change', values);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预览modal保存操作
|
// 预览modal保存操作
|
||||||
function handlePreviewChange(urls: string[]) {
|
function handlePreviewChange(fileItems: string[],valueKey:string) {
|
||||||
fileList.value = [...(urls || [])];
|
fileList.value = [...(fileItems || [])];
|
||||||
emit('update:value', fileList.value);
|
const values = getValue(valueKey);
|
||||||
emit('change', fileList.value);
|
emit('update:value', values);
|
||||||
|
emit('change', values);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDelete(record: Recordable<any>) {
|
function handleDelete(record: Recordable<any>) {
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
...basicProps,
|
...basicProps,
|
||||||
previewFileList: {
|
previewFileList: {
|
||||||
type: Array as PropType<string[]>,
|
type: Array as PropType<string[] | any[]>,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,15 @@
|
||||||
import { watch, ref } from 'vue';
|
import { watch, ref } from 'vue';
|
||||||
import FileList from './FileList.vue';
|
import FileList from './FileList.vue';
|
||||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||||
import { previewProps } from '../props';
|
import { handleFnKey, previewProps } from '../props';
|
||||||
import { FileBasicColumn, PreviewFileItem } from '../types/typing';
|
import { BaseFileItem, FileBasicColumn, PreviewFileItem } from '../types/typing';
|
||||||
import { downloadByUrl } from '@/utils/file/download';
|
import { downloadByUrl } from '@/utils/file/download';
|
||||||
import { createPreviewColumns, createPreviewActionColumn } from './data';
|
import { createPreviewColumns, createPreviewActionColumn } from './data';
|
||||||
import { useI18n } from '@/hooks/web/useI18n';
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
import { isArray, isFunction } from '@/utils/is';
|
import { isArray, isFunction } from '@/utils/is';
|
||||||
import { BasicColumn } from '@/components/Table';
|
import { BasicColumn } from '@/components/Table';
|
||||||
import { useMessage } from '@/hooks/web/useMessage';
|
import { useMessage } from '@/hooks/web/useMessage';
|
||||||
|
import { buildUUID } from '@/utils/uuid';
|
||||||
const { createMessage } = useMessage();
|
const { createMessage } = useMessage();
|
||||||
|
|
||||||
const props = defineProps(previewProps);
|
const props = defineProps(previewProps);
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
const [register] = useModalInner();
|
const [register] = useModalInner();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const fileListRef = ref<PreviewFileItem[] | Array<any>>([]);
|
const fileListRef = ref<BaseFileItem[] | Array<any>>([]);
|
||||||
watch(
|
watch(
|
||||||
() => props.previewColumns,
|
() => props.previewColumns,
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -64,14 +64,15 @@
|
||||||
fileListRef.value = value
|
fileListRef.value = value
|
||||||
.filter((item) => !!item)
|
.filter((item) => !!item)
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
if (typeof item != 'string') {
|
if (typeof item != 'object') {
|
||||||
console.error('return value should be string');
|
console.error('return value should be object');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
url: item,
|
uid: item?.uid,
|
||||||
type: item.split('.').pop() || '',
|
url: item?.url,
|
||||||
name: item.split('/').pop() || '',
|
type: item?.url?.split('.').pop() || '',
|
||||||
|
name: item?.url?.split('/').pop() || '',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -79,28 +80,26 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
function handleRemove(record: PreviewFileItem | Record<string, any>, urlKey = 'url') {
|
function handleRemove(obj: Record<handleFnKey, any>) {
|
||||||
const index = fileListRef.value.findIndex((item) => item[urlKey] === record[urlKey]);
|
let { record = {}, valueKey = 'url', uidKey = 'uid' } = obj;
|
||||||
|
const index = fileListRef.value.findIndex((item) => item[uidKey] === record[uidKey]);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
const removed = fileListRef.value.splice(index, 1);
|
const removed = fileListRef.value.splice(index, 1);
|
||||||
emit('delete', removed[0][urlKey]);
|
emit('delete', removed[0][uidKey]);
|
||||||
emit(
|
emit('list-change', fileListRef.value, valueKey);
|
||||||
'list-change',
|
|
||||||
fileListRef.value.map((item) => item[urlKey]),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 添加
|
// 添加
|
||||||
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;
|
const { maxNumber } = props;
|
||||||
if (fileListRef.value.length + fileListRef.value.length > maxNumber) {
|
if (fileListRef.value.length + fileListRef.value.length > maxNumber) {
|
||||||
return createMessage.warning(t('component.upload.maxNumber', [maxNumber]));
|
return createMessage.warning(t('component.upload.maxNumber', [maxNumber]));
|
||||||
}
|
}
|
||||||
|
record[uidKey] = record[uidKey] ?? buildUUID();
|
||||||
|
record[valueKey] = record[valueKey];
|
||||||
fileListRef.value = [...fileListRef.value, record];
|
fileListRef.value = [...fileListRef.value, record];
|
||||||
emit(
|
emit('list-change', fileListRef.value, valueKey);
|
||||||
'list-change',
|
|
||||||
fileListRef.value.map((item) => item[urlKey]),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// 下载
|
// 下载
|
||||||
function handleDownload(record: PreviewFileItem) {
|
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 TableAction from '@/components/Table/src/components/TableAction.vue';
|
||||||
import ThumbUrl from './ThumbUrl.vue';
|
import ThumbUrl from './ThumbUrl.vue';
|
||||||
import { useI18n } from '@/hooks/web/useI18n';
|
import { useI18n } from '@/hooks/web/useI18n';
|
||||||
|
import { previewColumnsFnType } from '../props';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
|
@ -81,7 +82,11 @@ export function createActionColumn(handleRemove: Function): FileBasicColumn {
|
||||||
{
|
{
|
||||||
label: t('component.upload.del'),
|
label: t('component.upload.del'),
|
||||||
color: 'error',
|
color: 'error',
|
||||||
onClick: handleRemove.bind(null, record),
|
onClick: handleRemove.bind(null, {
|
||||||
|
record,
|
||||||
|
uidKey: 'uid',
|
||||||
|
valueKey: 'url',
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return <TableAction actions={actions} outside={true} />;
|
return <TableAction actions={actions} outside={true} />;
|
||||||
|
|
@ -112,7 +117,7 @@ export function createPreviewActionColumn({
|
||||||
handleRemove,
|
handleRemove,
|
||||||
handleDownload,
|
handleDownload,
|
||||||
}: {
|
}: {
|
||||||
handleRemove: Fn;
|
handleRemove: previewColumnsFnType['handleRemove'];
|
||||||
handleDownload: Fn;
|
handleDownload: Fn;
|
||||||
}): BasicColumn {
|
}): BasicColumn {
|
||||||
return {
|
return {
|
||||||
|
|
@ -125,7 +130,11 @@ export function createPreviewActionColumn({
|
||||||
{
|
{
|
||||||
label: t('component.upload.del'),
|
label: t('component.upload.del'),
|
||||||
color: 'error',
|
color: 'error',
|
||||||
onClick: handleRemove.bind(null, record),
|
onClick: handleRemove.bind(null, {
|
||||||
|
record,
|
||||||
|
uidKey: 'uid',
|
||||||
|
valueKey: 'url',
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('component.upload.download'),
|
label: t('component.upload.download'),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import { FileBasicColumn } from './types/typing';
|
import { BaseFileItem, FileBasicColumn } from './types/typing';
|
||||||
|
|
||||||
import type { Options } from 'sortablejs';
|
import type { Options } from 'sortablejs';
|
||||||
|
|
||||||
|
|
@ -14,9 +14,10 @@ type SortableOptions = Merge<
|
||||||
// ...可扩展
|
// ...可扩展
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
type previewColumnsFnType = {
|
export type handleFnKey = "record" | "valueKey" | "uidKey"
|
||||||
handleRemove: (record: Record<string, any>, key: string) => any;
|
export type previewColumnsFnType = {
|
||||||
handleAdd: (record: Record<string, any>, key: string) => any;
|
handleRemove: (record: Record<handleFnKey, any>) => any;
|
||||||
|
handleAdd: (record: Record<handleFnKey, any>) => any;
|
||||||
};
|
};
|
||||||
export const previewType = {
|
export const previewType = {
|
||||||
previewColumns: {
|
previewColumns: {
|
||||||
|
|
@ -26,7 +27,7 @@ export const previewType = {
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
beforePreviewData: {
|
beforePreviewData: {
|
||||||
type: Function as PropType<(arg: string[]) => Recordable<any>>,
|
type: Function as PropType<(arg: BaseFileItem[] | any) => Recordable<any>>,
|
||||||
default: null,
|
default: null,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
|
|
@ -112,7 +113,7 @@ export const uploadContainerProps = {
|
||||||
|
|
||||||
export const previewProps = {
|
export const previewProps = {
|
||||||
value: {
|
value: {
|
||||||
type: Array as PropType<string[]>,
|
type: Array as PropType<BaseFileItem[] | any[]>,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
maxNumber: {
|
maxNumber: {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,11 @@ export interface FileItem {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BaseFileItem {
|
||||||
|
uid: string | number;
|
||||||
|
url: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
export interface PreviewFileItem {
|
export interface PreviewFileItem {
|
||||||
url: string;
|
url: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
||||||
|
|
@ -106,10 +106,14 @@
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
style: 'margin:4px',
|
style: 'margin:4px',
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
handleAdd(
|
handleAdd({
|
||||||
{ url6: 'https://vebn.oss-cn-beijing.aliyuncs.com/vben/logo.png' },
|
record: {
|
||||||
'url6',
|
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,
|
danger: true,
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
handleRemove({ url6: record.url6 }, 'url6');
|
handleRemove({
|
||||||
|
record: { url6: record.url6 },
|
||||||
|
uidKey: 'url6',
|
||||||
|
valueKey: 'url6',
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
() => '点我删除',
|
() => '点我删除',
|
||||||
|
|
@ -133,14 +141,15 @@
|
||||||
let data = arg
|
let data = arg
|
||||||
.filter((item) => !!item)
|
.filter((item) => !!item)
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
if (typeof item !== 'string') {
|
if (typeof item !== 'object') {
|
||||||
console.error('return value should be string');
|
console.error('return value should be object');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
url6: item,
|
uid: item?.uid,
|
||||||
type6: item.split('.').pop() || '',
|
url6: item?.url,
|
||||||
name6: item.split('/').pop() || '',
|
type6: item?.url?.split('.').pop() || '',
|
||||||
|
name6: item?.url?.split('/').pop() || '',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue