feat: add input upload
parent
3c4f954b77
commit
ba18eb37da
|
@ -2,7 +2,7 @@
|
|||
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
||||
import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||
|
||||
import type { AxiosResponse } from '@vben/request';
|
||||
import type { FileUploadProps } from './typing';
|
||||
|
||||
import type { AxiosProgressEvent } from '#/api/infra/file';
|
||||
|
||||
|
@ -20,44 +20,19 @@ import { useUpload, useUploadType } from './use-upload';
|
|||
|
||||
defineOptions({ name: 'FileUpload', inheritAttrs: false });
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 根据后缀,或者其他
|
||||
accept?: string[];
|
||||
api?: (
|
||||
file: File,
|
||||
onUploadProgress?: AxiosProgressEvent,
|
||||
) => Promise<AxiosResponse<any>>;
|
||||
// 上传的目录
|
||||
directory?: string;
|
||||
disabled?: boolean;
|
||||
helpText?: string;
|
||||
// 最大数量的文件,Infinity不限制
|
||||
maxNumber?: number;
|
||||
// 文件最大多少MB
|
||||
maxSize?: number;
|
||||
// 是否支持多选
|
||||
multiple?: boolean;
|
||||
// support xxx.xxx.xx
|
||||
resultField?: string;
|
||||
// 是否显示下面的描述
|
||||
showDescription?: boolean;
|
||||
value?: string | string[];
|
||||
}>(),
|
||||
{
|
||||
value: () => [],
|
||||
directory: undefined,
|
||||
disabled: false,
|
||||
helpText: '',
|
||||
maxSize: 2,
|
||||
maxNumber: 1,
|
||||
accept: () => [],
|
||||
multiple: false,
|
||||
api: undefined,
|
||||
resultField: '',
|
||||
showDescription: false,
|
||||
},
|
||||
);
|
||||
const props = withDefaults(defineProps<FileUploadProps>(), {
|
||||
value: () => [],
|
||||
directory: undefined,
|
||||
disabled: false,
|
||||
helpText: '',
|
||||
maxSize: 2,
|
||||
maxNumber: 1,
|
||||
accept: () => [],
|
||||
multiple: false,
|
||||
api: undefined,
|
||||
resultField: '',
|
||||
showDescription: false,
|
||||
});
|
||||
const emit = defineEmits(['change', 'update:value', 'delete', 'returnText']);
|
||||
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
||||
const isInnerOperate = ref<boolean>(false);
|
||||
|
@ -112,7 +87,7 @@ watch(
|
|||
},
|
||||
);
|
||||
|
||||
const handleRemove = async (file: UploadFile) => {
|
||||
async function handleRemove(file: UploadFile) {
|
||||
if (fileList.value) {
|
||||
const index = fileList.value.findIndex((item) => item.uid === file.uid);
|
||||
index !== -1 && fileList.value.splice(index, 1);
|
||||
|
@ -122,9 +97,9 @@ const handleRemove = async (file: UploadFile) => {
|
|||
emit('change', value);
|
||||
emit('delete', file);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const beforeUpload = async (file: File) => {
|
||||
async function beforeUpload(file: File) {
|
||||
// 使用现代的Blob.text()方法替代FileReader
|
||||
const fileContent = await file.text();
|
||||
emit('returnText', fileContent);
|
||||
|
@ -145,7 +120,7 @@ const beforeUpload = async (file: File) => {
|
|||
setTimeout(() => (isLtMsg.value = true), 1000);
|
||||
}
|
||||
return (isAct && !isLt) || Upload.LIST_IGNORE;
|
||||
};
|
||||
}
|
||||
|
||||
async function customRequest(info: UploadRequestOption<any>) {
|
||||
let { api } = props;
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* 默认图片类型
|
||||
*/
|
||||
export const defaultImageAccepts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||
|
||||
export function checkFileType(file: File, accepts: string[]) {
|
||||
if (!accepts || accepts.length === 0) {
|
||||
return true;
|
||||
|
@ -7,11 +12,6 @@ export function checkFileType(file: File, accepts: string[]) {
|
|||
return reg.test(file.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认图片类型
|
||||
*/
|
||||
export const defaultImageAccepts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||
|
||||
export function checkImgType(
|
||||
file: File,
|
||||
accepts: string[] = defaultImageAccepts,
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
||||
import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||
|
||||
import type { AxiosResponse } from '@vben/request';
|
||||
|
||||
import type { UploadListType } from './typing';
|
||||
import type { FileUploadProps } from './typing';
|
||||
|
||||
import type { AxiosProgressEvent } from '#/api/infra/file';
|
||||
|
||||
|
@ -22,46 +20,20 @@ import { useUpload, useUploadType } from './use-upload';
|
|||
|
||||
defineOptions({ name: 'ImageUpload', inheritAttrs: false });
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 根据后缀,或者其他
|
||||
accept?: string[];
|
||||
api?: (
|
||||
file: File,
|
||||
onUploadProgress?: AxiosProgressEvent,
|
||||
) => Promise<AxiosResponse<any>>;
|
||||
// 上传的目录
|
||||
directory?: string;
|
||||
disabled?: boolean;
|
||||
helpText?: string;
|
||||
listType?: UploadListType;
|
||||
// 最大数量的文件,Infinity不限制
|
||||
maxNumber?: number;
|
||||
// 文件最大多少MB
|
||||
maxSize?: number;
|
||||
// 是否支持多选
|
||||
multiple?: boolean;
|
||||
// support xxx.xxx.xx
|
||||
resultField?: string;
|
||||
// 是否显示下面的描述
|
||||
showDescription?: boolean;
|
||||
value?: string | string[];
|
||||
}>(),
|
||||
{
|
||||
value: () => [],
|
||||
directory: undefined,
|
||||
disabled: false,
|
||||
listType: 'picture-card',
|
||||
helpText: '',
|
||||
maxSize: 2,
|
||||
maxNumber: 1,
|
||||
accept: () => defaultImageAccepts,
|
||||
multiple: false,
|
||||
api: undefined,
|
||||
resultField: '',
|
||||
showDescription: true,
|
||||
},
|
||||
);
|
||||
const props = withDefaults(defineProps<FileUploadProps>(), {
|
||||
value: () => [],
|
||||
directory: undefined,
|
||||
disabled: false,
|
||||
listType: 'picture-card',
|
||||
helpText: '',
|
||||
maxSize: 2,
|
||||
maxNumber: 1,
|
||||
accept: () => defaultImageAccepts,
|
||||
multiple: false,
|
||||
api: undefined,
|
||||
resultField: '',
|
||||
showDescription: true,
|
||||
});
|
||||
const emit = defineEmits(['change', 'update:value', 'delete']);
|
||||
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
||||
const isInnerOperate = ref<boolean>(false);
|
||||
|
@ -130,7 +102,7 @@ function getBase64<T extends ArrayBuffer | null | string>(file: File) {
|
|||
});
|
||||
}
|
||||
|
||||
const handlePreview = async (file: UploadFile) => {
|
||||
async function handlePreview(file: UploadFile) {
|
||||
if (!file.url && !file.preview) {
|
||||
file.preview = await getBase64<string>(file.originFileObj!);
|
||||
}
|
||||
|
@ -141,9 +113,9 @@ const handlePreview = async (file: UploadFile) => {
|
|||
previewImage.value.slice(
|
||||
Math.max(0, previewImage.value.lastIndexOf('/') + 1),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const handleRemove = async (file: UploadFile) => {
|
||||
async function handleRemove(file: UploadFile) {
|
||||
if (fileList.value) {
|
||||
const index = fileList.value.findIndex((item) => item.uid === file.uid);
|
||||
index !== -1 && fileList.value.splice(index, 1);
|
||||
|
@ -153,14 +125,14 @@ const handleRemove = async (file: UploadFile) => {
|
|||
emit('change', value);
|
||||
emit('delete', file);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
function handleCancel() {
|
||||
previewOpen.value = false;
|
||||
previewTitle.value = '';
|
||||
};
|
||||
}
|
||||
|
||||
const beforeUpload = async (file: File) => {
|
||||
async function beforeUpload(file: File) {
|
||||
const { maxSize, accept } = props;
|
||||
const isAct = checkImgType(file, accept);
|
||||
if (!isAct) {
|
||||
|
@ -177,7 +149,7 @@ const beforeUpload = async (file: File) => {
|
|||
setTimeout(() => (isLtMsg.value = true), 1000);
|
||||
}
|
||||
return (isAct && !isLt) || Upload.LIST_IGNORE;
|
||||
};
|
||||
}
|
||||
|
||||
async function customRequest(info: UploadRequestOption<any>) {
|
||||
let { api } = props;
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export { default as FileUpload } from './file-upload.vue';
|
||||
export { default as ImageUpload } from './image-upload.vue';
|
||||
export { default as InputUpload } from './input-upload.vue';
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<script setup lang="ts">
|
||||
import type { InputProps, TextAreaProps } from 'ant-design-vue';
|
||||
|
||||
import type { FileUploadProps } from './typing';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { Col, Input, Row, Textarea } from 'ant-design-vue';
|
||||
|
||||
import FileUpload from './file-upload.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
fileUploadProps?: FileUploadProps;
|
||||
inputProps?: InputProps;
|
||||
inputType?: 'input' | 'textarea';
|
||||
textareaProps?: TextAreaProps;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['change', 'update:value']);
|
||||
|
||||
const value = ref('');
|
||||
|
||||
function handleReturnText(text: string) {
|
||||
value.value = text;
|
||||
emit('change', value.value);
|
||||
emit('update:value', value.value);
|
||||
}
|
||||
|
||||
const inputProps = computed(() => {
|
||||
return {
|
||||
...props.inputProps,
|
||||
value: value.value,
|
||||
};
|
||||
});
|
||||
|
||||
const textareaProps = computed(() => {
|
||||
return {
|
||||
...props.textareaProps,
|
||||
value: value.value,
|
||||
};
|
||||
});
|
||||
|
||||
const fileUploadProps = computed(() => {
|
||||
return {
|
||||
...props.fileUploadProps,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Row>
|
||||
<Col :span="18">
|
||||
<Input v-if="inputType === 'input'" v-bind="inputProps" />
|
||||
<Textarea v-else :row="4" v-bind="textareaProps" />
|
||||
</Col>
|
||||
<Col :span="6">
|
||||
<FileUpload
|
||||
class="ml-4"
|
||||
v-bind="fileUploadProps"
|
||||
@return-text="handleReturnText"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</template>
|
|
@ -1,3 +1,7 @@
|
|||
import type { AxiosResponse } from '@vben/request';
|
||||
|
||||
import type { AxiosProgressEvent } from '#/api/infra/file';
|
||||
|
||||
export enum UploadResultStatus {
|
||||
DONE = 'done',
|
||||
ERROR = 'error',
|
||||
|
@ -6,3 +10,28 @@ export enum UploadResultStatus {
|
|||
}
|
||||
|
||||
export type UploadListType = 'picture' | 'picture-card' | 'text';
|
||||
|
||||
export interface FileUploadProps {
|
||||
// 根据后缀,或者其他
|
||||
accept?: string[];
|
||||
api?: (
|
||||
file: File,
|
||||
onUploadProgress?: AxiosProgressEvent,
|
||||
) => Promise<AxiosResponse<any>>;
|
||||
// 上传的目录
|
||||
directory?: string;
|
||||
disabled?: boolean;
|
||||
helpText?: string;
|
||||
listType?: UploadListType;
|
||||
// 最大数量的文件,Infinity不限制
|
||||
maxNumber?: number;
|
||||
// 文件最大多少MB
|
||||
maxSize?: number;
|
||||
// 是否支持多选
|
||||
multiple?: boolean;
|
||||
// support xxx.xxx.xx
|
||||
resultField?: string;
|
||||
// 是否显示下面的描述
|
||||
showDescription?: boolean;
|
||||
value?: string | string[];
|
||||
}
|
||||
|
|
|
@ -80,17 +80,17 @@ export function useUploadType({
|
|||
}
|
||||
|
||||
// TODO @芋艿:目前保持和 admin-vue3 一致,后续可能重构
|
||||
export const useUpload = (directory?: string) => {
|
||||
export function useUpload(directory?: string) {
|
||||
// 后端上传地址
|
||||
const uploadUrl = getUploadUrl();
|
||||
// 是否使用前端直连上传
|
||||
const isClientUpload =
|
||||
UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE;
|
||||
// 重写ElUpload上传方法
|
||||
const httpRequest = async (
|
||||
async function httpRequest(
|
||||
file: File,
|
||||
onUploadProgress?: AxiosProgressEvent,
|
||||
) => {
|
||||
) {
|
||||
// 模式一:前端上传
|
||||
if (isClientUpload) {
|
||||
// 1.1 生成文件名称
|
||||
|
@ -114,20 +114,20 @@ export const useUpload = (directory?: string) => {
|
|||
// 模式二:后端上传
|
||||
return uploadFile({ file, directory }, onUploadProgress);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
uploadUrl,
|
||||
httpRequest,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得上传 URL
|
||||
*/
|
||||
export const getUploadUrl = (): string => {
|
||||
export function getUploadUrl(): string {
|
||||
return `${apiURL}/infra/file/upload`;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件信息
|
||||
|
@ -135,7 +135,10 @@ export const getUploadUrl = (): string => {
|
|||
* @param vo 文件预签名信息
|
||||
* @param file 文件
|
||||
*/
|
||||
function createFile0(vo: InfraFileApi.FilePresignedUrlRespVO, file: File) {
|
||||
function createFile0(
|
||||
vo: InfraFileApi.FilePresignedUrlRespVO,
|
||||
file: File,
|
||||
): InfraFileApi.File {
|
||||
const fileVO = {
|
||||
configId: vo.configId,
|
||||
url: vo.url,
|
||||
|
|
Loading…
Reference in New Issue