feat(upload->previewColumns): Adapt functions && chore upload demo (#3799)

* feat(upload->previewColumns): add function capability with delete fileList

* chore(upload->demo): split file && add previewColumns fn demo

* type(upload): optional  handleRemove

* type(upload): remove  optional  handleRemove

* feat(upload->previewCol): add enhanceing  handleAdd && handleDownload

* chore(upload->preview): remove  previewColumns
This commit is contained in:
Electrolux 2024-04-30 06:54:30 +08:00 committed by GitHub
parent 17f5b1b210
commit 29ef0d3915
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 377 additions and 228 deletions

View File

@ -36,6 +36,7 @@
<UploadPreviewModal
:value="fileList"
:max-number="bindValue.maxNumber"
@register="registerPreviewModal"
@list-change="handlePreviewChange"
@delete="handlePreviewDelete"

View File

@ -21,6 +21,9 @@
import { useI18n } from '@/hooks/web/useI18n';
import { isArray } from '@/utils/is';
import { BasicColumn } from '@/components/Table';
import { isFunction } from '@/utils/is';
import { useMessage } from '@/hooks/web/useMessage';
const { createMessage } = useMessage();
const props = defineProps(previewProps);
@ -36,9 +39,11 @@
watch(
() => props.previewColumns,
() => {
if (props.previewColumns.length) {
if (Array.isArray(props.previewColumns) && props.previewColumns.length) {
columns = props.previewColumns;
actionColumn = null;
} else if (isFunction(props.previewColumns)) {
columns = props.previewColumns({ handleRemove, handleAdd });
} else {
columns = createPreviewColumns();
actionColumn = createPreviewActionColumn({ handleRemove, handleDownload });
@ -74,18 +79,29 @@
);
//
function handleRemove(record: PreviewFileItem) {
const index = fileListRef.value.findIndex((item) => item.url === record.url);
function handleRemove(record: PreviewFileItem | Record<string, any>, urlKey = 'url') {
const index = fileListRef.value.findIndex((item) => item[urlKey] === record[urlKey]);
if (index !== -1) {
const removed = fileListRef.value.splice(index, 1);
emit('delete', removed[0].url);
emit('delete', removed[0][urlKey]);
emit(
'list-change',
fileListRef.value.map((item) => item.url),
fileListRef.value.map((item) => item[urlKey]),
);
}
}
//
function handleAdd(record: PreviewFileItem | Record<string, any>, urlKey = 'url') {
const { maxNumber } = props;
if (fileListRef.value.length + fileListRef.value.length > maxNumber) {
return createMessage.warning(t('component.upload.maxNumber', [maxNumber]));
}
fileListRef.value = [...fileListRef.value, record];
emit(
'list-change',
fileListRef.value.map((item) => item[urlKey]),
);
}
//
function handleDownload(record: PreviewFileItem) {
const { url = '' } = record;

View File

@ -14,11 +14,13 @@ type SortableOptions = Merge<
// ...可扩展
}
>;
type previewColumnsFnType = {
handleRemove:(record:Record<string,any>,key:string)=>any,
handleAdd:(record:Record<string,any>,key:string)=>any,
}
export const previewType = {
previewColumns: {
type: Array as PropType<BasicColumn[] | FileBasicColumn[]>,
default: [],
type: [Array,Function] as PropType<BasicColumn[] | ((arg:previewColumnsFnType) => BasicColumn[])>,
required: false,
},
beforePreviewData: {
@ -111,6 +113,10 @@ export const previewProps = {
type: Array as PropType<string[]>,
default: () => [],
},
maxNumber: {
type: Number as PropType<number>,
default: 1,
},
...previewType,
};

View File

@ -0,0 +1,28 @@
<template>
<Alert message="基础示例" />
<BasicUpload
:maxSize="20"
:maxNumber="10"
@change="handleChange"
:api="uploadApi"
class="my-5"
:accept="['image/*']"
/>
</template>
<script setup lang="ts">
import { BasicUpload } from '@/components/Upload';
import { uploadApi } from '@/api/sys/upload';
import { useMessage } from '@/hooks/web/useMessage';
const { createMessage } = useMessage();
import { Alert } from 'ant-design-vue';
function handleChange(list: string[]) {
createMessage.success(`已上传文件${JSON.stringify(list)}`);
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,56 @@
<template>
<Alert message="嵌入表单,加入表单校验" />
<BasicForm @register="registerValiate" class="my-5" />
</template>
<script setup lang="ts">
import { uploadApi } from '@/api/sys/upload';
import { useMessage } from '@/hooks/web/useMessage';
const { createMessage } = useMessage();
import { BasicForm, FormSchema, useForm } from '@/components/Form';
import { Alert } from 'ant-design-vue';
const schemasValiate: FormSchema[] = [
{
field: 'field1',
component: 'Upload',
label: '字段1',
rules: [{ required: true, message: '请选择上传文件' }],
componentProps: {
api: uploadApi,
},
},
{
field: 'field2',
component: 'ImageUpload',
label: '字段2(ImageUpload)',
colProps: {
span: 8,
},
componentProps: {
api: uploadApi,
},
},
];
const [registerValiate, { getFieldsValue: getFieldsValueValiate, validate }] = useForm({
labelWidth: 160,
schemas: schemasValiate,
actionColOptions: {
span: 18,
},
submitFunc: () => {
return new Promise((resolve) => {
validate()
.then(() => {
resolve();
console.log(getFieldsValueValiate());
createMessage.success(`请到控制台查看结果`);
})
.catch(() => {
createMessage.error(`请输入必填项`);
});
});
},
});
</script>
<style scoped></style>

View File

@ -0,0 +1,73 @@
<template>
<Alert message="嵌入表单,加入resultFiled自定义返回值" />
<BasicForm @register="registerCustom" class="my-5" />
</template>
<script setup lang="ts">
import { uploadApi } from '@/api/sys/upload';
import { useMessage } from '@/hooks/web/useMessage';
const { createMessage } = useMessage();
import { BasicForm, FormSchema, useForm } from '@/components/Form';
import { Alert } from 'ant-design-vue';
const schemasCustom: FormSchema[] = [
{
field: 'field3',
component: 'Upload',
label: '字段3',
componentProps: {
resultField: 'data3.url',
api: (file, progress) => {
return new Promise((resolve) => {
uploadApi(file, progress).then((uploadApiResponse) => {
resolve({
code: 200,
data3: {
url: uploadApiResponse.data.url,
},
});
});
});
},
},
},
{
field: 'field4',
component: 'ImageUpload',
label: '字段4(ImageUpload)',
colProps: {
span: 8,
},
componentProps: {
resultField: 'data4.url',
api: (file, progress) => {
return new Promise((resolve) => {
uploadApi(file, progress).then((uploadApiResponse) => {
resolve({
code: 200,
data4: {
url: uploadApiResponse.data.url,
},
});
});
});
},
},
},
];
const [registerCustom, { getFieldsValue: getFieldsValueCustom }] = useForm({
labelWidth: 160,
schemas: schemasCustom,
actionColOptions: {
span: 18,
},
submitFunc: () => {
return new Promise((resolve) => {
console.log(getFieldsValueCustom());
resolve();
createMessage.success(`请到控制台查看结果`);
});
},
});
</script>
<style scoped></style>

View File

@ -0,0 +1,180 @@
<template>
<Alert message="嵌入表单,自定义预览内容" />
<BasicForm @register="registerPreview" class="my-5" />
</template>
<script setup lang="ts">
import { uploadApi } from '@/api/sys/upload';
import { useMessage } from '@/hooks/web/useMessage';
const { createMessage } = useMessage();
import { BasicForm, FormSchema, useForm } from '@/components/Form';
import { Alert, Button } from 'ant-design-vue';
import { createVNode } from 'vue';
const schemasPreview: FormSchema[] = [
{
field: 'field5',
component: 'Upload',
label: '字段5',
componentProps: {
previewColumns: [
{
title: 'url5',
dataIndex: 'url5',
},
{
title: 'type5',
dataIndex: 'type5',
},
{
title: 'name5',
dataIndex: 'name5',
},
{
title: 'operation',
dataIndex: '',
customRender: ({ record }) => {
return createVNode(
Button,
{
onclick: () => {
console.log(record);
createMessage.success(`请到控制台查看该行输出结果`);
},
},
() => '点我输出该行信息',
);
},
},
],
beforePreviewData: (arg) => {
let data = arg
.filter((item) => !!item)
.map((item) => {
if (typeof item !== 'string') {
console.error('return value should be string');
return;
}
return {
url5: item,
type5: item.split('.').pop() || '',
name5: item.split('/').pop() || '',
};
});
return data;
},
resultField: 'data5.url',
api: (file, progress) => {
return new Promise((resolve) => {
uploadApi(file, progress).then((uploadApiResponse) => {
resolve({
code: 200,
data5: {
url: uploadApiResponse.data.url,
},
});
});
});
},
},
},
{
field: 'field6',
component: 'Upload',
label: '字段6',
componentProps: {
maxNumber:2,
previewColumns: ({ handleRemove, handleAdd}) => {
return [
{
title: 'url6',
dataIndex: 'url6',
},
{
title: 'type6',
dataIndex: 'type6',
},
{
title: '操作1',
dataIndex: 'operation',
customRender: ({ record }) => {
return createVNode('div', {}, [
createVNode(
Button,
{
type:"primary",
style:"margin:4px",
onclick: () => {
handleAdd(
{ url6: 'https://vebn.oss-cn-beijing.aliyuncs.com/vben/logo.png' },
'url6',
);
},
},
() => '点我新增',
),
createVNode(
Button,
{
danger:true,
onclick: () => {
handleRemove({ url6: record.url6 }, 'url6');
},
},
() => '点我删除',
),
]);
},
},
];
},
beforePreviewData: (arg) => {
let data = arg
.filter((item) => !!item)
.map((item) => {
if (typeof item !== 'string') {
console.error('return value should be string');
return;
}
return {
url6: item,
type6: item.split('.').pop() || '',
name6: item.split('/').pop() || '',
};
});
return data;
},
resultField: 'data6.url',
api: (file, progress) => {
return new Promise((resolve) => {
uploadApi(file, progress).then((uploadApiResponse) => {
resolve({
code: 200,
data6: {
url: uploadApiResponse.data.url,
},
});
});
});
},
},
},
];
const [registerPreview, { getFieldsValue: getFieldsValuePreview }] = useForm({
labelWidth: 160,
schemas: schemasPreview,
actionColOptions: {
span: 18,
},
submitFunc: () => {
return new Promise((resolve) => {
console.log(getFieldsValuePreview());
resolve();
createMessage.success(`请到控制台查看结果`);
});
},
});
</script>
<style scoped></style>

View File

@ -1,226 +1,15 @@
<template>
<PageWrapper title="上传组件示例">
<Alert message="基础示例" />
<BasicUpload
:maxSize="20"
:maxNumber="10"
@change="handleChange"
:api="uploadApi"
class="my-5"
:accept="['image/*']"
/>
<Alert message="嵌入表单,加入表单校验" />
<BasicForm @register="registerValiate" class="my-5" />
<Alert message="嵌入表单,加入resultFiled自定义返回值" />
<BasicForm @register="registerCustom" class="my-5" />
<Alert message="嵌入表单,自定义预览内容" />
<BasicForm @register="registerPreview" class="my-5" />
<Upload1></Upload1>
<Upload2></Upload2>
<Upload3></Upload3>
<Upload4></Upload4>
</PageWrapper>
</template>
<script lang="ts" setup>
import { BasicUpload } from '@/components/Upload';
import { useMessage } from '@/hooks/web/useMessage';
import { BasicForm, FormSchema, useForm } from '@/components/Form';
import Upload1 from './Upload1.vue';
import Upload2 from './Upload2.vue';
import Upload3 from './Upload3.vue';
import Upload4 from './Upload4.vue';
import { PageWrapper } from '@/components/Page';
import { Alert, Button } from 'ant-design-vue';
import { uploadApi } from '@/api/sys/upload';
import { createVNode } from 'vue';
const schemasValiate: FormSchema[] = [
{
field: 'field1',
component: 'Upload',
label: '字段1',
rules: [{ required: true, message: '请选择上传文件' }],
componentProps: {
api: uploadApi,
},
},
{
field: 'field2',
component: 'ImageUpload',
label: '字段2(ImageUpload)',
colProps: {
span: 8,
},
componentProps: {
api: uploadApi,
},
},
];
const schemasCustom: FormSchema[] = [
{
field: 'field3',
component: 'Upload',
label: '字段3',
componentProps: {
resultField: 'data3.url',
api: (file, progress) => {
return new Promise((resolve) => {
uploadApi(file, progress).then((uploadApiResponse) => {
resolve({
code: 200,
data3: {
url: uploadApiResponse.data.url,
},
});
});
});
},
},
},
{
field: 'field4',
component: 'ImageUpload',
label: '字段4(ImageUpload)',
colProps: {
span: 8,
},
componentProps: {
resultField: 'data4.url',
api: (file, progress) => {
return new Promise((resolve) => {
uploadApi(file, progress).then((uploadApiResponse) => {
resolve({
code: 200,
data4: {
url: uploadApiResponse.data.url,
},
});
});
});
},
},
},
];
const schemasPreview: FormSchema[] = [
{
field: 'field5',
component: 'Upload',
label: '字段5',
componentProps: {
previewColumns: [
{
title: 'url5',
dataIndex: 'url5',
},
{
title: 'type5',
dataIndex: 'type5',
},
{
title: 'name5',
dataIndex: 'name5',
},
{
title: 'operation',
dataIndex: '',
customRender: ({ record }) => {
return createVNode(
Button,
{
onclick: () => {
console.log(record);
createMessage.success(`请到控制台查看该行输出结果`);
},
},
'点我',
);
},
},
],
beforePreviewData: (arg) => {
let data = arg
.filter((item) => !!item)
.map((item) => {
if (typeof item !== 'string') {
console.error('return value should be string');
return;
}
return {
url5: item,
type5: item.split('.').pop() || '',
name5: item.split('/').pop() || '',
};
});
return data;
},
resultField: 'data5.url',
api: (file, progress) => {
return new Promise((resolve) => {
uploadApi(file, progress).then((uploadApiResponse) => {
resolve({
code: 200,
data5: {
url: uploadApiResponse.data.url,
},
});
});
});
},
},
},
];
const { createMessage } = useMessage();
function handleChange(list: string[]) {
createMessage.success(`已上传文件${JSON.stringify(list)}`);
}
const [registerValiate, { getFieldsValue: getFieldsValueValiate, validate }] = useForm({
labelWidth: 160,
schemas: schemasValiate,
actionColOptions: {
span: 18,
},
submitFunc: () => {
return new Promise((resolve) => {
validate()
.then(() => {
resolve();
console.log(getFieldsValueValiate());
createMessage.success(`请到控制台查看结果`);
})
.catch(() => {
createMessage.error(`请输入必填项`);
});
});
},
});
// resultFields
const [registerCustom, { getFieldsValue: getFieldsValueCustom }] = useForm({
labelWidth: 160,
schemas: schemasCustom,
actionColOptions: {
span: 18,
},
submitFunc: () => {
return new Promise((resolve) => {
console.log(getFieldsValueCustom());
resolve();
createMessage.success(`请到控制台查看结果`);
});
},
});
// registerPreview
const [registerPreview, { getFieldsValue: getFieldsValuePreview }] = useForm({
labelWidth: 160,
schemas: schemasPreview,
actionColOptions: {
span: 18,
},
submitFunc: () => {
return new Promise((resolve) => {
console.log(getFieldsValuePreview());
resolve();
createMessage.success(`请到控制台查看结果`);
});
},
});
</script>