feat: 新增 支付管理 - 应用信息模块

This commit is contained in:
吃货
2025-07-05 00:55:24 +08:00
parent e0080bb3e0
commit 4562d204e0
9 changed files with 1637 additions and 15 deletions

View File

@@ -3,12 +3,11 @@ import type {
UploadFile,
UploadProgressEvent,
UploadRequestOptions,
UploadUserFile,
} from 'element-plus';
import type { AxiosResponse } from '@vben/request';
import type { CustomUploadFile } from './typing';
import type { AxiosProgressEvent } from '#/api/infra/file';
import { ref, toRefs, watch } from 'vue';
@@ -63,7 +62,7 @@ const props = withDefaults(
showDescription: false,
},
);
const emit = defineEmits(['change', 'update:value', 'delete']);
const emit = defineEmits(['change', 'update:value', 'delete', 'returnText']);
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
const isInnerOperate = ref<boolean>(false);
const { getStringAccept } = useUploadType({
@@ -73,7 +72,7 @@ const { getStringAccept } = useUploadType({
maxSizeRef: maxSize,
});
const fileList = ref<CustomUploadFile[]>([]);
const fileList = ref<UploadUserFile[]>([]);
const isLtMsg = ref<boolean>(true); // 文件大小错误提示
const isActMsg = ref<boolean>(true); // 文件类型错误提示
const isFirstRender = ref<boolean>(true); // 是否第一次渲染
@@ -100,7 +99,7 @@ watch(
name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)),
status: UploadResultStatus.DONE,
url: item,
} as CustomUploadFile;
} as UploadUserFile;
} else if (item && isObject(item)) {
const file = item as Record<string, any>;
return {
@@ -111,11 +110,11 @@ watch(
response: file.response,
percentage: file.percentage,
size: file.size,
} as CustomUploadFile;
} as UploadUserFile;
}
return null;
})
.filter(Boolean) as CustomUploadFile[];
.filter(Boolean) as UploadUserFile[];
}
if (!isFirstRender.value) {
emit('change', value);
@@ -141,6 +140,8 @@ const handleRemove = async (file: UploadFile) => {
};
const beforeUpload = async (file: File) => {
const fileContent = await file.text();
emit('returnText', fileContent);
const { maxSize, accept } = props;
const isAct = checkFileType(file, accept);
if (!isAct) {
@@ -175,17 +176,17 @@ async function customRequest(options: UploadRequestOptions) {
total: e.total || 0,
loaded: e.loaded || 0,
lengthComputable: true,
target: e.target as EventTarget,
target: e.event.target as EventTarget,
bubbles: false,
cancelBubble: false,
cancelable: false,
composed: false,
currentTarget: e.target as EventTarget,
currentTarget: e.event.target as EventTarget,
defaultPrevented: false,
eventPhase: 0,
isTrusted: true,
returnValue: true,
srcElement: e.target as EventTarget,
srcElement: e.event.target as EventTarget,
timeStamp: Date.now(),
type: 'progress',
composedPath: () => [],
@@ -193,6 +194,10 @@ async function customRequest(options: UploadRequestOptions) {
preventDefault: () => {},
stopImmediatePropagation: () => {},
stopPropagation: () => {},
NONE: 0,
CAPTURING_PHASE: 1,
AT_TARGET: 2,
BUBBLING_PHASE: 3,
};
options.onProgress!(progressEvent);
};

View File

@@ -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';

View File

@@ -0,0 +1,75 @@
<script setup lang="ts">
// TODO @xingyu这个组件只有 pay 在用,和现有的 file-upload 和 image-upload 有点不一致。是不是可以考虑移除,只在 pay 那搞个复用的组件;
import type { InputProps } from 'element-plus';
import type { FileUploadProps } from './typing';
import { computed } from 'vue';
import { useVModel } from '@vueuse/core';
import { ElCol, ElInput, ElRow } from 'element-plus';
import FileUpload from './file-upload.vue';
const props = defineProps<{
defaultValue?: number | string;
fileUploadProps?: FileUploadProps;
inputProps?: InputProps;
inputType?: 'input' | 'textarea';
modelValue?: number | string;
textareaProps?: InputProps;
}>();
const emits = defineEmits<{
(e: 'change', payload: number | string): void;
(e: 'update:value', payload: number | string): void;
(e: 'update:modelValue', payload: number | string): void;
}>();
const modelValue = useVModel(props, 'modelValue', emits, {
defaultValue: props.defaultValue,
passive: true,
});
function handleReturnText(text: string) {
modelValue.value = text;
emits('change', modelValue.value);
emits('update:value', modelValue.value);
emits('update:modelValue', modelValue.value);
}
const inputProps = computed(() => {
return {
...props.inputProps,
value: modelValue.value,
};
});
const textareaProps = computed(() => {
return {
...props.textareaProps,
value: modelValue.value,
};
});
const fileUploadProps = computed(() => {
return {
...props.fileUploadProps,
};
});
</script>
<template>
<ElRow>
<ElCol :span="18">
<ElInput v-if="inputType === 'input'" v-bind="inputProps" />
<ElInput v-else :row="4" type="textarea" v-bind="textareaProps" />
</ElCol>
<ElCol :span="6">
<FileUpload
class="ml-4"
v-bind="fileUploadProps"
@return-text="handleReturnText"
/>
</ElCol>
</ElRow>
</template>

View File

@@ -1,11 +1,17 @@
import type { UploadStatus } from 'element-plus';
import type { AxiosResponse } from '@vben/request';
import type { AxiosProgressEvent } from '#/api/infra/file';
export type UploadListType = 'picture' | 'picture-card' | 'text';
export type UploadStatus = 'error' | 'removed' | 'success' | 'uploading';
export type UploadStatus =
| 'error'
| 'fail'
| 'removed'
| 'success'
| 'uploading';
export enum UploadResultStatus {
DONE = 'success',
ERROR = 'error',
REMOVED = 'removed',
SUCCESS = 'success',
@@ -27,11 +33,11 @@ export function convertToUploadStatus(
status: UploadResultStatus,
): UploadStatus {
switch (status) {
case UploadResultStatus.DONE: {
case UploadResultStatus.SUCCESS: {
return 'success';
}
case UploadResultStatus.ERROR: {
return 'error';
return 'fail';
}
case UploadResultStatus.REMOVED: {
return 'removed';
@@ -44,3 +50,28 @@ export function convertToUploadStatus(
}
}
}
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[];
}