批量上传优化.

This commit is contained in:
lijiahangmax
2024-05-11 00:16:42 +08:00
parent 0a43e5db45
commit 978d94dddf
21 changed files with 274 additions and 111 deletions

View File

@@ -53,6 +53,8 @@ export interface UploadTaskQueryResponse extends TableData {
description: string;
status: string;
extraInfo: string;
fileCount: number;
hostCount: number;
startTime: number;
endTime: number;
createTime: number;
@@ -86,6 +88,17 @@ export interface UploadTaskFile {
current: number;
}
/**
* 上传任务状态响应
*/
export interface UploadTaskStatusResponse extends TableData {
id: number;
status: string;
startTime: number;
endTime: number;
files: Array<UploadTaskFile>;
}
/**
* 创建上传任务
*/
@@ -125,7 +138,7 @@ export function getUploadTaskPage(request: UploadTaskQueryRequest) {
* 查询上传任务状态
*/
export function getUploadTaskStatus(idList: Array<number>, queryFiles: boolean) {
return axios.get<Array<UploadTaskQueryResponse>>('/asset/upload-task/status', {
return axios.get<Array<UploadTaskStatusResponse>>('/asset/upload-task/status', {
params: { idList, queryFiles },
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'comma' });

View File

@@ -41,14 +41,10 @@
<template #file-name="{ fileItem }">
<div class="file-name-wrapper">
<!-- 文件名称 -->
<a-tooltip position="left"
:mini="true"
:content="fileItem.file.webkitRelativePath || fileItem.file.name">
<!-- 文件名称 -->
<span class="file-name text-ellipsis">
<span class="file-name text-ellipsis"
:title="fileItem.file.webkitRelativePath || fileItem.file.name">
{{ fileItem.file.webkitRelativePath || fileItem.file.name }}
</span>
</a-tooltip>
<!-- 文件大小 -->
<span class="file-size span-blue">
{{ getFileSize(fileItem.file.size) }}
@@ -144,7 +140,7 @@
:deep(.waiting-files-wrapper) {
.arco-upload-list {
padding: 0 6px 0 0 !important;
padding: 0 12px 0 0 !important;
}
.arco-upload-list-item-name {
@@ -205,16 +201,18 @@
justify-content: space-between;
.file-name {
color: var(--color-text-1);
display: inline-block;
width: calc(100% - @file-size-width);
padding: 2px 0;
color: var(--color-text-1);
}
.file-size {
font-size: 13px;
display: inline-block;
width: @file-size-width;
text-align: end;
display: inline-flex;
font-size: 13px;
justify-content: flex-end;
align-items: center;
user-select: none;
}
}

View File

@@ -6,19 +6,19 @@
<!-- 操作 -->
<a-button-group size="mini">
<!-- 重置 -->
<a-button v-if="status.value === UploadTaskStatus.WAITING.value"
<a-button v-if="status.value === UploadTaskStepStatus.WAITING.value"
@click="emits('clear')">
重置
</a-button>
<!-- 取消上传 -->
<a-button v-if="status.value === UploadTaskStatus.REQUESTING.value"
<a-button v-if="status.value === UploadTaskStepStatus.REQUESTING.value"
type="primary"
status="warning"
@click="emits('abort')">
取消上传
</a-button>
<!-- 开始上传 -->
<a-button v-if="status.value === UploadTaskStatus.WAITING.value"
<a-button v-if="status.value === UploadTaskStepStatus.WAITING.value"
type="primary"
@click="submit">
开始上传
@@ -39,7 +39,10 @@
allow-clear />
</a-form-item>
<!-- 上传路径 -->
<a-form-item field="remotePath" label="上传路径">
<a-form-item field="remotePath"
style="margin-bottom: 4px;"
label="上传路径"
help="${username} 用户名 ${home} 家目录">
<a-input v-model="formModel.remotePath"
placeholder="请输入上传路径"
allow-clear />
@@ -71,7 +74,7 @@
import type { UploadTaskStatusType } from '../types/const';
import { ref } from 'vue';
import formRules from '../types/form.rules';
import { UploadTaskStatus } from '../types/const';
import { UploadTaskStepStatus } from '../types/const';
const emits = defineEmits(['upload', 'openHost', 'abort', 'clear']);
const props = defineProps<{

View File

@@ -8,7 +8,8 @@
<!-- 返回 -->
<a-button @click="emits('back')">返回</a-button>
<!-- 取消上传 -->
<a-button type="primary"
<a-button v-if="status.value === UploadTaskStepStatus.UPLOADING.value"
type="primary"
status="warning"
@click="emits('cancel')">
取消上传
@@ -37,14 +38,20 @@
<!-- 主机状态 -->
<a-space class="host-item-status" direction="vertical">
<!-- 未完成 -->
<a-tag class="host-item-status-tag" color="#52C41A">
<a-tag class="host-item-status-tag"
color="#73D13D"
title="未完成数量"
size="small">
{{ host.files.length - getFinishCount(host.files) }}
<template #icon>
<icon-clock-circle class="host-item-status-icon" />
</template>
</a-tag>
<!-- 已完成 -->
<a-tag class="host-item-status-tag" color="#1890FF">
<a-tag class="host-item-status-tag"
color="#40A9FF"
title="已完成数量"
size="small">
{{ getFinishCount(host.files) }}
<template #icon>
<icon-check-circle class="host-item-status-icon" />
@@ -66,10 +73,13 @@
<script lang="ts" setup>
import type { UploadTaskQueryResponse } from '@/api/exec/upload-task';
import type { UploadTaskFile } from '@/api/exec/upload-task';
import { UploadTaskFileStatus } from '../types/const';
import type { UploadTaskStatusType } from '../types/const';
import { UploadTaskStepStatus } from '../types/const';
import { UploadTaskFileStatus } from '@/views/exec/upload-task/types/const';
const emits = defineEmits(['update:selectedHost', 'back', 'cancel']);
const props = defineProps<{
status: UploadTaskStatusType;
selectedHost: number;
task: UploadTaskQueryResponse;
}>();
@@ -126,6 +136,7 @@
&-tag {
max-width: 64px;
width: 100%;
}
&-icon {

View File

@@ -16,12 +16,28 @@
<div class="file-item-path text-ellipsis" :title="file.filePath">
{{ file.filePath }}
</div>
<!-- 进度 -->
<div class="file-item-progress">
<a-progress type="circle"
size="mini"
:status="getDictValue(fileStatusKey, file.status, 'status') as any"
:percent="file.current / file.fileSize" />
<!-- 状态 -->
<div class="file-item-status">
<!-- 文件大小 -->
<div class="file-item-size span-blue">
<!-- 当前大小 -->
<template v-if="file.status === UploadTaskFileStatus.WAITING || file.status === UploadTaskFileStatus.UPLOADING">
{{ getFileSize(file.current || 0) }}
</template>
<!-- 总大小 -->
<template v-else>
{{ getFileSize(file.fileSize) }}
</template>
</div>
<!-- 进度 -->
<a-tooltip position="left"
:content="((file.current || 0) / file.fileSize * 100).toFixed(2) + '%'"
mini>
<a-progress type="circle"
size="mini"
:status="getDictValue(fileStatusKey, file.status, 'status') as any"
:percent="(file.current || 0) / file.fileSize" />
</a-tooltip>
</div>
</div>
</a-scrollbar>
@@ -38,7 +54,9 @@
<script lang="ts" setup>
import type { UploadTaskFile } from '@/api/exec/upload-task';
import { fileStatusKey } from '../types/const';
import { UploadTaskFileStatus } from '@/views/exec/upload-task/types/const';
import { useDictStore } from '@/store';
import { getFileSize } from '@/utils/file';
const emits = defineEmits(['update:selectedHost']);
const props = defineProps<{
@@ -47,12 +65,11 @@
const { getDictValue } = useDictStore();
</script>
<style lang="less" scoped>
@icon-width: 24px;
@progress-width: 24px;
@status-width: 102px;
.wrapper {
width: 100%;
@@ -83,15 +100,24 @@
}
&-path {
width: calc(100% - @icon-width - @progress-width);
padding: 2px 0;
width: calc(100% - @icon-width - @status-width);
display: inline-block;
font-size: 14px;
color: var(--color-text-1);
}
&-progress {
width: @progress-width;
&-size {
font-size: 12px;
margin-right: 12px;
user-select: none;
}
&-status {
width: @status-width;
display: flex;
justify-content: flex-end;
align-items: center;
}
}
}

View File

@@ -2,12 +2,12 @@
<a-spin class="panel-container full" :loading="loading">
<!-- 上传步骤 -->
<batch-upload-step class="panel-item first-panel-container"
:status="status" />
:status="taskStatus" />
<!-- 上传表单 -->
<batch-upload-form v-if="status.formPanel"
<batch-upload-form v-if="taskStatus.formPanel"
class="panel-item center-panel-container"
:form-model="formModel"
:status="status"
:status="taskStatus"
@upload="doCreateUploadTask"
@abort="abortUploadRequest"
@open-host="openHostModal"
@@ -16,11 +16,12 @@
<batch-upload-hosts v-else
class="panel-item center-panel-container"
v-model:selected-host="selectedHost"
:status="taskStatus"
:task="task"
@back="backFormPanel"
@cancel="doCancelUploadTask" />
<!-- 文件列表 -->
<batch-upload-files v-if="status.formPanel"
<batch-upload-files v-if="taskStatus.formPanel"
v-model:file-list="fileList"
class="panel-item last-panel-container"
ref="filesRef"
@@ -51,9 +52,10 @@
import type { FileItem } from '@arco-design/web-vue';
import type { UploadTaskCreateRequest, UploadTaskQueryResponse } from '@/api/exec/upload-task';
import type { UploadTaskStatusType } from '../types/const';
import { ref } from 'vue';
import { UploadTaskStatus } from '../types/const';
import { cancelUploadTask, createUploadTask, startUploadTask, getUploadTask } from '@/api/exec/upload-task';
import { onMounted, onUnmounted, ref } from 'vue';
import { UploadTaskStepStatus } from '../types/const';
import { UploadTaskStatus } from '@/views/exec/upload-task/types/const';
import { cancelUploadTask, createUploadTask, startUploadTask, getUploadTask, getUploadTaskStatus } from '@/api/exec/upload-task';
import useLoading from '@/hooks/loading';
import { Message } from '@arco-design/web-vue';
import BatchUploadStep from './batch-upload-step.vue';
@@ -66,24 +68,23 @@
const defaultForm = (): UploadTaskCreateRequest => {
return {
description: '',
remotePath: '/root/batch',
hostIdList: [1],
remotePath: '',
hostIdList: [],
files: []
};
};
const { loading, setLoading } = useLoading();
const pullStatusId = ref();
const taskId = ref();
const task = ref<UploadTaskQueryResponse>({} as UploadTaskQueryResponse);
const selectedHost = ref();
const formModel = ref<UploadTaskCreateRequest>({ ...defaultForm() });
const fileList = ref<Array<FileItem>>([]);
const status = ref<UploadTaskStatusType>(UploadTaskStatus.WAITING);
const taskStatus = ref<UploadTaskStatusType>(UploadTaskStepStatus.WAITING);
const filesRef = ref();
const hostModal = ref<any>();
// TODO pullstatus
const hostModal = ref();
// 设置选中主机
const setSelectedHost = (hosts: Array<number>) => {
@@ -106,16 +107,16 @@
}
// 创建任务
setLoading(true);
status.value = UploadTaskStatus.WAITING;
taskStatus.value = UploadTaskStepStatus.WAITING;
try {
formModel.value.files = files;
const { data } = await createUploadTask(formModel.value);
taskId.value = data.id;
status.value = UploadTaskStatus.REQUESTING;
taskStatus.value = UploadTaskStepStatus.REQUESTING;
// 上传文件
await filesRef.value.startUpload(data.token);
} catch (e) {
status.value = UploadTaskStatus.FAILED;
taskStatus.value = UploadTaskStepStatus.FAILED;
} finally {
setLoading(false);
}
@@ -127,7 +128,7 @@
try {
// 取消上传
await cancelUploadTask(taskId.value, false);
status.value = UploadTaskStatus.WAITING;
taskStatus.value = UploadTaskStepStatus.WAITING;
Message.success('已取消');
} catch (e) {
} finally {
@@ -137,13 +138,13 @@
// 中断上传请求
const abortUploadRequest = () => {
status.value = UploadTaskStatus.WAITING;
taskStatus.value = UploadTaskStepStatus.WAITING;
filesRef.value?.close();
};
// 上传请求结束
const uploadRequestEnd = async () => {
if (status.value.value === UploadTaskStatus.REQUESTING.value) {
if (taskStatus.value.value === UploadTaskStepStatus.REQUESTING.value) {
// 如果结束后还是请求中则代表请求完毕
setLoading(true);
try {
@@ -153,7 +154,7 @@
const { data } = await getUploadTask(taskId.value);
task.value = data;
selectedHost.value = data.hosts[0].id;
status.value = UploadTaskStatus.UPLOADING;
taskStatus.value = UploadTaskStepStatus.UPLOADING;
} catch (e) {
// 设置失败
await uploadRequestError();
@@ -172,13 +173,48 @@
try {
// 开始上传
await cancelUploadTask(taskId.value, true);
status.value = UploadTaskStatus.FAILED;
taskStatus.value = UploadTaskStepStatus.FAILED;
} catch (e) {
} finally {
setLoading(false);
}
};
// 加载轮询状态
const pullTaskStatus = async () => {
if (!taskId.value || !task.value) {
return;
}
// 非上传中则不查询
if (taskStatus.value.value !== UploadTaskStepStatus.UPLOADING.value) {
return;
}
// 查询状态
const { data } = await getUploadTaskStatus([taskId.value], true);
if (!data.length) {
return;
}
const taskStatusData = data[0];
// 设置任务状态
if (taskStatusData.status === UploadTaskStatus.FINISHED) {
taskStatus.value = UploadTaskStepStatus.FINISHED;
} else if (taskStatusData.status === UploadTaskStatus.CANCELED) {
taskStatus.value = UploadTaskStepStatus.FINISHED;
} else if (taskStatusData.status === UploadTaskStatus.FAILED) {
taskStatus.value = UploadTaskStepStatus.FAILED;
}
// 设置文件进度
for (let host of task.value.hosts) {
for (let file of host.files) {
const fileStatus = taskStatusData.files.find(s => s.id === file.id);
if (fileStatus) {
file.status = fileStatus.status;
file.current = fileStatus.current;
}
}
}
};
// 打开主机模态框
const openHostModal = () => {
hostModal.value.open(formModel.value.hostIdList);
@@ -186,7 +222,7 @@
// 返回表单页面
const backFormPanel = () => {
status.value = UploadTaskStatus.WAITING;
taskStatus.value = UploadTaskStepStatus.WAITING;
taskId.value = undefined;
task.value = undefined as any;
selectedHost.value = undefined as any;
@@ -202,6 +238,16 @@
fileList.value = [];
};
// 设置轮询状态
onMounted(() => {
pullStatusId.value = setInterval(pullTaskStatus, 5000);
});
// 卸载状态查询
onUnmounted(() => {
clearInterval(pullStatusId.value);
});
</script>
<style lang="less" scoped>

View File

@@ -7,7 +7,7 @@ export interface UploadTaskStatusType {
}
// 上传任务状态
export const UploadTaskStatus = {
export const UploadTaskStepStatus = {
// 等待中
WAITING: {
value: 'WAITING',
@@ -45,20 +45,6 @@ export const UploadTaskStatus = {
},
};
// 上传任务文件状态
export const UploadTaskFileStatus = {
// 等待中
WAITING: 'WAITING',
// 上传中
UPLOADING: 'UPLOADING',
// 已完成
FINISHED: 'FINISHED',
// 已完成
FAILED: 'FAILED',
// 已取消
CANCELED: 'CANCELED',
};
// 上传任务状态 字典项
export const taskStatusKey = 'uploadTaskStatus';

View File

@@ -102,6 +102,7 @@
};
defineExpose({ open });
// 确定
const handlerOk = async () => {
setLoading(true);

View File

@@ -1,17 +0,0 @@
<template>
<div>upload-task-log</div>
</template>
<script lang="ts">
export default {
name: 'index'
};
</script>
<script lang="ts" setup>
</script>
<style lang="less" scoped>
</style>