🔨 批量上传.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<div class="panel-header">
|
||||
<h3>文件列表</h3>
|
||||
<!-- 操作 -->
|
||||
<a-button-group size="small">
|
||||
<a-button-group size="small" :disabled="startStatus">
|
||||
<a-button @click="clear">清空</a-button>
|
||||
<!-- 选择文件 -->
|
||||
<a-upload v-model:file-list="fileList"
|
||||
@@ -29,11 +29,12 @@
|
||||
<!-- 文件列表 -->
|
||||
<div v-if="fileList.length" class="files-container">
|
||||
<a-upload class="files-wrapper"
|
||||
:class="['waiting-files-wrapper']"
|
||||
:class="[ startStatus ? 'uploading-files-wrapper' : 'waiting-files-wrapper' ]"
|
||||
v-model:file-list="fileList"
|
||||
:auto-upload="false"
|
||||
:show-cancel-button="false"
|
||||
:show-remove-button="true"
|
||||
:show-retry-button="false"
|
||||
:show-remove-button="!startStatus"
|
||||
:show-file-list="true">
|
||||
<template #upload-button />
|
||||
<template #file-name="{ fileItem }">
|
||||
@@ -71,15 +72,61 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FileItem } from '@arco-design/web-vue';
|
||||
import type { UploadTaskFileCreateRequest } from '@/api/exec/upload-task';
|
||||
import type { IFileUploader } from '@/components/system/uploader/const';
|
||||
import { ref } from 'vue';
|
||||
import { getFileSize } from '@/utils/file';
|
||||
import FileUploader from '@/components/system/uploader/file-uploader';
|
||||
|
||||
const emits = defineEmits(['end', 'error']);
|
||||
|
||||
const startStatus = ref(false);
|
||||
const fileList = ref<FileItem[]>([]);
|
||||
const uploader = ref<IFileUploader>();
|
||||
|
||||
// 获取上传的文件
|
||||
const getFiles = (): Array<UploadTaskFileCreateRequest> => {
|
||||
return fileList.value
|
||||
.map(s => {
|
||||
return {
|
||||
fileId: s.uid,
|
||||
filePath: s.file?.webkitRelativePath || s.file?.name,
|
||||
fileSize: s.file?.size,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// 开始上传
|
||||
const startUpload = async (token: string) => {
|
||||
// 修改状态
|
||||
startStatus.value = true;
|
||||
fileList.value.forEach(s => s.status = 'uploading');
|
||||
// 开始上传
|
||||
try {
|
||||
uploader.value = new FileUploader(token, fileList.value);
|
||||
uploader.value?.setHook(() => {
|
||||
emits('end');
|
||||
});
|
||||
await uploader.value?.start();
|
||||
} catch (e) {
|
||||
emits('error');
|
||||
}
|
||||
};
|
||||
|
||||
// 清空
|
||||
const clear = () => {
|
||||
fileList.value = [];
|
||||
startStatus.value = false;
|
||||
};
|
||||
|
||||
// 关闭
|
||||
const close = () => {
|
||||
startStatus.value = false;
|
||||
uploader.value?.close();
|
||||
};
|
||||
|
||||
defineExpose({ getFiles, startUpload, close });
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -127,11 +174,22 @@
|
||||
}
|
||||
|
||||
:deep(.arco-upload-list) {
|
||||
max-height: 100%;
|
||||
padding: 0;
|
||||
max-height: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
:deep(.arco-upload-list-item-error) {
|
||||
.arco-upload-list-item-name {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.arco-upload-progress {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-upload-list-item-name-text) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,24 @@
|
||||
<h3>批量上传</h3>
|
||||
<!-- 操作 -->
|
||||
<a-button-group size="small">
|
||||
<a-button>重置</a-button>
|
||||
<a-button type="primary">上传</a-button>
|
||||
<!-- 重置 -->
|
||||
<a-button v-if="status.value !== UploadTaskStatus.REQUESTING.value"
|
||||
@click="emits('clear')">
|
||||
重置
|
||||
</a-button>
|
||||
<!-- 取消上传 -->
|
||||
<a-button v-if="status.value === UploadTaskStatus.REQUESTING.value
|
||||
|| status.value === UploadTaskStatus.UPLOADING.value"
|
||||
@click="emits('cancel')">
|
||||
取消上传
|
||||
</a-button>
|
||||
<!-- 开始上传 -->
|
||||
<a-button v-if="status.value !== UploadTaskStatus.REQUESTING.value
|
||||
&& status.value !== UploadTaskStatus.UPLOADING.value"
|
||||
type="primary"
|
||||
@click="submit">
|
||||
开始上传
|
||||
</a-button>
|
||||
</a-button-group>
|
||||
</div>
|
||||
<!-- 表单 -->
|
||||
@@ -35,7 +51,7 @@
|
||||
<span class="usn" v-if="formModel.hostIdList?.length">
|
||||
已选择<span class="selected-host-count span-blue">{{ formModel.hostIdList?.length }}</span>台主机
|
||||
</span>
|
||||
<span class="usn pointer span-blue" @click="openSelectHost">
|
||||
<span class="usn pointer span-blue" @click="emits('openHost')">
|
||||
{{ formModel.hostIdList?.length ? '重新选择' : '选择主机' }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -52,28 +68,27 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { UploadTaskCreateRequest } from '@/api/exec/upload-task';
|
||||
import type { UploadTaskStatusType } from '../types/const';
|
||||
import { ref } from 'vue';
|
||||
import formRules from '../types/form.rules';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { UploadTaskStatus } from '../types/const';
|
||||
|
||||
const defaultForm = (): UploadTaskCreateRequest => {
|
||||
return {
|
||||
description: '',
|
||||
remotePath: '',
|
||||
hostIdList: [],
|
||||
files: []
|
||||
};
|
||||
};
|
||||
|
||||
const { loading, setLoading } = useLoading();
|
||||
const emits = defineEmits(['upload', 'openHost', 'cancel', 'clear']);
|
||||
const props = defineProps<{
|
||||
status: UploadTaskStatusType;
|
||||
formModel: UploadTaskCreateRequest;
|
||||
}>();
|
||||
|
||||
const formRef = ref<any>();
|
||||
const formModel = ref<UploadTaskCreateRequest>({ ...defaultForm() });
|
||||
const hostModal = ref<any>();
|
||||
|
||||
// 打开选择主机
|
||||
const openSelectHost = () => {
|
||||
hostModal.value.open(formModel.value.hostIdList);
|
||||
// 提交表单
|
||||
const submit = async () => {
|
||||
// 验证参数
|
||||
let error = await formRef.value.validate();
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
emits('upload');
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
<template>
|
||||
<div class="panel-container">
|
||||
<a-spin class="panel-container full" :loading="loading">
|
||||
<!-- 上传步骤 -->
|
||||
<batch-upload-step class="panel-item step-panel-container"
|
||||
:step="step"
|
||||
:status="stepStatus" />
|
||||
:status="status" />
|
||||
<!-- 上传表单 -->
|
||||
<batch-upload-form class="panel-item form-panel-container" />
|
||||
<batch-upload-form class="panel-item form-panel-container"
|
||||
:form-model="formModel"
|
||||
:status="status"
|
||||
@upload="doCreateUploadTask"
|
||||
@cancel="doCancelUploadTask"
|
||||
@open-host="openHostModal"
|
||||
@clear="clear" />
|
||||
<!-- 上传文件 -->
|
||||
<batch-upload-files class="panel-item files-panel-container" />
|
||||
</div>
|
||||
<batch-upload-files class="panel-item files-panel-container"
|
||||
ref="filesRef"
|
||||
@end="uploadRequestEnd"
|
||||
@error="uploadRequestError" />
|
||||
<!-- 主机模态框 -->
|
||||
<authorized-host-modal ref="hostModal"
|
||||
@selected="setSelectedHost" />
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -18,14 +29,125 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { UploadTaskCreateRequest } from '@/api/exec/upload-task';
|
||||
import type { UploadTaskStatusType } from '../types/const';
|
||||
import { ref } from 'vue';
|
||||
import { UploadStep, UploadStepStatus } from '../types/const';
|
||||
import { UploadTaskStatus } from '../types/const';
|
||||
import { cancelUploadTask, createUploadTask, startUploadTask } from '@/api/exec/upload-task';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import BatchUploadStep from './batch-upload-step.vue';
|
||||
import BatchUploadForm from './batch-upload-form.vue';
|
||||
import BatchUploadFiles from '@/views/exec/batch-upload/components/batch-upload-files.vue';
|
||||
import BatchUploadFiles from './batch-upload-files.vue';
|
||||
import AuthorizedHostModal from '@/components/asset/host/authorized-host-modal/index.vue';
|
||||
|
||||
const step = ref(UploadStep.PREPARATION);
|
||||
const stepStatus = ref(UploadStepStatus.PROCESS);
|
||||
const defaultForm = (): UploadTaskCreateRequest => {
|
||||
return {
|
||||
description: '',
|
||||
remotePath: '/root/batch',
|
||||
hostIdList: [1],
|
||||
files: []
|
||||
};
|
||||
};
|
||||
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
const taskId = ref();
|
||||
const formModel = ref<UploadTaskCreateRequest>({ ...defaultForm() });
|
||||
const status = ref<UploadTaskStatusType>(UploadTaskStatus.WAITING);
|
||||
const filesRef = ref();
|
||||
const hostModal = ref<any>();
|
||||
|
||||
// TODO pullstatus
|
||||
// TODO 测试 error 情况
|
||||
|
||||
// 设置选中主机
|
||||
const setSelectedHost = (hosts: Array<number>) => {
|
||||
formModel.value.hostIdList = hosts;
|
||||
};
|
||||
|
||||
// 创建上传任务
|
||||
const doCreateUploadTask = async () => {
|
||||
// 获取文件
|
||||
const files = filesRef.value?.getFiles();
|
||||
if (!files || !files.length) {
|
||||
Message.error('请先选择需要上传的文件');
|
||||
return;
|
||||
}
|
||||
// 创建任务
|
||||
setLoading(true);
|
||||
status.value = UploadTaskStatus.WAITING;
|
||||
try {
|
||||
formModel.value.files = files;
|
||||
const { data } = await createUploadTask(formModel.value);
|
||||
taskId.value = data.id;
|
||||
status.value = UploadTaskStatus.REQUESTING;
|
||||
// 上传文件
|
||||
await filesRef.value.startUpload(data.token);
|
||||
} catch (e) {
|
||||
status.value = UploadTaskStatus.FAILED;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 取消上传任务
|
||||
const doCancelUploadTask = async () => {
|
||||
setLoading(true);
|
||||
filesRef.value?.close();
|
||||
try {
|
||||
// 取消上传
|
||||
await cancelUploadTask(taskId.value, false);
|
||||
status.value = UploadTaskStatus.CANCELED;
|
||||
} catch (e) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 上传请求结束
|
||||
const uploadRequestEnd = async () => {
|
||||
if (status.value.value !== UploadTaskStatus.REQUESTING.value) {
|
||||
// 手动停止或者其他原因
|
||||
return;
|
||||
}
|
||||
// 如果结束后还是请求中则代表请求完毕
|
||||
setLoading(true);
|
||||
try {
|
||||
// 开始上传
|
||||
await startUploadTask(taskId.value);
|
||||
status.value = UploadTaskStatus.UPLOADING;
|
||||
} catch (e) {
|
||||
// 设置失败
|
||||
await uploadRequestError();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 上传请求失败
|
||||
const uploadRequestError = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 开始上传
|
||||
await cancelUploadTask(taskId.value, true);
|
||||
status.value = UploadTaskStatus.FAILED;
|
||||
} catch (e) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 打开主机模态框
|
||||
const openHostModal = () => {
|
||||
hostModal.value.open(formModel.value.hostIdList);
|
||||
};
|
||||
|
||||
// 清空
|
||||
const clear = () => {
|
||||
status.value = UploadTaskStatus.WAITING;
|
||||
formModel.value = { ...defaultForm() };
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<a-steps :current="step"
|
||||
:status="status as any"
|
||||
<a-steps :current="status.step"
|
||||
:status="status.status as any"
|
||||
direction="vertical">
|
||||
<a-step description="创建上传任务">创建任务</a-step>
|
||||
<a-step description="将文件上传到临时分区">上传文件</a-step>
|
||||
<a-step description="将文件分发到目标服务器">分发文件</a-step>
|
||||
<a-step>
|
||||
上传文件
|
||||
<template #description>
|
||||
<span>将文件上传到临时分区</span><br>
|
||||
<span class="span-red">在此期间请不要关闭页面</span>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step>
|
||||
分发文件
|
||||
<template #description>
|
||||
<span>将文件分发到目标服务器</span><br>
|
||||
<span>可以关闭页面</span>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step description="上传完成并释放资源">上传完成</a-step>
|
||||
</a-steps>
|
||||
</div>
|
||||
@@ -18,13 +30,16 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { UploadTaskStatusType } from '../types/const';
|
||||
|
||||
defineProps<{
|
||||
step: number;
|
||||
status: string;
|
||||
status: UploadTaskStatusType;
|
||||
}>();
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,35 +1,48 @@
|
||||
// 上传任务状态定义
|
||||
export interface UploadTaskStatusType {
|
||||
value: string,
|
||||
step: number,
|
||||
status: string,
|
||||
}
|
||||
|
||||
// 上传任务状态
|
||||
export const UploadTaskStatus = {
|
||||
// 准备中
|
||||
PREPARATION: 'PREPARATION',
|
||||
// 上传中
|
||||
UPLOADING: 'UPLOADING',
|
||||
// 已完成
|
||||
FINISHED: 'FINISHED',
|
||||
// 已取消
|
||||
CANCELED: 'CANCELED',
|
||||
};
|
||||
|
||||
// 上传步骤
|
||||
export const UploadStep = {
|
||||
// 准备中
|
||||
PREPARATION: 1,
|
||||
// 等待中
|
||||
WAITING: {
|
||||
value: 'WAITING',
|
||||
step: 1,
|
||||
status: 'process',
|
||||
},
|
||||
// 请求中
|
||||
REQUESTING: 2,
|
||||
// 分发中
|
||||
UPLOADING: 3,
|
||||
REQUESTING: {
|
||||
value: 'REQUESTING',
|
||||
step: 2,
|
||||
status: 'process',
|
||||
},
|
||||
// 上传中
|
||||
UPLOADING: {
|
||||
value: 'UPLOADING',
|
||||
step: 3,
|
||||
status: 'process',
|
||||
},
|
||||
// 已完成
|
||||
FINISHED: 4,
|
||||
};
|
||||
|
||||
// 上传步骤状态
|
||||
export const UploadStepStatus = {
|
||||
// 处理中
|
||||
PROCESS: 'process',
|
||||
// 上传完成
|
||||
FINISH: 'finish',
|
||||
// 上传失败
|
||||
ERROR: 'error',
|
||||
FINISHED: {
|
||||
value: 'FINISHED',
|
||||
step: 4,
|
||||
status: 'finish',
|
||||
},
|
||||
// 已失败
|
||||
FAILED: {
|
||||
value: 'FAILED',
|
||||
step: 4,
|
||||
status: 'error',
|
||||
},
|
||||
// 已取消
|
||||
CANCELED: {
|
||||
value: 'CANCELED',
|
||||
step: 4,
|
||||
status: 'error',
|
||||
},
|
||||
};
|
||||
|
||||
// 上传任务状态 字典项
|
||||
|
||||
@@ -18,14 +18,8 @@ export const remotePath = [{
|
||||
message: '上传路径长度不能大于1024位'
|
||||
}] as FieldRule[];
|
||||
|
||||
export const files = [{
|
||||
required: true,
|
||||
message: '请选择文件'
|
||||
}] as FieldRule[];
|
||||
|
||||
export default {
|
||||
description,
|
||||
hostIdList,
|
||||
remotePath,
|
||||
files,
|
||||
} as Record<string, FieldRule | FieldRule[]>;
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
return true;
|
||||
}
|
||||
// 添加到上传列表
|
||||
const files = fileList.value.map(s => s.file);
|
||||
const files = fileList.value.map(s => s.file as File);
|
||||
transferManager.addUpload(hostId.value, parentPath.value, files);
|
||||
Message.success('已开始上传, 点击右侧传输列表查看进度');
|
||||
// 清空
|
||||
@@ -158,9 +158,10 @@
|
||||
}
|
||||
|
||||
:deep(.arco-upload-list) {
|
||||
max-height: calc(100vh - 386px);
|
||||
overflow-y: auto;
|
||||
padding: 0 12px 0 0;
|
||||
max-height: calc(100vh - 386px);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
:deep(.arco-upload-list-item-name) {
|
||||
|
||||
Reference in New Issue
Block a user