✨ 批量上传优化.
This commit is contained in:
@@ -11,6 +11,7 @@ import com.orion.ops.module.asset.entity.request.upload.UploadTaskCreateRequest;
|
||||
import com.orion.ops.module.asset.entity.request.upload.UploadTaskQueryRequest;
|
||||
import com.orion.ops.module.asset.entity.request.upload.UploadTaskRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.UploadTaskCreateVO;
|
||||
import com.orion.ops.module.asset.entity.vo.UploadTaskStatusVO;
|
||||
import com.orion.ops.module.asset.entity.vo.UploadTaskVO;
|
||||
import com.orion.ops.module.asset.service.UploadTaskService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -40,8 +41,6 @@ import java.util.List;
|
||||
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
|
||||
public class UploadTaskController {
|
||||
|
||||
// TODO 前端日志 测试删除慢吗
|
||||
|
||||
@Resource
|
||||
private UploadTaskService uploadTaskService;
|
||||
|
||||
@@ -92,7 +91,7 @@ public class UploadTaskController {
|
||||
@Operation(summary = "查询上传状态")
|
||||
@Parameter(name = "id", description = "id", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('asset:upload-task:query')")
|
||||
public List<UploadTaskVO> getUploadTaskStatus(@RequestParam("idList") List<Long> idList, @RequestParam("queryFiles") Boolean queryFiles) {
|
||||
public List<UploadTaskStatusVO> getUploadTaskStatus(@RequestParam("idList") List<Long> idList, @RequestParam("queryFiles") Boolean queryFiles) {
|
||||
return uploadTaskService.getUploadTaskStatus(idList, queryFiles);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.orion.ops.module.asset.convert;
|
||||
import com.orion.ops.module.asset.entity.domain.UploadTaskDO;
|
||||
import com.orion.ops.module.asset.entity.request.upload.UploadTaskCreateRequest;
|
||||
import com.orion.ops.module.asset.entity.request.upload.UploadTaskQueryRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.UploadTaskStatusVO;
|
||||
import com.orion.ops.module.asset.entity.vo.UploadTaskVO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
@@ -27,6 +28,8 @@ public interface UploadTaskConvert {
|
||||
|
||||
UploadTaskVO to(UploadTaskDO domain);
|
||||
|
||||
List<UploadTaskVO> to(List<UploadTaskDO> list);
|
||||
List<UploadTaskVO> toList(List<UploadTaskDO> list);
|
||||
|
||||
UploadTaskStatusVO toStatus(UploadTaskDO domain);
|
||||
|
||||
}
|
||||
|
||||
@@ -56,6 +56,14 @@ public class UploadTaskDO extends BaseDO {
|
||||
@TableField("extra_info")
|
||||
private String extraInfo;
|
||||
|
||||
@Schema(description = "文件数量")
|
||||
@TableField("file_count")
|
||||
private Integer fileCount;
|
||||
|
||||
@Schema(description = "主机数量")
|
||||
@TableField("host_count")
|
||||
private Integer hostCount;
|
||||
|
||||
@Schema(description = "开始时间")
|
||||
@TableField("start_time")
|
||||
private Date startTime;
|
||||
|
||||
@@ -38,6 +38,9 @@ public class HostTerminalConnectDTO {
|
||||
@Schema(description = "主机地址")
|
||||
private String hostAddress;
|
||||
|
||||
@Schema(description = "系统类型")
|
||||
private String osType;
|
||||
|
||||
@Schema(description = "端口")
|
||||
private Integer port;
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.orion.ops.module.asset.entity.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 上传任务状态 视图响应对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.7
|
||||
* @since 2024-5-7 22:15
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "UploadTaskStatusVO", description = "上传任务状态 视图响应对象")
|
||||
public class UploadTaskStatusVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "开始时间")
|
||||
private Date startTime;
|
||||
|
||||
@Schema(description = "结束时间")
|
||||
private Date endTime;
|
||||
|
||||
@Schema(description = "上传文件")
|
||||
private List<UploadTaskFileVO> files;
|
||||
|
||||
}
|
||||
@@ -47,6 +47,12 @@ public class UploadTaskVO implements Serializable {
|
||||
@Schema(description = "额外信息")
|
||||
private String extraInfo;
|
||||
|
||||
@Schema(description = "文件数量")
|
||||
private Integer fileCount;
|
||||
|
||||
@Schema(description = "主机数量")
|
||||
private Integer hostCount;
|
||||
|
||||
@Schema(description = "开始时间")
|
||||
private Date startTime;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.orion.ops.module.asset.handler.host.upload.task;
|
||||
|
||||
import com.orion.lang.utils.Threads;
|
||||
import com.orion.lang.utils.io.Files1;
|
||||
import com.orion.lang.utils.io.Streams;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.module.asset.dao.UploadTaskDAO;
|
||||
@@ -136,7 +137,7 @@ public class FileUploadTask implements IFileUploadTask {
|
||||
.map(s -> FileUploadFileItemDTO.builder()
|
||||
.id(s.getId())
|
||||
.fileId(s.getFileId())
|
||||
.remotePath(record.getRemotePath() + Const.SLASH + s.getFilePath())
|
||||
.remotePath(Files1.getPath(Const.SLASH + record.getRemotePath() + Const.SLASH + s.getFilePath()))
|
||||
.status(UploadTaskFileStatusEnum.WAITING.name())
|
||||
.current(0L)
|
||||
.build())
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package com.orion.ops.module.asset.handler.host.upload.uploader;
|
||||
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.lang.utils.io.Files1;
|
||||
import com.orion.lang.utils.io.Streams;
|
||||
import com.orion.net.host.SessionStore;
|
||||
import com.orion.net.host.sftp.SftpExecutor;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.common.file.FileClient;
|
||||
import com.orion.ops.framework.common.utils.PathUtils;
|
||||
import com.orion.ops.module.asset.dao.UploadTaskFileDAO;
|
||||
import com.orion.ops.module.asset.define.config.AppSftpConfig;
|
||||
import com.orion.ops.module.asset.entity.domain.UploadTaskFileDO;
|
||||
import com.orion.ops.module.asset.entity.dto.HostTerminalConnectDTO;
|
||||
import com.orion.ops.module.asset.enums.HostSshOsTypeEnum;
|
||||
import com.orion.ops.module.asset.enums.UploadTaskFileStatusEnum;
|
||||
import com.orion.ops.module.asset.handler.host.upload.dto.FileUploadFileItemDTO;
|
||||
import com.orion.ops.module.asset.service.HostTerminalService;
|
||||
@@ -23,6 +27,7 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -100,9 +105,10 @@ public class FileUploader implements IFileUploader {
|
||||
private boolean initSession() {
|
||||
log.info("HostFileUploader.initSession start taskId: {}, hostId: {}", taskId, hostId);
|
||||
try {
|
||||
// TODO 测试看看有没有问题, 则修改为 打开 executor 后 是否会connect, 不需要的话就关闭 executor 然后重新打开
|
||||
// 打开会话
|
||||
// 替换用户路径
|
||||
HostTerminalConnectDTO connectInfo = hostTerminalService.getTerminalConnectInfo(hostId);
|
||||
this.replaceRemotePathVariable(connectInfo.getOsType(), connectInfo.getUsername());
|
||||
// 打开会话
|
||||
this.sessionStore = hostTerminalService.openSessionStore(connectInfo);
|
||||
this.executor = sessionStore.getSftpExecutor(connectInfo.getFileNameCharset());
|
||||
executor.connect();
|
||||
@@ -215,6 +221,27 @@ public class FileUploader implements IFileUploader {
|
||||
uploadTaskFileDAO.updateById(update);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换文件路径变量
|
||||
*
|
||||
* @param osType osType
|
||||
* @param username username
|
||||
*/
|
||||
private void replaceRemotePathVariable(String osType, String username) {
|
||||
// 包含变量
|
||||
if (!files.get(0).getRemotePath().contains(Const.DOLLAR)) {
|
||||
return;
|
||||
}
|
||||
String home = PathUtils.getHomePath(HostSshOsTypeEnum.WINDOWS.name().equals(osType), username);
|
||||
// 替换变量
|
||||
Map<String, String> env = Maps.newMap(4);
|
||||
env.put("username", username);
|
||||
env.put("home", home);
|
||||
for (FileUploadFileItemDTO file : files) {
|
||||
file.setRemotePath(Files1.getPath(Strings.format(file.getRemotePath(), env)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
log.info("HostFileUploader.cancel taskId: {}, hostId: {}, canceled: {}, closed: {}", taskId, hostId, canceled, closed);
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.orion.ops.module.asset.entity.request.upload.UploadTaskCreateRequest;
|
||||
import com.orion.ops.module.asset.entity.request.upload.UploadTaskQueryRequest;
|
||||
import com.orion.ops.module.asset.entity.request.upload.UploadTaskRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.UploadTaskCreateVO;
|
||||
import com.orion.ops.module.asset.entity.vo.UploadTaskStatusVO;
|
||||
import com.orion.ops.module.asset.entity.vo.UploadTaskVO;
|
||||
|
||||
import java.util.List;
|
||||
@@ -51,7 +52,7 @@ public interface UploadTaskService {
|
||||
* @param queryFiles queryFiles
|
||||
* @return rows
|
||||
*/
|
||||
List<UploadTaskVO> getUploadTaskStatus(List<Long> idList, Boolean queryFiles);
|
||||
List<UploadTaskStatusVO> getUploadTaskStatus(List<Long> idList, Boolean queryFiles);
|
||||
|
||||
/**
|
||||
* 获取上传任务数量
|
||||
|
||||
@@ -241,6 +241,7 @@ public class HostTerminalServiceImpl implements HostTerminalService {
|
||||
conn.setHostId(host.getId());
|
||||
conn.setHostName(host.getName());
|
||||
conn.setHostAddress(host.getAddress());
|
||||
conn.setOsType(config.getOsType());
|
||||
conn.setPort(config.getPort());
|
||||
conn.setTimeout(config.getConnectTimeout());
|
||||
conn.setCharset(config.getCharset());
|
||||
|
||||
@@ -112,6 +112,8 @@ public class UploadTaskServiceImpl implements UploadTaskService {
|
||||
record.setUsername(user.getUsername());
|
||||
record.setDescription(Strings.def(record.getDescription(), () -> Strings.format(DEFAULT_DESC, Dates.current())));
|
||||
record.setStatus(UploadTaskStatusEnum.WAITING.name());
|
||||
record.setFileCount(files.size());
|
||||
record.setHostCount(hostIdList.size());
|
||||
UploadTaskExtraDTO extra = UploadTaskExtraDTO.builder()
|
||||
.hostIdList(hostIdList)
|
||||
.hosts(hosts)
|
||||
@@ -159,7 +161,7 @@ public class UploadTaskServiceImpl implements UploadTaskService {
|
||||
// 计算传输进度
|
||||
this.computeUploadProgress(id, files);
|
||||
// 设置任务文件
|
||||
this.setTaskFiles(uploadTask, files);
|
||||
this.setTaskHostFiles(uploadTask, files);
|
||||
return uploadTask;
|
||||
}
|
||||
|
||||
@@ -174,40 +176,39 @@ public class UploadTaskServiceImpl implements UploadTaskService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UploadTaskVO> getUploadTaskStatus(List<Long> idList, Boolean queryFiles) {
|
||||
public List<UploadTaskStatusVO> getUploadTaskStatus(List<Long> idList, Boolean queryFiles) {
|
||||
// 查询任务
|
||||
List<UploadTaskVO> tasks = uploadTaskDAO.of()
|
||||
List<UploadTaskStatusVO> tasks = uploadTaskDAO.of()
|
||||
.createWrapper()
|
||||
.select(UploadTaskDO::getId, UploadTaskDO::getStatus,
|
||||
UploadTaskDO::getStartTime, UploadTaskDO::getEndTime)
|
||||
.in(UploadTaskDO::getId, idList)
|
||||
.then()
|
||||
.list(UploadTaskConvert.MAPPER::to);
|
||||
.list(UploadTaskConvert.MAPPER::toStatus);
|
||||
if (!Booleans.isTrue(queryFiles)) {
|
||||
return tasks;
|
||||
}
|
||||
// 查询任务文件
|
||||
Map<Long, List<UploadTaskFileVO>> filesMap = uploadTaskFileDAO.of()
|
||||
Map<Long, List<UploadTaskFileVO>> taskFilesMap = uploadTaskFileDAO.of()
|
||||
.createWrapper()
|
||||
.select(UploadTaskFileDO::getId, UploadTaskFileDO::getTaskId,
|
||||
UploadTaskFileDO::getHostId, UploadTaskFileDO::getStatus,
|
||||
.select(UploadTaskFileDO::getId, UploadTaskFileDO::getTaskId, UploadTaskFileDO::getHostId,
|
||||
UploadTaskFileDO::getStatus, UploadTaskFileDO::getFileSize,
|
||||
UploadTaskFileDO::getStartTime, UploadTaskFileDO::getEndTime)
|
||||
.in(UploadTaskFileDO::getTaskId, idList)
|
||||
.then()
|
||||
.stream()
|
||||
.map(UploadTaskFileConvert.MAPPER::to)
|
||||
.collect(Collectors.groupingBy(UploadTaskFileVO::getTaskId));
|
||||
for (UploadTaskVO task : tasks) {
|
||||
for (UploadTaskStatusVO task : tasks) {
|
||||
Long id = task.getId();
|
||||
List<UploadTaskFileVO> files = filesMap.get(id);
|
||||
List<UploadTaskFileVO> files = taskFilesMap.get(id);
|
||||
if (files == null) {
|
||||
files = Lists.empty();
|
||||
} else {
|
||||
// 计算进度
|
||||
this.computeUploadProgress(id, files);
|
||||
// 设置任务文件
|
||||
}
|
||||
this.setTaskFiles(task, files);
|
||||
task.setFiles(files);
|
||||
}
|
||||
return tasks;
|
||||
}
|
||||
@@ -311,8 +312,8 @@ public class UploadTaskServiceImpl implements UploadTaskService {
|
||||
return uploadTaskDAO.wrapper()
|
||||
.eq(UploadTaskDO::getId, request.getId())
|
||||
.eq(UploadTaskDO::getUserId, request.getUserId())
|
||||
.in(UploadTaskDO::getDescription, request.getDescription())
|
||||
.eq(UploadTaskDO::getRemotePath, request.getRemotePath())
|
||||
.like(UploadTaskDO::getDescription, request.getDescription())
|
||||
.like(UploadTaskDO::getRemotePath, request.getRemotePath())
|
||||
.eq(UploadTaskDO::getStatus, request.getStatus())
|
||||
.ge(UploadTaskDO::getCreateTime, Arrays1.getIfPresent(request.getCreateTimeRange(), 0))
|
||||
.le(UploadTaskDO::getCreateTime, Arrays1.getIfPresent(request.getCreateTimeRange(), 1))
|
||||
@@ -450,12 +451,12 @@ public class UploadTaskServiceImpl implements UploadTaskService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置任务文件
|
||||
* 设置主机任务文件
|
||||
*
|
||||
* @param task task
|
||||
* @param files files
|
||||
*/
|
||||
private void setTaskFiles(UploadTaskVO task, List<UploadTaskFileVO> files) {
|
||||
private void setTaskHostFiles(UploadTaskVO task, List<UploadTaskFileVO> files) {
|
||||
Map<Long, List<UploadTaskFileVO>> hostFiles = files.stream()
|
||||
.collect(Collectors.groupingBy(UploadTaskFileVO::getHostId));
|
||||
List<UploadTaskHostVO> hosts = JSON.parseObject(task.getExtraInfo(), UploadTaskExtraDTO.class)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
<result column="description" property="description"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="extra_info" property="extraInfo"/>
|
||||
<result column="file_count" property="fileCount"/>
|
||||
<result column="host_count" property="hostCount"/>
|
||||
<result column="start_time" property="startTime"/>
|
||||
<result column="end_time" property="endTime"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
@@ -22,7 +24,7 @@
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, user_id, username, remote_path, description, status, extra_info, start_time, end_time, create_time, update_time, creator, updater, deleted
|
||||
id, user_id, username, remote_path, description, status, extra_info, file_count, host_count, start_time, end_time, create_time, update_time, creator, updater, deleted
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
|
||||
// 确定
|
||||
const handlerOk = async () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user