Commit 5f20da70 authored by chenshouchao's avatar chenshouchao

feat: 新增应用环境校验 删除应用环境

parent 92c60990
......@@ -53,6 +53,8 @@ const RESTAPI = {
API_GET_PUBLIC_PROJECT:`${BACKEND_API_URI_PREFIX}/cpp/common/public/project`, // 获取公共环境
API_ACTORENV_BUILDENV:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/buildenv`, // 新增应用环境
API_ACTORENV_LIST:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/list`, // 查询用户的应用环境(算子环境)
API_ACTORENV_DELETE:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/delete`, // 删除用户算子环境
};
export default RESTAPI;
......@@ -52,10 +52,19 @@ const getActorenvList = (params: {
});
};
// 删除用户算子环境
const deleteActorenv = (params: {id: string}) => {
return request({
url:`${Api.API_ACTORENV_DELETE}/${params.id}`,
method: "get",
});
};
export {
getPublicEnv,
getPublicProject,
addActorenvBuildenv,
getActorenvList,
deleteActorenv,
};
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 6备份 5</title>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#136EEF" offset="0%"></stop>
<stop stop-color="#084FC2" offset="100%"></stop>
</linearGradient>
</defs>
<g id="新" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="个人资源-新建应用环境备份-6" transform="translate(-642.000000, -2505.000000)">
<g id="编组-20" transform="translate(106.000000, 996.000000)">
<g id="编组-10备份" transform="translate(392.000000, 1479.000000)">
<g id="编组-18" transform="translate(97.000000, 30.000000)">
<g id="编组-6备份-5" transform="translate(47.000000, 0.000000)">
<rect id="矩形" x="0" y="0" width="32" height="32"></rect>
<g id="编组-12备份-1" transform="translate(4.000000, 1.000000)" fill-rule="nonzero">
<path d="M2.52631579,0 L21.4736842,0 C22.8689053,0 24,1.1192875 24,2.5 L24,22.5 L17.6842105,30 L2.52631579,30 C1.13106947,30 0,28.8806875 0,27.5 L0,2.5 C0,1.1192875 1.13106947,0 2.52631579,0 Z" id="矩形备份-11" fill="url(#linearGradient-1)"></path>
<path d="M13.5789474,15 L13.5789474,18.5 C13.5788842,18.7321875 13.4965263,18.95475 13.35,19.1186875 C13.2034105,19.282875 13.0047789,19.375 12.7976842,19.375 L11.2023158,19.375 C10.9952842,19.375 10.7966526,19.2829375 10.65,19.118875 C10.5034105,18.954875 10.4210526,18.732125 10.4210526,18.4998125 L10.4210526,15 L13.5789474,15 Z M12.9473684,16.875 L11.0526316,16.875 L11.0526316,18.75 L12.9473684,18.75 L12.9473684,16.875 Z M13.5789474,11.25 L13.5789474,13.125 L11.6842105,13.125 L11.6842105,11.25 L13.5789474,11.25 Z M12.0032842,0 L12.0032842,1.7237625 L13.5789474,1.7237625 L13.5789474,3.447525 L12.0031579,3.447525 L12.0031579,5.043575 L13.5789474,5.043575 L13.5789474,6.7673125 L12.0032842,6.7673125 L12.0032842,8.4699375 L13.5789474,8.4699375 L13.5789474,10.1725625 L12.0032842,10.1725625 L12.0032842,11.875 L10.4210526,11.875 L10.4210526,10.172375 L11.9967158,10.172375 L11.9967158,8.4698125 L10.4210526,8.4698125 L10.4210526,6.7673125 L11.9967158,6.7673125 L11.9967158,5.043575 L10.4210526,5.043575 L10.4210526,3.3198125 L11.9967158,3.3198125 L11.9967158,1.7237625 L10.4210526,1.7237625 L10.4210526,0 L12.0032842,0 Z" id="形状" fill="#FFFFFF"></path>
<polygon id="矩形备份-13" fill="#FFFFFF" opacity="0.5" points="17.6842105 22.5 24 22.5 20.8421053 26.25 17.6842105 30"></polygon>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
......@@ -40,7 +40,7 @@ interface IMyTableProps {
totalElements?: number; // 数据总量 不止是列表渲染的长度
sortState?: sortState; // 排序状态
setSortState?: any; // 设置排序状态
paginationType?: "simple" | "complex"; // 分页组件的类型 simple简式 complex复杂、带每页数量切换、总数等
paginationType?: "simple" | "complex"; // 分页组件的类型 simple简式 complex复杂、带每页数量切换、总数等
rowsPerPage?: number; // 每页多少条数据
handleChangeRowsPerPage?: any; // 每页多少条数据变化
nodataText?: any; // 无数据文案
......
......@@ -89,6 +89,12 @@
position: relative;
margin-bottom: 24px;
}
.tips {
color: rgba(255, 78, 78, 1);
margin-top: 2px;
line-height: 20px;
font-size: 12px;
}
.uploadBox {
border-radius: 4px;
border: 1px dashed #dde1e6;
......@@ -97,6 +103,31 @@
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 14px;
line-height: 22px;
color: rgba(194, 198, 204, 1);
}
.uploaderIcon {
margin-bottom: 8px;
}
.progressContent {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 7px;
}
.progressBox {
width: 200px;
border-radius: 3px;
background-color: rgba(235, 237, 240, 1);
margin-right: 12px;
height: 6px;
}
.progress {
border-radius: 3px;
background-color: rgba(19, 110, 250, 1);
height: 6px;
}
.isDragActive {
border: 1px dashed #000;
......@@ -104,3 +135,10 @@
.formItemHaveHelperText {
margin-bottom: 10px;
}
.uploadAgain {
cursor: pointer;
color: rgba(19, 110, 250, 1);
font-size: 12px;
line-height: 20px;
margin-top: 12px;
}
......@@ -10,8 +10,13 @@ import { useDropzone } from "react-dropzone";
import SwitchBatchFolw from "@/views/ResourceCenter/components/SwitchBatchFolw";
import { getLoaclStorageOfKey } from "@/api/fileserver/utils";
import Code from "@/components/CommonComponents/Code";
import { useMessage } from "@/components/MySnackbar";
import * as Base64 from "js-base64";
import CloseIcon from "@mui/icons-material/Close";
import { getDataFileToken, hpczone } from "@/api/project_api";
import { checkIsNumberLetterChinese } from "@/utils/util";
import uploaderIcon from "@/assets/project/uploaderIcon.svg";
import zip from "@/assets/resourceCenter/zip.svg";
import {
getPublicEnv,
getPublicProject,
......@@ -26,6 +31,7 @@ type IAddEnvironmentProps = {
const AddEnvironment = (props: IAddEnvironmentProps) => {
const { setAddopen } = props;
const Message = useMessage();
let tokenInfo = getTokenInfo();
const [hpczoneList, setHpczoneList] = useState<Array<any>>([]);
const [publicProjectId, setPublicProjectId] = useState("");
......@@ -37,15 +43,25 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
const [desc, setDesc] = useState("");
const [baseEnvId, setBaseEnvId] = useState("");
const [filePaths, setFilePaths] = useState<Array<string>>([]);
const [isUploading, setIsUploading] = useState(false);
const [fileName, setFileName] = useState("");
// const [bashScript, setBashScript] = useState('');
const [envList, setEnvList] = useState<Array<any>>([]);
const [progress, setProgress] = useState("0%");
const [code, setCode] = useState("");
console.log(code);
const [upload, setUpload] = useState<any>(() => {});
const [nameHelper, setNameHelper] = useState({
error: false,
text: "30字符以内,仅限字母、数字、中文",
});
const [descHelper, setDescHelper] = useState({
error: false,
text: "",
});
const [filePathsHelper, setFilePathsHelper] = useState({
error: false,
text: "",
});
const onDrop = useCallback(
(acceptedFiles: any) => {
let origin = "";
......@@ -56,6 +72,10 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
});
const fileInfo = acceptedFiles[0];
const { path } = fileInfo;
if (path.indexOf(".zip") === -1) {
Message.error("请上传压缩文件");
return;
}
const userInfo = getLoaclStorageOfKey("userInfo");
const homeDirectoryMountPoint = userInfo?.homeDirectoryMountPoint;
const url =
......@@ -80,17 +100,21 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
setProgress(`0%`);
},
onError: function (error: string) {
setIsUploading(false);
console.log("Failed because: " + error);
},
onProgress: function (bytesUploaded: number, bytesTotal: number) {
setProgress(`${bytesUploaded / bytesTotal}%`);
console.log(`${bytesUploaded / bytesTotal}%`);
setProgress(`${(bytesUploaded * 100) / bytesTotal}%`);
},
onSuccess: function () {
setIsUploading(false);
setFilePaths([`/ProjectData/${homeDirectoryMountPoint}/${path}`]);
setFileName(path);
},
});
setIsUploading(true);
upload.start();
setUpload(upload);
},
[
fileToken,
......@@ -98,13 +122,13 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
tokenInfo?.access_token,
hpczoneList,
publicZoneId,
Message,
]
);
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
const { run: getPublicEnvFn } = useMyRequest(getPublicEnv, {
onSuccess: (res: any) => {
console.log(res);
let arr = res.data.map((item: any) => {
return {
label: item.title,
......@@ -122,7 +146,6 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
const { run: getPublicProjectFn } = useMyRequest(getPublicProject, {
onSuccess: (res: any) => {
console.log(res);
setPublicProjectId(res.data[0].id);
setPublicZoneId(res.data[0].zoneId);
getDataFileTokenFn({ id: res.data[0].id });
......@@ -131,16 +154,19 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
const { run: getDataFileTokenFn } = useMyRequest(getDataFileToken, {
onSuccess: (res: any) => {
console.log(res);
setFileToken(res.data);
},
});
const { run: addActorenvBuildenvFn } = useMyRequest(addActorenvBuildenv, {
onSuccess: (res: any) => {
console.log(res);
},
});
const { run: addActorenvBuildenvFn, loading } = useMyRequest(
addActorenvBuildenv,
{
onSuccess: () => {
Message.success("开始构建应用环境");
setAddopen(false);
},
}
);
useEffect(() => {
getPublicProjectFn();
......@@ -162,10 +188,12 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
const handleNameChange = (e: any) => {
setName(e.target.value);
checkName(e.target.value);
};
const handleDescChange = (e: any) => {
setDesc(e.target.value);
checkDesc(e.target.value);
};
const handelBaseEnvIdChange = (e: any) => {
setBaseEnvId(e);
......@@ -175,17 +203,99 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
setComputeType(envList[index].computeType);
};
const checkName = (name: string) => {
if (!name) {
setNameHelper({
error: true,
text: "环境名称不能为空",
});
return true;
} else if (name.length > 30) {
setNameHelper({
error: true,
text: "格式不正确,30字符以内,仅限字母、数字、中文",
});
return true;
} else if (!checkIsNumberLetterChinese(name)) {
setNameHelper({
error: true,
text: "格式不正确,30字符以内,仅限字母、数字、中文",
});
return true;
} else {
setNameHelper({
error: false,
text: "30字符以内,仅限字母、数字、中文",
});
return false;
}
};
const checkDesc = (desc: string) => {
if (!desc) {
setDescHelper({
error: true,
text: "环境描述不能为空",
});
return true;
} else if (desc.length > 300) {
setDescHelper({
error: true,
text: "描述限300字",
});
return true;
} else {
setDescHelper({
error: false,
text: "",
});
return false;
}
};
const checkFile = (filePaths: any) => {
if (filePaths.length === 0) {
setFilePathsHelper({
error: true,
text: "请上传环境压缩包",
});
return true;
} else {
setFilePathsHelper({
error: false,
text: "",
});
return false;
}
};
const handleSubmit = () => {
addActorenvBuildenvFn({
title: name,
desc,
baseEnvId,
type: taskType,
filePaths,
bashScript: Base64.encode(code),
publicProjectId,
computeType,
});
if (
!checkName(name) &&
!checkDesc(desc) &&
!checkFile(filePaths) &&
baseEnvId &&
code
) {
addActorenvBuildenvFn({
title: name,
desc,
baseEnvId,
type: taskType,
filePaths,
bashScript: Base64.encode(code),
publicProjectId,
computeType,
});
} else {
Message.error("请完善环境信息");
}
};
const handleAbort = () => {
upload.abort(true);
setIsUploading(false);
setFilePaths([]);
};
return (
......@@ -227,11 +337,12 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
<MyInput
value={desc}
id="desc"
// label="项目描述"
multiline
rows={4}
placeholder="请输入项目描述"
onChange={handleDescChange}
error={descHelper.error}
helperText={descHelper.text}
/>
<span
style={{
......@@ -262,17 +373,72 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
<span className={style.download}>下载模板</span>
</div>
<div className={style.formItem}>
<div
className={classNames({
[style.uploadBox]: true,
[style.isDragActive]: isDragActive,
})}
{...getRootProps()}
>
<input {...getInputProps()} />
<span>点击选择环境包或将文件</span>
<span>拖到此处上传</span>
</div>
{filePaths.length === 0 && !isUploading && (
<div
className={classNames({
[style.uploadBox]: true,
[style.isDragActive]: isDragActive,
})}
{...getRootProps()}
>
<input {...getInputProps()} />
<img
className={style.uploaderIcon}
src={uploaderIcon}
alt=""
/>
<span>点击选择环境包或将文件</span>
<span>拖到此处上传</span>
</div>
)}
{isUploading && (
<div
className={classNames({
[style.uploadBox]: true,
})}
>
<div className={style.progressContent}>
<div className={style.progressBox}>
<div
className={style.progress}
style={{ width: progress }}
></div>
</div>
<CloseIcon
onClick={() => handleAbort()}
sx={{
fontSize: "16px",
color: "#C2C6CC",
cursor: "pointer",
":hover": {
background: "#f0f2f5",
borderRadius: "2px",
},
}}
/>
</div>
<span>上传中,请稍等...</span>
</div>
)}
{filePaths.length !== 0 && !isUploading && (
<div
className={classNames({
[style.uploadBox]: true,
})}
>
<img className={style.uploaderIcon} src={zip} alt="" />
<span>{fileName}</span>
<span
onClick={() => setFilePaths([])}
className={style.uploadAgain}
>
重新上传
</span>
</div>
)}
{filePathsHelper.text && (
<div className={style.tips}>{filePathsHelper.text}</div>
)}
</div>
</div>
<div className={style.codeBox}>
......@@ -289,7 +455,11 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
</div>
</div>
<div className={style.button}>
<MyButton text="开始构建" onClick={() => handleSubmit()}></MyButton>
<MyButton
text="开始构建"
onClick={() => handleSubmit()}
loading={loading}
></MyButton>
</div>
</div>
</div>
......
import { deleteActorenv } from "@/api/resourceCenter";
import useMyRequest from "@/hooks/useMyRequest";
import { useMessage } from "@/components/MySnackbar";
import MyDialog from "@/components/mui/MyDialog";
interface IDeleteEnvironmentProps {
id: string;
open: boolean;
setDeleteOpen: any;
}
const DeleteEnvironment = (props: IDeleteEnvironmentProps) => {
const { open, id, setDeleteOpen } = props;
const Message = useMessage();
const { run, loading } = useMyRequest(deleteActorenv, {
onSuccess: () => {
setDeleteOpen(false);
Message.success("删除成功");
},
});
const handleConfirm = () => {
console.log(id);
run({ id });
};
return (
<MyDialog
okColor="error"
open={open}
onClose={() => setDeleteOpen(false)}
title="删除环境"
okText="删除"
onConfirm={() => handleConfirm()}
loading={loading}
>
<span>删除后无法恢复,确认删除吗?</span>
</MyDialog>
);
};
export default DeleteEnvironment;
const EnvironmentDetails = () => {
return <div>EnvironmentDetails</div>;
};
export default EnvironmentDetails;
......@@ -10,3 +10,11 @@
.tableBox {
height: calc(100vh - 177px);
}
.statusBox {
display: flex;
justify-content: flex-start;
align-items: center;
}
.status {
margin-left: 4px;
}
......@@ -7,7 +7,12 @@ import MyButton from "@/components/mui/MyButton";
import MyTable from "@/components/mui/MyTableNew";
import useMyRequest from "@/hooks/useMyRequest";
import { getActorenvList } from "@/api/resourceCenter";
import moment from "moment";
import jobFail from "@/assets/project/jobFail.svg";
import jobRun from "@/assets/project/jobRun.svg";
import jobSue from "@/assets/project/jobSue.svg";
import AddEnvironment from "./AddEnvironment";
import DeleteEnvironment from "./DeleteEnvironment";
const UserResourcesEnvironment = () => {
const [addOpen, setAddopen] = useState(false);
......@@ -17,6 +22,8 @@ const UserResourcesEnvironment = () => {
const [page, setPage] = useState(0);
const [count, setCount] = useState(0);
const [size, setSize] = useState(20);
const [id, setId] = useState("");
const [deleteOpen, setDeleteOpen] = useState(false);
const headCells: Array<any> = [
{
id: "title",
......@@ -28,9 +35,9 @@ const UserResourcesEnvironment = () => {
width: 100,
},
{
id: "",
id: "createdTime",
label: "创建时间",
width: 160,
width: 180,
},
{
id: "status",
......@@ -40,7 +47,7 @@ const UserResourcesEnvironment = () => {
{
id: "caozuo",
label: "操作",
width: 160,
width: 140,
},
];
const { run: getList } = useMyRequest(getActorenvList, {
......@@ -56,13 +63,15 @@ const UserResourcesEnvironment = () => {
};
useEffect(() => {
getList({
page,
size,
title,
type,
});
}, [getList, page, size, title, type]);
if (!addOpen && !deleteOpen) {
getList({
page,
size,
title,
type,
});
}
}, [getList, page, size, title, type, addOpen, deleteOpen]);
const renderType = (item: any) => {
if (item.type === "BATCH") {
......@@ -76,18 +85,47 @@ const UserResourcesEnvironment = () => {
const renderStatus = (item: any) => {
if (item.status === "PENDING") {
return "准备构建";
return (
<div className={style.statusBox}>
<img src={jobRun} alt="" />
<span className={style.status}>准备构建</span>
</div>
);
} else if (item.status === "CREATING") {
return "正在构建";
return (
<div className={style.statusBox}>
<img src={jobRun} alt="" />
<span className={style.status}>正在构建</span>
</div>
);
} else if (item.status === "FAILED") {
return "构建失败";
return (
<div className={style.statusBox}>
<img src={jobFail} alt="" />
<span className={style.status}>构建失败</span>
</div>
);
} else if (item.status === "CREATED") {
return "构建完成";
return (
<div className={style.statusBox}>
<img src={jobSue} alt="" />
<span className={style.status}>构建完成</span>
</div>
);
} else {
return "";
}
};
const renderCreatedTime = (item: any) => {
return moment(new Date(item.createdTime)).format("yyyy-MM-DD hh:mm:ss");
};
const handleDelete = (item: any) => {
setId(item.id);
setDeleteOpen(true);
};
const renderButtons = (item: any) => {
if (item.status === "FAILED") {
return (
......@@ -105,6 +143,7 @@ const UserResourcesEnvironment = () => {
size="medium"
/>
<MyButton
onClick={() => handleDelete(item)}
text="删除"
style={{
position: "relative",
......@@ -190,6 +229,7 @@ const UserResourcesEnvironment = () => {
type: renderType(item),
status: renderStatus(item),
caozuo: renderButtons(item),
createdTime: renderCreatedTime(item),
}))}
headCells={headCells}
fixedHead={true}
......@@ -200,6 +240,13 @@ const UserResourcesEnvironment = () => {
></MyTable>
</div>
{addOpen && <AddEnvironment setAddopen={setAddopen}></AddEnvironment>}
{deleteOpen && (
<DeleteEnvironment
id={id}
open={deleteOpen}
setDeleteOpen={setDeleteOpen}
></DeleteEnvironment>
)}
</div>
);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment