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