feat(打包): 打包部署
commit
5840992c97
|
@ -4,7 +4,7 @@ services:
|
|||
image: nex-be:1.0.3
|
||||
container_name: nex-be
|
||||
ports:
|
||||
- "8113:8112"
|
||||
- "8113:8113"
|
||||
volumes:
|
||||
- /var/lib/vdi/:/var/lib/vdi/
|
||||
environment:
|
||||
|
|
|
@ -86,6 +86,13 @@
|
|||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Apache HttpClient 依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.14</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.unisinsight.project.config;
|
|||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
|
@ -15,6 +16,10 @@ public class RestTemplateConfig {
|
|||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
|
||||
factory.setConnectTimeout(600000);
|
||||
factory.setReadTimeout(600000);
|
||||
factory.setConnectionRequestTimeout(600000);
|
||||
return new RestTemplate(factory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.unisinsight.project.service.DeviceImageMappingService;
|
|||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -47,10 +48,20 @@ public class DeviceImageMappingController {
|
|||
}
|
||||
log.info("终端镜像映射新增请求参数为:{}", JSONUtil.toJsonStr(deviceImageMappingReq));
|
||||
|
||||
QueryWrapper<DeviceImageMapping> wrapper = new QueryWrapper<>();
|
||||
if (CollectionUtil.isEmpty(deviceImageMappingReq.getData()) && ObjectUtils.isNotEmpty(deviceImageMappingReq.getDeviceId())) {
|
||||
wrapper.lambda().eq(DeviceImageMapping::getDeviceId, deviceImageMappingReq.getDeviceId());
|
||||
List<DeviceImageMapping> list = deviceImageMappingService.list(wrapper);
|
||||
List<Long> collect = list.stream().map(DeviceImageMapping::getId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
if (CollectionUtil.isNotEmpty(collect)) {
|
||||
boolean removedByIds = deviceImageMappingService.removeByIds(collect);
|
||||
log.info("终端镜像映射新增接口删除了 {} 条旧数据,ID列表: {}", removedByIds, collect);
|
||||
}
|
||||
return Result.successResult();
|
||||
}
|
||||
List<DeviceImageMappingReq> reqData = deviceImageMappingReq.getData();
|
||||
List<DeviceImageMappingReq> addList = reqData.stream().distinct().filter(e -> Objects.isNull(e.getId())).collect(Collectors.toList());
|
||||
|
||||
QueryWrapper<DeviceImageMapping> wrapper = new QueryWrapper<>();
|
||||
wrapper.lambda().eq(DeviceImageMapping::getDeviceId, reqData.get(0).getDeviceId());
|
||||
List<DeviceImageMapping> list = deviceImageMappingService.list(wrapper);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.unisinsight.project.service.DeviceUserMappingService;
|
|||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -46,10 +47,20 @@ public class DeviceUserMappingController {
|
|||
return Result.errorResult(BaseErrorCode.PARAMETERS_INVALID);
|
||||
}
|
||||
log.info("终端用户映射新增请求参数为:{}", JSONUtil.toJsonStr(deviceUserMappingReq));
|
||||
QueryWrapper<DeviceUserMapping> wrapper = new QueryWrapper<>();
|
||||
if (CollectionUtil.isEmpty(deviceUserMappingReq.getData()) && ObjectUtils.isNotEmpty(deviceUserMappingReq.getDeviceId())) {
|
||||
wrapper.lambda().eq(DeviceUserMapping::getDeviceId, deviceUserMappingReq.getDeviceId());
|
||||
List<DeviceUserMapping> list = deviceUserMappingService.list(wrapper);
|
||||
List<Long> collect = list.stream().map(DeviceUserMapping::getId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
if (CollectionUtil.isNotEmpty(collect)) {
|
||||
boolean removedByIds = deviceUserMappingService.removeByIds(collect);
|
||||
log.info("终端用户映射新增接口删除了 {} 条旧数据,ID列表: {}", removedByIds, collect);
|
||||
}
|
||||
return Result.successResult();
|
||||
}
|
||||
List<DeviceUserMappingReq> userMappingReqData = deviceUserMappingReq.getData();
|
||||
List<DeviceUserMappingReq> addList = userMappingReqData.stream().distinct().filter(e -> Objects.isNull(e.getId())).collect(Collectors.toList());
|
||||
|
||||
QueryWrapper<DeviceUserMapping> wrapper = new QueryWrapper<>();
|
||||
wrapper.lambda().eq(DeviceUserMapping::getDeviceId, userMappingReqData.get(0).getDeviceId());
|
||||
List<DeviceUserMapping> list = deviceUserMappingService.list(wrapper);
|
||||
|
||||
|
|
|
@ -161,13 +161,13 @@ public class FileChunkController {
|
|||
// 异步执行创建和做种操作
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
String url = btUrl + "/test/start?sourceFile=%s&torrentFile=%s";
|
||||
String url = btUrl + "/vdi/start?sourceFile=%s&torrentFile=%s";
|
||||
url = String.format(url, finalFilePath, finalFilePath + ".torrent");
|
||||
log.info("请求bt创建接口参数: {}", url);
|
||||
ResponseEntity<Boolean> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, Boolean.class);
|
||||
log.info("请求bt创建接口返回: {}", JSONUtil.toJsonStr(responseEntity));
|
||||
HttpStatus statusCode = responseEntity.getStatusCode();
|
||||
if (statusCode != HttpStatus.OK) {
|
||||
if (statusCode == HttpStatus.OK) {
|
||||
boolean result = Boolean.TRUE.equals(responseEntity.getBody());
|
||||
if (result) {
|
||||
log.info("请求bt创建接口成功");
|
||||
|
|
|
@ -3,11 +3,13 @@ package com.unisinsight.project.controller;
|
|||
import com.unisinsight.project.util.BtTorrentUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController("test")
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RequestMapping("/vdi")
|
||||
public class TestController {
|
||||
|
||||
@GetMapping("/start")
|
||||
|
@ -20,11 +22,11 @@ public class TestController {
|
|||
}
|
||||
|
||||
@GetMapping("/stop")
|
||||
public String stop(@RequestParam("sourceFile") String sourceFile) {
|
||||
public boolean stop(@RequestParam("sourceFile") String sourceFile) {
|
||||
// 测试停止
|
||||
System.out.println("停止做种...");
|
||||
boolean stopResult = BtTorrentUtils.stopSeeding(sourceFile);
|
||||
System.out.println("停止结果: " + (stopResult ? "成功" : "失败"));
|
||||
return "success";
|
||||
return stopResult;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
|
|||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @TableName image
|
||||
|
@ -70,6 +71,11 @@ public class ImageRes implements Serializable {
|
|||
@ApiModelProperty("镜像存储路径")
|
||||
@JsonProperty("storage_path")
|
||||
private String storagePath;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@ApiModelProperty("创建时间")
|
||||
@JsonProperty("create_time")
|
||||
private Date createTime;
|
||||
|
||||
}
|
||||
|
|
|
@ -20,4 +20,12 @@ public class ListReq<T> {
|
|||
@JsonProperty("data")
|
||||
private List<T> data;
|
||||
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
@ApiModelProperty("序列号")
|
||||
@JsonProperty("device_id")
|
||||
private String deviceId;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -7,16 +7,17 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||
import com.unisinsight.project.entity.dao.DeviceImageMapping;
|
||||
import com.unisinsight.project.entity.dao.Image;
|
||||
import com.unisinsight.project.entity.res.ImageRes;
|
||||
import com.unisinsight.project.exception.BaseErrorCode;
|
||||
import com.unisinsight.project.exception.Result;
|
||||
import com.unisinsight.project.mapper.DeviceImageMappingMapper;
|
||||
import com.unisinsight.project.mapper.ImageMapper;
|
||||
import com.unisinsight.project.service.ClientService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
@ -37,15 +38,19 @@ public class ClientServiceImpl implements ClientService {
|
|||
@Resource
|
||||
private DeviceImageMappingMapper deviceImageMappingMapper;
|
||||
|
||||
// 请求bt配置
|
||||
@Value("${file.upload.bt-url}")
|
||||
private String btUrl;
|
||||
|
||||
@Override
|
||||
public Result<?> getImageList(String deviceId, String token) {
|
||||
|
||||
HashMap<String, Object> hashMap = new HashMap<>();
|
||||
QueryWrapper<DeviceImageMapping> deviceImageMappingQueryWrapper = new QueryWrapper<>();
|
||||
deviceImageMappingQueryWrapper.lambda().eq(DeviceImageMapping::getDeviceId, deviceId);
|
||||
List<DeviceImageMapping> deviceImageMappings = deviceImageMappingMapper.selectList(deviceImageMappingQueryWrapper);
|
||||
if (CollectionUtil.isEmpty(deviceImageMappings)) {
|
||||
return Result.errorResultMessage(BaseErrorCode.PARAMS_CHK_ERROR, "请先配置终端镜像");
|
||||
hashMap.put("list", new ArrayList<>());
|
||||
return Result.successResult(hashMap);
|
||||
}
|
||||
List<Long> imageIdList = deviceImageMappings.stream().map(DeviceImageMapping::getImageId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
if (CollectionUtil.isNotEmpty(imageIdList)) {
|
||||
|
@ -55,20 +60,28 @@ public class ClientServiceImpl implements ClientService {
|
|||
log.info("用户登录查询镜像结果:{}", JSONUtil.toJsonStr(images));
|
||||
List<ImageRes> imageRes = BeanUtil.copyToList(images, ImageRes.class);
|
||||
List<HashMap<String, Object>> collect = imageRes.stream().distinct().map(e -> {
|
||||
HashMap<String, Object> hashMap = new HashMap<>();
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
if (StringUtils.isNotBlank(e.getImageName())) {
|
||||
hashMap.put("name", e.getImageName());
|
||||
map.put("name", e.getImageName());
|
||||
}
|
||||
if (StringUtils.isNotBlank(e.getBtPath())) {
|
||||
hashMap.put("torrent", e.getBtPath());
|
||||
if (e.getBtPath().contains("http://") || e.getBtPath().contains("https://")) {
|
||||
map.put("torrent", e.getBtPath());
|
||||
} else {
|
||||
String fileName = e.getBtPath().substring(Math.max(e.getBtPath().lastIndexOf("\\"), e.getBtPath().lastIndexOf("/")) + 1);
|
||||
map.put("torrent", btUrl + "/api/vdi/file/down/" + fileName);
|
||||
}
|
||||
return hashMap;
|
||||
}
|
||||
return map;
|
||||
}).collect(Collectors.toList());
|
||||
HashMap<String, Object> hashMap = new HashMap<>();
|
||||
|
||||
if (CollectionUtil.isNotEmpty(collect)) {
|
||||
hashMap.put("list", collect);
|
||||
return Result.successResult(hashMap);
|
||||
} else {
|
||||
hashMap.put("list", new ArrayList<>());
|
||||
}
|
||||
return Result.successResult();
|
||||
}
|
||||
return Result.successResult(hashMap);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -119,12 +119,20 @@ public class DeviceUserMappingServiceImpl extends ServiceImpl<DeviceUserMappingM
|
|||
|
||||
@Override
|
||||
public Result<?> loginUser(DeviceUserReq deviceUserReq) {
|
||||
|
||||
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
|
||||
userQueryWrapper.lambda().in(User::getUserName, deviceUserReq.getUserName());
|
||||
List<User> userList1 = userMapper.selectList(userQueryWrapper);
|
||||
log.info("登录查询用户结果:{}", JSONUtil.toJsonStr(userList1));
|
||||
if (CollectionUtil.isEmpty(userList1)) {
|
||||
return Result.errorResultMessage(BaseErrorCode.PARAMS_CHK_ERROR, "用户不存在");
|
||||
}
|
||||
QueryWrapper<DeviceUserMapping> wrapper = new QueryWrapper<>();
|
||||
wrapper.lambda().eq(DeviceUserMapping::getDeviceId, deviceUserReq.getDeviceId());
|
||||
List<DeviceUserMapping> deviceUserMappings = deviceUserMappingMapper.selectList(wrapper);
|
||||
log.info("用户登录查询映射结果:{}", JSONUtil.toJsonStr(deviceUserMappings));
|
||||
List<User> users = new ArrayList<>();
|
||||
|
||||
List<User> users = new ArrayList<>();
|
||||
List<Long> userIdList = deviceUserMappings.stream().map(DeviceUserMapping::getUserId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
log.info("用户登录查询用户id结果:{}", JSONUtil.toJsonStr(userIdList));
|
||||
if (CollectionUtil.isNotEmpty(userIdList)) {
|
||||
|
@ -152,11 +160,11 @@ public class DeviceUserMappingServiceImpl extends ServiceImpl<DeviceUserMappingM
|
|||
}
|
||||
log.info("用户登录查询结果:{}", JSONUtil.toJsonStr(users));
|
||||
if (CollectionUtil.isEmpty(users)) {
|
||||
return Result.errorResultMessage(BaseErrorCode.PARAMS_CHK_ERROR, "用户不存在,请重试");
|
||||
return Result.errorResultMessage(BaseErrorCode.PARAMS_CHK_ERROR, "当前用户无权限登录终端,请联系管理员");
|
||||
}
|
||||
users = users.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
if (CollectionUtil.isEmpty(users)) {
|
||||
return Result.errorResultMessage(BaseErrorCode.PARAMS_CHK_ERROR, "用户不存在,请重试");
|
||||
return Result.errorResultMessage(BaseErrorCode.PARAMS_CHK_ERROR, "当前用户无权限登录终端,请联系管理员");
|
||||
}
|
||||
if (!users.get(0).getPassword().equals(deviceUserReq.getPassword())) {
|
||||
return Result.errorResultMessage(BaseErrorCode.PARAMS_CHK_ERROR, "密码错误,请重新输入");
|
||||
|
|
|
@ -30,6 +30,8 @@ import java.io.IOException;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author rdpnr_puzhi
|
||||
|
@ -71,6 +73,16 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, Image>
|
|||
return Result.successResult();
|
||||
} else {
|
||||
PageResult<ImageRes> convert = PageResult.convertIPage(imageIPage, ImageRes.class);
|
||||
List<ImageRes> data = convert.getData();
|
||||
List<ImageRes> collect = data.stream().distinct().peek(e -> {
|
||||
if (StringUtils.isNotBlank(e.getBtPath())) {
|
||||
if (!e.getBtPath().contains("http://") || !e.getBtPath().contains("https://")) {
|
||||
String fileName = e.getBtPath().substring(Math.max(e.getBtPath().lastIndexOf("\\"), e.getBtPath().lastIndexOf("/")) + 1);
|
||||
e.setBtPath(btUrl + "/api/vdi/file/down/" + fileName);
|
||||
}
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
convert.setData(collect);
|
||||
return Result.successResult(convert);
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +93,7 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, Image>
|
|||
if (ObjectUtils.isNotEmpty(image)) {
|
||||
try {
|
||||
try {
|
||||
String url = btUrl + "/test/stop?sourceFile=%s";
|
||||
String url = btUrl + "/vdi/stop?sourceFile=%s";
|
||||
url = String.format(url, image.getStoragePath());
|
||||
log.info("请求bt停止接口参数: {}", url);
|
||||
ResponseEntity<Boolean> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, Boolean.class);
|
||||
|
@ -107,8 +119,13 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, Image>
|
|||
return Result.successResult();
|
||||
}
|
||||
} else {
|
||||
int deleted = imageMapper.deleteById(deleteIdReq.getId());
|
||||
log.info("文件不存在,镜像删除insert:{}", deleted);
|
||||
if (deleted == 1) {
|
||||
return Result.successResult();
|
||||
}
|
||||
log.warn("文件不存在,无需删除: {}", filePath);
|
||||
return Result.errorResultMessage(BaseErrorCode.HTTP_ERROR_CODE_500, "文件不存在,无需删除");
|
||||
return Result.successResult();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("删除文件失败: {}", e.getMessage(), e);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# application.yml
|
||||
server:
|
||||
port: 8112
|
||||
port: 8113
|
||||
file:
|
||||
upload:
|
||||
temp-dir: /var/lib/vdi/tmp/chunked-uploads
|
||||
|
|
|
@ -45,11 +45,11 @@ export default defineConfig({
|
|||
npmClient: 'pnpm',
|
||||
proxy: {
|
||||
'/api/nex/v1/': {
|
||||
target: 'http://10.100.51.85:8112',
|
||||
target: 'http://10.100.51.85:8113',
|
||||
// changeOrigin: true,
|
||||
},
|
||||
'/api/files': {
|
||||
target: 'http://10.100.51.85:8112',
|
||||
target: 'http://10.100.51.85:8113',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link rel="stylesheet" href="/umi.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="/umi.js"></script>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.main-layout{min-height:100vh}.main-sider .logo{height:64px;display:flex;align-items:center;justify-content:center;color:#fff;font-size:18px;font-weight:700;border-bottom:1px solid #303030}.main-header{background:#fff;padding:0 24px;display:flex;align-items:center;justify-content:space-between;box-shadow:0 2px 8px #0000001a}.main-header .trigger{font-size:18px;color:#666}.main-header .trigger:hover{color:#1890ff}.main-header .header-right{display:flex;align-items:center;gap:16px}.main-header .header-right .welcome-text{color:#666;font-size:14px}.main-header .header-right .user-avatar{cursor:pointer;background:#1890ff}.main-header .header-right .user-avatar:hover{opacity:.8}.main-content{background:#fff;border-radius:8px;box-shadow:0 2px 8px #0000001a;min-height:calc(100vh - 112px)}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.main-layout{min-height:100vh}.main-sider .logo{height:64px;display:flex;align-items:center;justify-content:center;color:#fff;font-size:18px;font-weight:700;border-bottom:1px solid #303030}.main-header{background:#fff;padding:0 24px;display:flex;align-items:center;justify-content:space-between;box-shadow:0 2px 8px #0000001a}.main-header .trigger{font-size:18px;color:#666}.main-header .trigger:hover{color:#1890ff}.main-header .header-right{display:flex;align-items:center;gap:16px}.main-header .header-right .welcome-text{color:#666;font-size:14px}.main-header .header-right .user-avatar{cursor:pointer;background:#1890ff}.main-header .header-right .user-avatar:hover{opacity:.8}.main-content{background:#fff;border-radius:8px;box-shadow:0 2px 8px #0000001a;min-height:calc(100vh - 112px)}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.page-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}.page-header h2{margin:0;font-size:24px;font-weight:600;color:#333}.image-list{height:100%;display:flex;flex-direction:column;padding:16px;box-sizing:border-box}.image-list .search-box{margin-bottom:16px;display:flex;justify-content:space-between}.image-list .search-box .search-input{display:flex;gap:8px;align-items:center}.image-list .images-list-container,.image-list .images-list-container .images-list-table{flex:1 1;display:flex;flex-direction:column;overflow:hidden}.image-list .images-list-container .images-list-table .ant-table-wrapper{display:flex;flex-direction:column;flex:1 1;overflow:hidden}.image-list .images-list-container .images-list-table .ant-table-wrapper .ant-spin-nested-loading,.image-list .images-list-container .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container{flex:1 1;display:flex;flex-direction:column;overflow:hidden}.image-list .images-list-container .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table,.image-list .images-list-container .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-container{display:flex;flex-direction:column;flex:1 1;overflow:hidden}.image-list .images-list-container .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-container .ant-table-header{flex-shrink:0}.image-list .images-list-container .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-container .ant-table-body{flex:1 1;overflow:auto!important}.image-list .images-list-container .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-pagination{flex-shrink:0;position:relative;z-index:1}.image-list .image-detail .detail-item{margin-bottom:16px}.image-list .image-detail .detail-item label{font-weight:600;color:#333;display:inline-block;width:100px}.image-list .image-detail .detail-item span{color:#666}.image-list .image-detail .detail-item p{margin:8px 0 0 100px;color:#666;line-height:1.6}.profile-page .profile-content .profile-header{display:flex;align-items:center;gap:16px;margin-bottom:16px}.profile-page .profile-content .profile-header .profile-info h3{margin:0 0 4px;font-size:20px;font-weight:600;color:#333}.profile-page .profile-content .profile-header .profile-info p{margin:0;color:#666;font-size:14px}.profile-page .profile-content .quick-actions{display:flex;gap:12px;flex-wrap:wrap}@media (max-width: 768px){.page-header{flex-direction:column;align-items:flex-start;gap:12px}.profile-content .profile-header{flex-direction:column;text-align:center}.profile-content .quick-actions{justify-content:center}}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.login-container{display:flex;min-height:100vh;background:linear-gradient(135deg,#1890ff,#722ed1)}.login-container:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba%28255,255,255,0.1%29" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url%28%23grid%29"/></svg>');opacity:.3}.login-left{flex:1 1;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden}.brand-content{text-align:center;color:#fff;z-index:1;position:relative;max-width:400px;padding:40px}.brand-logo{margin-bottom:30px}.brand-logo .logo-icon{font-size:60px;margin-bottom:20px;display:block;color:#ffffffe6}.brand-logo .brand-title{font-size:36px;font-weight:700;margin:0;color:#fff;text-shadow:0 2px 4px rgba(0,0,0,.3)}.brand-subtitle{font-size:20px;font-weight:500;margin-bottom:30px;color:#ffffffe6}.brand-description{margin-bottom:40px}.brand-description p{font-size:16px;line-height:1.6;margin:8px 0;color:#fffc}.brand-features{display:flex;justify-content:space-around;flex-wrap:wrap;gap:20px}.brand-features .feature-item{display:flex;flex-direction:column;align-items:center;gap:8px}.brand-features .feature-item .feature-icon{font-size:24px}.brand-features .feature-item span:last-child{font-size:14px;color:#fffc}.login-right{flex:1 1;display:flex;align-items:center;justify-content:center;padding:40px}.login-form-container{width:100%;max-width:400px}.login-header{text-align:center;margin-bottom:40px}.login-header .login-title{font-size:28px;font-weight:600;color:#fff;margin:0 0 8px}.login-header .login-subtitle{font-size:16px;color:#fff;margin:0}.login-form .ant-form-item{margin-bottom:24px}.login-input{height:48px;border-radius:8px;border:1px solid #d9d9d9;font-size:16px}.login-input:hover,.login-input:focus,.login-input.ant-input-focused{border-color:#1890ff;box-shadow:0 0 0 2px #1890ff33}.login-input .input-icon{color:#bfbfbf;font-size:16px}.login-button{height:48px;border-radius:8px;font-size:16px;font-weight:500;background:linear-gradient(135deg,#1890ff,#722ed1);border:none;margin-top:8px}.login-button:hover{background:linear-gradient(135deg,#40a9ff,#9254de);transform:translateY(-1px);box-shadow:0 4px 12px #1890ff4d}.login-button:active{transform:translateY(0)}.login-tips{text-align:center;margin-top:24px;padding:16px;background-color:#f6f8fa;border-radius:8px;border:1px solid #e8e8e8}.login-tips p{margin:0;color:#595959;font-size:14px}.login-footer{text-align:center;margin-top:40px}.login-footer p{color:#8c8c8c;font-size:12px;margin:0}@media (max-width: 768px){.login-container{flex-direction:column}.login-left{flex:none;height:200px;padding:20px}.brand-content{padding:20px}.brand-logo .logo-icon{font-size:40px}.brand-title{font-size:24px}.brand-subtitle{font-size:16px}.brand-description p{font-size:14px}.login-right{flex:1 1;padding:20px}}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.page-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}.page-header h2{margin:0;font-size:24px;font-weight:600;color:#333}.image-list .image-detail .detail-item{margin-bottom:16px}.image-list .image-detail .detail-item label{font-weight:600;color:#333;display:inline-block;width:100px}.image-list .image-detail .detail-item span{color:#666}.image-list .image-detail .detail-item p{margin:8px 0 0 100px;color:#666;line-height:1.6}.profile-page .profile-content .profile-header{display:flex;align-items:center;gap:16px;margin-bottom:16px}.profile-page .profile-content .profile-header .profile-info h3{margin:0 0 4px;font-size:20px;font-weight:600;color:#333}.profile-page .profile-content .profile-header .profile-info p{margin:0;color:#666;font-size:14px}.profile-page .profile-content .quick-actions{display:flex;gap:12px;flex-wrap:wrap}@media (max-width: 768px){.page-header{flex-direction:column;align-items:flex-start;gap:12px}.profile-content .profile-header{flex-direction:column;text-align:center}.profile-content .quick-actions{justify-content:center}}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.user_content___NVrSf{display:flex;width:100%;height:100%;background-color:#f7f8fa}.user_content___NVrSf .left_content___k_v4w{width:400px;height:100%;padding:8px;background-color:#fff}.user_content___NVrSf .left_content___k_v4w .search___mN53j{width:100%;height:70px}.user_content___NVrSf .left_content___k_v4w .tree_box___HLlDc{width:100%;height:calc(100% - 70px);overflow:auto;padding-top:10px}.user_content___NVrSf .right_content___NTJte{width:calc(100% - 400px);height:100%;padding-left:10px}.user_content___NVrSf .right_content___NTJte .teble_content___yJ7lW{width:100%;height:100%;background-color:#fff;padding:8px}.user_content___NVrSf .right_content___NTJte .teble_content___yJ7lW .teble_box___YE1no{display:flex;flex-direction:column;width:100%;height:calc(100% - 50px);overflow:hidden}.user_content___NVrSf :where(.css-dev-only-do-not-override-1vjf2v5).ant-pagination .ant-pagination-total-text{position:absolute;left:5px}.user_content___NVrSf .images-list-table{flex:1 1;display:flex;flex-direction:column;overflow:hidden}.user_content___NVrSf .images-list-table .ant-table-wrapper{display:flex;flex-direction:column;flex:1 1;overflow:hidden}.user_content___NVrSf .images-list-table .ant-table-wrapper .ant-spin-nested-loading,.user_content___NVrSf .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container{flex:1 1;display:flex;flex-direction:column;overflow:hidden}.user_content___NVrSf .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table,.user_content___NVrSf .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-container{display:flex;flex-direction:column;flex:1 1;overflow:hidden}.user_content___NVrSf .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-container .ant-table-header{flex-shrink:0}.user_content___NVrSf .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-container .ant-table-body{flex:1 1;overflow:auto!important}.user_content___NVrSf .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-pagination{flex-shrink:0;position:relative;z-index:1}.content_wrap___IzyVq{width:100%;height:100%}.content_wrap___IzyVq .search_wrap___vyvi6{display:flex;justify-content:flex-end;margin-bottom:5px;padding-bottom:5px}.content_wrap___IzyVq :where(.css-dev-only-do-not-override-1vjf2v5).ant-pagination .ant-pagination-total-text{position:absolute;left:5px}.model_content___MsF87{width:100%;height:650px}.content_wrap___t2j0e{width:100%;height:100%}.content_wrap___t2j0e .search_wrap___MKaSL{display:flex;justify-content:flex-end;margin-bottom:5px;padding-bottom:5px}.content_wrap___t2j0e :where(.css-dev-only-do-not-override-1vjf2v5).ant-pagination .ant-pagination-total-text{position:absolute;left:5px}.model_content___DaYce{width:100%;height:650px}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.user_content___n6dbD{display:flex;width:100%;height:100%;background-color:#f7f8fa}.user_content___n6dbD .left_content___CkwkA{width:400px;height:100%;padding:8px;background-color:#fff}.user_content___n6dbD .left_content___CkwkA .search___HoQbf{width:100%;height:70px}.user_content___n6dbD .left_content___CkwkA .tree_box___x56Nb{width:100%;height:calc(100% - 70px);overflow:auto;padding-top:10px}.user_content___n6dbD .right_content___JtLdU{width:calc(100% - 400px);height:100%;padding-left:10px}.user_content___n6dbD .right_content___JtLdU .teble_content___kLIoM{width:100%;height:100%;background-color:#fff;padding:8px}.user_content___n6dbD .right_content___JtLdU .teble_content___kLIoM .teble_box___Gk7lM{display:flex;flex-direction:column;width:100%;height:calc(100% - 50px);overflow:hidden}.user_content___n6dbD :where(.css-dev-only-do-not-override-1vjf2v5).ant-pagination .ant-pagination-total-text{position:absolute;left:5px}.user_content___n6dbD .images-list-table{flex:1 1;display:flex;flex-direction:column;overflow:hidden}.user_content___n6dbD .images-list-table .ant-table-wrapper{display:flex;flex-direction:column;flex:1 1;overflow:hidden}.user_content___n6dbD .images-list-table .ant-table-wrapper .ant-spin-nested-loading,.user_content___n6dbD .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container{flex:1 1;display:flex;flex-direction:column;overflow:hidden}.user_content___n6dbD .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table,.user_content___n6dbD .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-container{display:flex;flex-direction:column;flex:1 1;overflow:hidden}.user_content___n6dbD .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-container .ant-table-header{flex-shrink:0}.user_content___n6dbD .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-container .ant-table-body{flex:1 1;overflow:auto!important}.user_content___n6dbD .images-list-table .ant-table-wrapper .ant-spin-nested-loading .ant-spin-container .ant-table .ant-table-pagination{flex-shrink:0;position:relative;z-index:1}
|
|
@ -0,0 +1 @@
|
|||
html,body{width:100%;height:100%}input::-ms-clear,input::-ms-reveal{display:none}*,*:before,*:after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{margin:0}[tabindex="-1"]:focus{outline:none}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5em;font-weight:500}p{margin-top:0;margin-bottom:1em}abbr[title],abbr[data-original-title]{text-decoration:underline dotted;border-bottom:0;cursor:help}address{margin-bottom:1em;font-style:normal;line-height:inherit}input[type=text],input[type=password],input[type=number],textarea{-webkit-appearance:none}ol,ul,dl{margin-top:0;margin-bottom:1em}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:500}dd{margin-bottom:.5em;margin-left:0}blockquote{margin:0 0 1em}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}pre,code,kbd,samp{font-size:1em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace}pre{margin-top:0;margin-bottom:1em;overflow:auto}figure{margin:0 0 1em}img{vertical-align:middle;border-style:none}a,area,button,[role=button],input:not([type=range]),label,select,summary,textarea{touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75em;padding-bottom:.3em;text-align:left;caption-side:bottom}input,button,select,optgroup,textarea{margin:0;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{padding:0;border-style:none}input[type=radio],input[type=checkbox]{box-sizing:border-box;padding:0}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;margin:0;padding:0;border:0}legend{display:block;width:100%;max-width:100%;margin-bottom:.5em;padding:0;color:inherit;font-size:1.5em;line-height:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}mark{padding:.2em;background-color:#feffe6}
|
File diff suppressed because one or more lines are too long
|
@ -87,19 +87,19 @@ const MainLayout: React.FC = () => {
|
|||
collapsed={collapsed}
|
||||
className="main-sider"
|
||||
>
|
||||
<div className="logo">{!collapsed && <span>VDI管理平台</span>}</div>
|
||||
<div className="logo">{!collapsed && <span>Nex管理平台</span>}</div>
|
||||
<Menu
|
||||
theme="dark"
|
||||
mode="inline"
|
||||
selectedKeys={[selectedKey]}
|
||||
onClick={handleMenuClick}
|
||||
>
|
||||
<Menu.Item key="userList" icon={<AppstoreOutlined />}>
|
||||
用户
|
||||
</Menu.Item>
|
||||
<Menu.Item key="terminal" icon={<AppstoreOutlined />}>
|
||||
终端
|
||||
</Menu.Item>
|
||||
<Menu.Item key="userList" icon={<AppstoreOutlined />}>
|
||||
用户
|
||||
</Menu.Item>
|
||||
<Menu.Item key="images" icon={<AppstoreOutlined />}>
|
||||
镜像列表
|
||||
</Menu.Item>
|
||||
|
|
|
@ -76,37 +76,25 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
|||
// 上传镜像时间相关
|
||||
const [elapsedTime, setElapsedTime] = useState<number>(0); // 上传时间
|
||||
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
// const [uploadCompleted, _setUploadCompleted] = useState(false);
|
||||
// const uploadCompletedRef = useRef(false);
|
||||
|
||||
// const setUploadCompleted = (value: boolean) => {
|
||||
// uploadCompletedRef.current = value;
|
||||
// _setUploadCompleted(value);
|
||||
// };
|
||||
// 上传完成状态
|
||||
const uploadCompletedRef = useRef(false);
|
||||
// 手动取消状态
|
||||
const isManualCancel = useRef(false); // 是否手动取消上传
|
||||
|
||||
// 处理页面刷新/关闭
|
||||
// useEffect(() => {
|
||||
// const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||
// if (isUploading && !uploadCompletedRef.current) {
|
||||
// e.preventDefault();
|
||||
// e.returnValue = '镜像正在上传中,确定要离开吗?';
|
||||
useEffect(() => {
|
||||
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||
if (isUploading && !uploadCompletedRef.current) {
|
||||
e.preventDefault();
|
||||
e.returnValue = '镜像正在上传中,确定要离开吗?';
|
||||
return e.returnValue;
|
||||
|
||||
// // 使用 sendBeacon 发送取消请求
|
||||
// const params = new URLSearchParams();
|
||||
// params.append('file_id', fileId.current);
|
||||
// const blob = new Blob([params.toString()], {
|
||||
// type: 'application/x-www-form-urlencoded',
|
||||
// });
|
||||
// navigator.sendBeacon('/api/cancel-upload', blob);
|
||||
}
|
||||
};
|
||||
|
||||
// return e.returnValue;
|
||||
// }
|
||||
// };
|
||||
|
||||
// window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
// return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
// }, [isUploading]);
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
}, [isUploading]);
|
||||
|
||||
// 计时器清理(仅组件卸载时执行)
|
||||
useEffect(() => {
|
||||
|
@ -117,30 +105,14 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
|||
};
|
||||
}, []);
|
||||
|
||||
// // 上传取消逻辑(依赖 isUploading 和 uploadCompletedRef)
|
||||
// useEffect(() => {
|
||||
// return () => {
|
||||
// if (isUploading && !uploadCompletedRef.current && fileId.current) {
|
||||
// const params = new URLSearchParams();
|
||||
// params.append('file_id', fileId.current);
|
||||
// cancelUploadImagesAPI(params).then((res) => {
|
||||
// if (res.code === CODE) {
|
||||
// message.success('上传已取消');
|
||||
// } else {
|
||||
// message.error('取消上传失败');
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
// }, [isUploading]); // 保留原有依赖
|
||||
|
||||
// 添加重置状态函数
|
||||
const resetState = () => {
|
||||
// setUploadCompleted(false); // 重置上传完成状态
|
||||
setUploadProgress(0);
|
||||
setIsUploading(false);
|
||||
setUploadStatus(READY);
|
||||
setUploadMessage('');
|
||||
uploadCompletedRef.current = false; // 重置上传完成状态
|
||||
isManualCancel.current = false; // 重置手动取消状态
|
||||
completedChunks.current = 0;
|
||||
totalChunks.current = 0;
|
||||
fileId.current = '';
|
||||
|
@ -162,13 +134,6 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
// 当弹窗关闭时重置状态
|
||||
useEffect(() => {
|
||||
if (!visible) {
|
||||
resetState();
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
// 4. 上传单个分片
|
||||
const uploadChunk = async (
|
||||
chunk: Blob,
|
||||
|
@ -206,8 +171,9 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
|||
// 根据后端返回的状态进行判断
|
||||
if (response.success) {
|
||||
if (response.status === 'completed') {
|
||||
uploadCompletedRef.current = true; // 设置上传完成状态
|
||||
isManualCancel.current = false; // 重置手动取消状态
|
||||
// 文件上传完成,设置进度为100%
|
||||
// setUploadCompleted(true); // 标记上传完成
|
||||
setUploadProgress(100); // 这里已经正确设置了100%
|
||||
setIsUploading(false);
|
||||
setUploadStatus(SUCCESS);
|
||||
|
@ -297,8 +263,11 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
|||
hasError = true; // 设置错误标记
|
||||
setIsUploading(false);
|
||||
setUploadStatus(ERROR);
|
||||
setUploadMessage(result.message || '文件上传失败,请重试');
|
||||
// 只有当不是用户取消时才显示错误消息
|
||||
if (!isManualCancel.current) {
|
||||
setUploadMessage(result.message || '文件上传失败');
|
||||
message.error(result.message || '文件上传失败');
|
||||
}
|
||||
|
||||
// 中止其他正在进行的上传
|
||||
if (abortController.current) {
|
||||
|
@ -326,6 +295,8 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
|||
// 2. 开始上传
|
||||
const startUpload = async (file: File) => {
|
||||
try {
|
||||
isManualCancel.current=false;
|
||||
uploadCompletedRef.current = false;
|
||||
setIsUploading(true);
|
||||
setUploadStatus(UPLOADING);
|
||||
setUploadMessage('正在准备上传...');
|
||||
|
@ -421,12 +392,17 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
|||
|
||||
// 取消上传
|
||||
const cancelUpload = async () => {
|
||||
// 先更新 ref 再更新 state
|
||||
// uploadCompletedRef.current = true;
|
||||
// _setUploadCompleted(true);
|
||||
if (abortController.current) {
|
||||
abortController.current.abort();
|
||||
abortController.current = null;
|
||||
// 1. 立即标记状态
|
||||
isManualCancel.current = true;
|
||||
uploadCompletedRef.current = true; // 标记完成(阻止 beforeunload)
|
||||
|
||||
// 2. 清空上传队列(阻止新请求)
|
||||
uploadQueue.current = [];
|
||||
// 3. 中止所有进行中的请求
|
||||
const oldController = abortController.current;
|
||||
abortController.current = new AbortController(); // 新建控制器
|
||||
if (oldController) {
|
||||
oldController.abort(); // 中止旧请求
|
||||
}
|
||||
// 如果有正在上传的文件,调用后端取消上传API
|
||||
if (fileId.current) {
|
||||
|
@ -458,11 +434,10 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
|||
<Alert
|
||||
message="重要提示"
|
||||
description={
|
||||
<div style={{ color: "rgb(237, 41, 31)" }}>
|
||||
<div>1. 文件上传后需要组装,需要时间,请耐心等待。</div>
|
||||
<div>
|
||||
2. 文件上传中请勿刷新或者离开页面,否则会导致文件上传失败。
|
||||
</div>
|
||||
<div style={{ color: 'rgb(237, 41, 31)' }}>
|
||||
<div>1. 上传过程中刷新或离开将导致上传中断。</div>
|
||||
<div>2. 大文件上传可能需要较长时间,建议保持网络稳定。</div>
|
||||
<div>3. 最后阶段需要校验文件完整性,请耐心等待。</div>
|
||||
</div>
|
||||
}
|
||||
type="warning"
|
||||
|
@ -530,31 +505,25 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
|||
</div>
|
||||
);
|
||||
|
||||
const handleCancel = () => {
|
||||
if (isUploading) {
|
||||
cancelUpload().finally(() => {
|
||||
resetState(); // 确保取消完成后再重置
|
||||
onCancel();
|
||||
});
|
||||
} else {
|
||||
resetState();
|
||||
onCancel();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="导入镜像"
|
||||
open={visible}
|
||||
onCancel={() => {
|
||||
if (isUploading) {
|
||||
cancelUpload();
|
||||
} else {
|
||||
// 如果不是上传状态,直接重置状态
|
||||
resetState();
|
||||
}
|
||||
onCancel();
|
||||
}}
|
||||
onCancel={handleCancel}
|
||||
footer={[
|
||||
<Button
|
||||
key="close"
|
||||
onClick={() => {
|
||||
if (isUploading) {
|
||||
cancelUpload();
|
||||
} else {
|
||||
resetState();
|
||||
}
|
||||
onCancel();
|
||||
}}
|
||||
>
|
||||
<Button key="close" onClick={handleCancel}>
|
||||
关闭
|
||||
</Button>,
|
||||
]}
|
||||
|
|
|
@ -195,7 +195,7 @@ const ImageList: React.FC = () => {
|
|||
key: 'bt_path',
|
||||
title: 'BT路径',
|
||||
dataIndex: 'bt_path',
|
||||
width: 250,
|
||||
width: 180,
|
||||
defaultVisible: true,
|
||||
ellipsis: true,
|
||||
render: (text: string) => text ? <Tooltip title={text} placement="topLeft">{text}</Tooltip>:'--'
|
||||
|
@ -224,7 +224,7 @@ const ImageList: React.FC = () => {
|
|||
key: 'image_status',
|
||||
title: '镜像状态',
|
||||
dataIndex: 'image_status',
|
||||
width: 80,
|
||||
width: 90,
|
||||
render: (text: number) => (text ? getStatusTag(text) : '--'),
|
||||
defaultVisible: true,
|
||||
},
|
||||
|
@ -232,7 +232,7 @@ const ImageList: React.FC = () => {
|
|||
key: 'create_time',
|
||||
title: '创建时间',
|
||||
dataIndex: 'create_time',
|
||||
width: 180,
|
||||
width: 160,
|
||||
render: (text: string) =>
|
||||
text ? (
|
||||
<Tooltip title={dayjs(text).format('YYYY-MM-DD HH:mm:ss')}>
|
||||
|
@ -247,7 +247,7 @@ const ImageList: React.FC = () => {
|
|||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
width: 100,
|
||||
width: 90,
|
||||
fixed: 'right' as 'right',
|
||||
render: (_: any, record: IMAGES.ImageItem) => (
|
||||
<Space size="small">
|
||||
|
|
|
@ -42,7 +42,7 @@ const LoginPage: React.FC = () => {
|
|||
<SafetyCertificateOutlined className="logo-icon" />
|
||||
<h1 className="brand-title">紫光汇智</h1>
|
||||
</div>
|
||||
<div className="brand-subtitle">VDI 虚拟桌面管理平台</div>
|
||||
<div className="brand-subtitle">Nex管理平台</div>
|
||||
<div className="brand-description">
|
||||
<p>专业的虚拟桌面基础设施解决方案</p>
|
||||
<p>提供安全、高效、灵活的桌面云服务</p>
|
||||
|
@ -69,7 +69,7 @@ const LoginPage: React.FC = () => {
|
|||
<div className="login-form-container">
|
||||
<div className="login-header">
|
||||
<h2 className="login-title">系统登录</h2>
|
||||
<p className="login-subtitle">欢迎使用VDI管理平台</p>
|
||||
<p className="login-subtitle">欢迎使用Nex管理平台</p>
|
||||
</div>
|
||||
|
||||
<Form
|
||||
|
|
|
@ -7,9 +7,9 @@ import { deleteUserGroup, getGroupTree } from '@/services/userList';
|
|||
import {
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
GoldOutlined,
|
||||
PlusOutlined,
|
||||
RedoOutlined,
|
||||
GoldOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
|
@ -387,6 +387,29 @@ const UserListPage: React.FC = () => {
|
|||
};
|
||||
const onDeleteGroup = async () => {
|
||||
if (selectedOrg) {
|
||||
const params: any = {
|
||||
page_size: pageSize,
|
||||
page_num: currentPage,
|
||||
};
|
||||
if (selectedOrg) {
|
||||
params.device_group_id = selectedOrg;
|
||||
}
|
||||
try {
|
||||
const result: any = await getTerminalList(params);
|
||||
const { data } = result || {};
|
||||
const { total = 0 } = data || {};
|
||||
if (total > 0) {
|
||||
message.info("该分组下有终端,请先删除该分组下的所有终端");
|
||||
} else {
|
||||
onDeleteGroupSave();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteGroupSave = async () => {
|
||||
try {
|
||||
const params = {
|
||||
id: selectedOrg,
|
||||
|
@ -400,7 +423,6 @@ const UserListPage: React.FC = () => {
|
|||
} catch (error) {
|
||||
message.error('分组删除失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onSaveGroup = () => {
|
||||
|
|
|
@ -61,9 +61,8 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
|
|||
const handleOk = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
const { image_list } = values || {};
|
||||
const { image_list = [] } = values || {};
|
||||
console.log('image_list=====', image_list);
|
||||
if (image_list && image_list.length > 0) {
|
||||
const list: any[] = [];
|
||||
image_list.forEach((item: any) => {
|
||||
const obj: any = {
|
||||
|
@ -80,11 +79,9 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
|
|||
});
|
||||
const payload: any = {
|
||||
data: list,
|
||||
device_id:device_id,
|
||||
};
|
||||
onBind(payload);
|
||||
} else {
|
||||
message.info('请先选择绑定的镜像');
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('请检查表单字段');
|
||||
}
|
||||
|
@ -131,7 +128,7 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
|
|||
<Form.Item
|
||||
name="image_list"
|
||||
label="选择镜像"
|
||||
rules={[{ required: true, message: '请输入终端型号' }]}
|
||||
rules={[{ required: false, message: '请输入终端型号' }]}
|
||||
>
|
||||
<SelectedTable />
|
||||
</Form.Item>
|
||||
|
|
|
@ -112,7 +112,6 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
|
|||
const values = await form.validateFields();
|
||||
const { user_list = [] } = values || {};
|
||||
const list: any[] = [];
|
||||
if (user_list && user_list.length > 0) {
|
||||
user_list.forEach((item: any) => {
|
||||
const { type, id } = item || {};
|
||||
if (type === 1) {
|
||||
|
@ -149,9 +148,9 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
|
|||
});
|
||||
const payload = {
|
||||
data: list,
|
||||
device_id:device_id,
|
||||
};
|
||||
onBind(payload);
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('请检查表单字段');
|
||||
}
|
||||
|
@ -198,7 +197,7 @@ const BindUserModal: React.FC<UserEditModalProps> = ({
|
|||
<Form.Item
|
||||
name="user_list"
|
||||
label="选择用户"
|
||||
rules={[{ required: true, message: '请选择绑定用户' }]}
|
||||
rules={[{ required: false, message: '请选择绑定用户' }]}
|
||||
>
|
||||
<SelectedTable orgTreeData={orgTreeData} />
|
||||
</Form.Item>
|
||||
|
|
|
@ -394,6 +394,41 @@ const UserListPage: React.FC = () => {
|
|||
|
||||
const onDeleteGroup = async () => {
|
||||
if (selectedOrg) {
|
||||
// try {
|
||||
// const params = {
|
||||
// id: selectedOrg,
|
||||
// };
|
||||
// const res = await deleteUserGroup(params);
|
||||
// const { code } = res || {};
|
||||
// if (code === ERROR_CODE) {
|
||||
// message.success('分组删除成功');
|
||||
// setSelectedOrg(null);
|
||||
// getUserGroupList();
|
||||
// }
|
||||
// } catch (error) {
|
||||
// message.error('分组删除失败');
|
||||
// }
|
||||
const params: any = {
|
||||
page_size: pageSize,
|
||||
page_num: currentPage,
|
||||
};
|
||||
if (selectedOrg) {
|
||||
params.user_group_id = selectedOrg;
|
||||
}
|
||||
try {
|
||||
const result = await getUserList(params);
|
||||
const { data = {} } = result || {};
|
||||
const { total = 0 } = data || {};
|
||||
if (total > 0) {
|
||||
message.info('当前分组下有用户,请先删除用户', 5);
|
||||
} else {
|
||||
ondeleteCroupSave();
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
};
|
||||
|
||||
const ondeleteCroupSave = async () => {
|
||||
try {
|
||||
const params = {
|
||||
id: selectedOrg,
|
||||
|
@ -408,7 +443,6 @@ const UserListPage: React.FC = () => {
|
|||
} catch (error) {
|
||||
message.error('分组删除失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
Loading…
Reference in New Issue