Commit 73c3aef2 authored by chenshouchao's avatar chenshouchao

Merge branch 'feat-20221012-environment' into 'staging'

cn-Feat 20221012 environment

See merge request !146
parents 60993455 567ba4cd
This diff is collapsed.
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-13 09:56:57
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-09-01 15:30:28
* @LastEditTime: 2022-10-19 21:14:11
* @FilePath: /bkunyun/src/api/api_manager.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -52,6 +52,9 @@ const RESTAPI = {
API_GET_PUBLIC_ENV:`${BACKEND_API_URI_PREFIX}/cpp/common/public/env`, // 获取公共环境
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`, // 删除用户算子环境
API_ACTORENV_DETAIL:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/detail`, // 查询应用环境的详情信息
};
export default RESTAPI;
......@@ -260,13 +260,14 @@ class CloudEController {
}
// 获取文件文本内容(字符串)
static JobFileDownloadText(url: any, filetoken: string, projectId: string) {
static JobFileDownloadText(url: any, filetoken: string, projectId: string, fileServerEndPoint: string = '') {
if (ApiUtils.getAuthorizationHeaders(headers)) {
url = url + urlToken(filetoken, projectId);
headers["Cache-Control"] = "no-cache";
headers["Content-Type"] = "multipart/form-data";
let origin = fileServerEndPoint ? fileServerEndPoint : APIOPTION()
return axios.get(
APIOPTION() + "/download" + url + "&showhidden=false",
origin + "/download" + url + "&showhidden=false",
{
headers: headers,
}
......
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-19 17:09:23
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 21:14:35
* @FilePath: /bkunyun/src/api/resourceCenter.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import request from "@/utils/axios/service";
import Api from "./api_manager";
......@@ -12,6 +20,13 @@ type addActorenvBuildenvParams = {
publicProjectId: string,
}
/** 获取算子列表请求参数 */
export interface IOperatorListParams {
keyword: string;
productId: string;
type: string;
}
// 获取公共项目
const getPublicProject = () => {
return request({
......@@ -34,7 +49,47 @@ const addActorenvBuildenv = (params: addActorenvBuildenvParams) => {
return request({
url: Api.API_ACTORENV_BUILDENV,
method: "post",
data: {...params, name: '123'},
data: params,
});
};
// 获取公共环境
const getActorenvList = (params: {
type: 'BATCH' | 'FLOW' | '',
page: number,
size: number,
title?: string,
}) => {
return request({
url: Api.API_ACTORENV_LIST,
method: "get",
params,
});
};
// 删除用户算子环境
const deleteActorenv = (params: {id: string}) => {
return request({
url:`${Api.API_ACTORENV_DELETE}/${params.id}`,
method: "delete",
});
};
// 获取算子列表
const getOperatorList = (data: IOperatorListParams) => {
return request({
url: Api.API_OPERATOR_LIST,
method: "get",
data
});
}
// 查询应用环境的详情信息
const getActorenvDetail = (params: {id: string}) => {
return request({
url:`${Api.API_ACTORENV_DETAIL}/${params.id}`,
method: "get",
});
};
......@@ -42,4 +97,8 @@ export {
getPublicEnv,
getPublicProject,
addActorenvBuildenv,
getActorenvList,
deleteActorenv,
getOperatorList,
getActorenvDetail,
};
<?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
......@@ -7,9 +7,10 @@ import NotFound from "@/views/404";
import useMyRequest from "@/hooks/useMyRequest";
import { useEffect } from "react";
import { menu } from "@/api/routes_api";
import { IProductOption } from "@/store/modules/productList";
const useMyRouter = () => {
const { permissionStore, menuStore } = useStores();
const { permissionStore, menuStore, productListStore } = useStores();
const userInfo = useMyRequest(current);
const menuInfo = useMyRequest(menu);
......@@ -42,16 +43,42 @@ const useMyRouter = () => {
}
});
}
for (let item of menuInfo.res.data) {
let childrenRoutes: any = []
for (let route of item.routes) {
route.element = elements[route.element] || NotFound;
route.path = `/product/${item.id}${route.path}`;
if (Array.isArray(route.children)) {
route.children.forEach((childrenItem: any, index: number) => {
if (childrenItem.path) {
childrenRoutes.push({
...childrenItem,
element: elements[childrenItem.element] || NotFound,
path: `${route.path}${childrenItem.path}`,
show: false
})
}
})
route.children = route.children.filter((childrenItem: any) => !childrenItem.path)
}
}
permissionStore.setAddRoutes(item.routes);
// permissionStore.setAddRoutes(item.routes);
permissionStore.setAddRoutes([...childrenRoutes, ...item.routes]);
}
menuStore.initMenu(menuInfo.res.data);
permissionStore.initAllRoutes();
// /** 所有产品列表 */
const newProductList:IProductOption[] = [];
for (let item of menuInfo.res.data) {
if (item.type === "product") {
newProductList.push({
label: item.name,
value: item.id,
});
}
}
productListStore.setProductList(newProductList)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
......
.is_require::after {
display: inline-block;
content: "*";
color: #ff4e4e;
margin-right: 4px;
}
.dynamicFormItem_label {
display: block;
font-size: 14px;
font-weight: 600;
color: #1e2633;
}
.dynamicFormItem_col {
position: relative;
margin-bottom: 20px;
}
.dynamicFormItem_col_helpText {
margin-bottom: 46px;
}
.dynamicFormItem_content {
font-size: 14px;
color: #736f7f;
}
.dynamicFormitem_child {
position: relative;
flex: 1;
width: 100%;
text-align: left;
}
.dynamicFormitem_helpText {
position: absolute;
bottom: -36px;
font-size: 13px;
line-height: 1.15;
color: #9894a5;
}
.dynamicFormitem_helpText_warning {
color: #ff4e4e;
}
.dynamicFormitem_errorTips {
position: absolute;
bottom: -20px;
font-size: 13px;
color: #ff4e4e;
}
import React from "react";
import classNames from "classnames";
import styles from "./index.module.css";
import "./label.css";
// import { Control, ValidationRules } from 'react-hook-form/dist/types/form';
export interface IHelpText {
value: string /** 帮助文字内容 */;
className?: string;
type?: "normal" | "warning" /** 默认normal */;
}
export interface IFormItemBoxProps {
/** label 标签的文本 */
label?: string;
/** 标签宽度 */
labelWidth?: number;
/** 标签文本对齐方式 */
labelAlign?: "left" | "center" | "right";
/** 标签文本的className */
labelClassName?: string;
/** 是否必填 */
require?: boolean;
/** */
children: React.ReactNode;
className?: string;
/** 最外层class */
mainClass?: string;
/** 校验信息 */
errorMessage?: string;
errorClassName?: string;
itemFlex?: "row" | "column";
/** 帮助文字提示信息 */
helpText?: IHelpText;
}
const FormItemBox: React.FunctionComponent<IFormItemBoxProps> = (
props: IFormItemBoxProps
) => {
const {
className,
label,
children,
require,
labelWidth,
labelAlign,
errorMessage,
itemFlex,
mainClass,
helpText,
labelClassName,
errorClassName,
} = props;
return (
<div
className={classNames(
styles.dynamicFormItem_div,
mainClass,
helpText && helpText.value && styles.dynamicFormItem_col_helpText
)}
>
<div
className={classNames(
styles.dynamicFormItem_content,
`dynamicFormItem-${itemFlex}`,
className
)}
>
<span
style={{ width: `${labelWidth}px` }}
className={classNames(
styles.dynamicFormItem_label,
{ [styles.is_require]: require },
`align-${itemFlex === "row" ? labelAlign : "left"}`,
labelClassName
)}
>
{label}
</span>
<div className={styles.dynamicFormitem_child}>
{children}
{errorMessage && (
<div
className={classNames(
styles.dynamicFormitem_errorTips,
errorClassName
)}
>
{errorMessage}
</div>
)}
<div
className={classNames(
styles.dynamicFormitem_helpText,
helpText &&
helpText.type === "warning" &&
styles.dynamicFormitem_helpText_warning,
helpText && helpText.className
)}
>
{helpText?.value}
</div>
</div>
</div>
</div>
);
};
FormItemBox.defaultProps = {
labelWidth: 100,
labelAlign: "right",
itemFlex: "row",
};
export default FormItemBox;
.dynamicFormItem-row {
display: flex;
align-items: center;
}
.dynamicFormItem-column {
display: flex;
flex-direction: column;
align-items: flex-start;
}
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2021-12-04 15:46:25
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-21 18:00:58
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 18:41:04
* @FilePath: /lionet-slb-pc/src/components/SearchView/components/Collapse.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -13,6 +13,7 @@ import FormHelperText from "@mui/material/FormHelperText";
import Select, { SelectChangeEvent, SelectProps } from "@mui/material/Select";
import { createTheme, ThemeProvider } from "@mui/material";
import selectActive from "@/assets/project/selectActive.svg";
import { useState } from "react";
export interface IOption {
label: string;
......@@ -192,7 +193,10 @@ export default function MySelect(props: IProps) {
},
});
const [insideValue, setInsideValue] = useState<string>("");
const handleChange = (e: SelectChangeEvent<unknown>) => {
setInsideValue(e.target.value as string);
onChange && onChange(e.target.value as string);
};
......@@ -214,7 +218,7 @@ export default function MySelect(props: IProps) {
size="small"
multiple={multiple}
{...other}
value={value || ""}
value={value || insideValue || ""}
onChange={handleChange}
>
{options.length
......
......@@ -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; // 无数据文案
......
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-24 14:32:32
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-18 10:54:54
* @FilePath: /bkunyun/src/router/index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
import { AnyMap } from "immer/dist/internal";
......@@ -13,6 +13,7 @@ import MenuLayout from "@/views/MenuLayout";
import * as React from "react";
import NotFound from "@/views/404";
import Demo from "@/views/demo";
import SeeEnv from "@/views/ResourceCenter/UserResources/UserResourcesEnvironment/SeeEnv";
import ProjectSetting from "@/views/Project/ProjectSetting";
import ProjectData from "@/views/Project/ProjectData";
import ProjectWorkbench from "@/views/Project/ProjectWorkbench";
......@@ -52,7 +53,9 @@ export const elements: {
}) => JSX.Element | any;
} = {
Demo: Demo,
SeeTemplate: Demo,
UserResources: UserResources,
SeeEnv: SeeEnv,
ProjectSetting: ProjectSetting,
ProjectData: ProjectData,
ProjectWorkbench: ProjectWorkbench,
......
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-09 20:41:05
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-06-14 20:55:22
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 16:21:50
* @FilePath: /bkunyun/src/store/index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -12,10 +12,11 @@ import permissionStore from "./modules/permission";
import menuStore from "./modules/menu";
import currentProjectStore from "./modules/currentProject";
import fileListStore from "./modules/fileList";
import productListStore from './modules/productList'
configure({ enforceActions: "always" });
export const stores = { permissionStore, menuStore, currentProjectStore, fileListStore };
export const stores = { permissionStore, menuStore, currentProjectStore, fileListStore, productListStore };
/** Store类型 */
export type Stores = typeof stores;
......
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-07-11 11:49:55
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 16:25:27
* @FilePath: /bkunyun/src/store/modules/currentProject.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { makeAutoObservable } from "mobx";
export interface IProductOption{
label: string;
value: string;
}
class newProductList {
constructor() {
makeAutoObservable(this);
}
// 产品列表
productList: IProductOption[] = [];
setProductList = (list: IProductOption[]) => {
this.productList = list;
};
}
const productListStore = new newProductList();
export default productListStore;
......@@ -164,10 +164,10 @@ const OperatorList = observer((props: IProps) => {
const dom = document.getElementById("customOperatorFlow");
const clientX = e.clientX;
const clientY = e.clientY;
const upperLeftPointX = Number(dom?.offsetLeft);
const upperLeftPointY = Number(dom?.offsetTop);
const lowerRightX = Number(upperLeftPointX) + Number(dom?.offsetWidth);
const lowerRightY = Number(upperLeftPointY) + Number(dom?.offsetHeight);
const upperLeftPointX = Number(dom?.getBoundingClientRect()?.left);
const upperLeftPointY = Number(dom?.getBoundingClientRect()?.top);
const lowerRightX = Number(upperLeftPointX) + Number(dom?.clientWidth);
const lowerRightY = Number(upperLeftPointY) + Number(dom?.clientHeight);
if (
clientX > upperLeftPointX &&
clientY > upperLeftPointY &&
......@@ -178,7 +178,7 @@ const OperatorList = observer((props: IProps) => {
const newOperatorItem = getNewOperatorItem(
newDragItem,
clientX - upperLeftPointX,
clientY - upperLeftPointY - 42
clientY - upperLeftPointY
);
const newVal = cloneDeep(operatorList);
newVal.push(newOperatorItem);
......
......@@ -85,7 +85,8 @@ const MenuLayout = observer(() => {
key={"sidebar" + index}
className={classnames({
[style.listItem]: true,
[style.active]: `/v3${item.path}` === pathname,
[style.active]: pathname.indexOf(`/v3${item.path}`) !== -1,
// [style.active]: `/v3${item.path}` === pathname,
})}
style={
`/v3${item.path}` === pathname
......
......@@ -32,7 +32,7 @@
display: flex;
border-radius: 4px;
margin-bottom: 24px;
height: 600px;
height: 580px;
}
.form {
width: 368px;
......@@ -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,7 +103,42 @@
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;
}
.formItemHaveHelperText {
margin-bottom: 10px;
}
.uploadAgain {
cursor: pointer;
color: rgba(19, 110, 250, 1);
font-size: 12px;
line-height: 20px;
margin-top: 12px;
}
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;
.titleBox {
padding: 14px 24px;
display: flex;
justify-content: flex-start;
align-items: center;
border-bottom: 1px solid rgba(235, 237, 240, 1);
}
.goBackIcon {
width: 22px;
height: 22px;
/* background-color: #fff; */
/* border-radius: 4px; */
/* margin-right: 12px; */
/* box-shadow: 0px 2px 5px 0px rgba(3, 47, 105, 0.09); */
cursor: pointer;
}
.title {
margin-left: 2px;
font-size: 18px;
line-height: 26px;
color: rgba(30, 38, 51, 1);
font-weight: 550;
margin-right: 16px;
}
.type {
background-color: rgba(235, 237, 240, 1);
font-size: 12px;
line-height: 20px;
padding: 1px 9px;
color: #565c66;
border-radius: 2px;
}
.baseTitle {
padding: 19px 24px 16px;
line-height: 24px;
font-size: 16px;
color: rgba(30, 38, 51, 1);
font-weight: 550;
}
.basicInfoBox {
border: 1px solid #ebedf0;
border-radius: 4px;
margin: 0 24px;
}
.logsTitle {
padding: 24px 24px 16px;
line-height: 24px;
font-size: 16px;
color: rgba(30, 38, 51, 1);
font-weight: 550;
}
.statusBox {
display: flex;
justify-content: flex-start;
align-items: center;
}
.status {
margin-left: 4px;
}
.LogViewBox {
margin: 0 24px;
position: relative;
}
import style from "./index.module.css";
import React, { useState, useEffect, useMemo } from "react";
import LogView from "@/views/ResourceCenter/components/LogView";
import { useNavigate } from "react-router-dom";
import BasicInfo, {
IInfoItem,
} from "@/views/ResourceCenter/components/BasinInfo";
import { getActorenvDetail } from "@/api/resourceCenter";
import useMyRequest from "@/hooks/useMyRequest";
import goback from "@/assets/project/goback.svg";
import { useLocation } from "react-router-dom";
import jobFail from "@/assets/project/jobFail.svg";
import jobRun from "@/assets/project/jobRun.svg";
import jobSue from "@/assets/project/jobSue.svg";
const SeeEnv = () => {
const navigate = useNavigate();
const location = useLocation();
const [id, setId] = useState("");
const [info, setInfo] = useState<any>({});
const [infoListTop, setInfoListTop] = useState<Array<IInfoItem>>([]);
const [infoListBot, setInfoListBot] = useState<Array<IInfoItem>>([]);
const [logs, setLogs] = useState<Array<any>>([]);
const getStatus = (item: any) => {
if (item.status === "PENDING") {
return (
<span className={style.statusBox}>
<img src={jobRun} alt="" />
<span className={style.status}>准备构建</span>
</span>
);
} else if (item.status === "CREATING") {
return (
<span className={style.statusBox}>
<img src={jobRun} alt="" />
<span className={style.status}>正在构建</span>
</span>
);
} else if (item.status === "FAILED") {
return (
<span className={style.statusBox}>
<img src={jobFail} alt="" />
<span className={style.status}>构建失败</span>
</span>
);
} else if (item.status === "CREATED") {
return (
<span className={style.statusBox}>
<img src={jobSue} alt="" />
<span className={style.status}>构建完成</span>
</span>
);
} else {
return "";
}
};
const { run, loading } = useMyRequest(getActorenvDetail, {
onSuccess: (res) => {
console.log(res);
setInfo(res.data);
let data = res.data;
setInfoListTop([
{
label: "构建状态",
value: getStatus(data),
},
{
label: "创建时间",
value: data.createdTime,
},
{
label: "构建时长",
value: data.costTime,
},
{
label: "构建成本",
value: "¥" + data.feeCost,
},
{
label: "基础环境",
value: data.baseEnvName,
},
]);
setInfoListBot([
{
label: "描述",
value: data.desc,
},
]);
setLogs(
data.tasks
.filter((task: any) => task.outLog)
.map((task: any) => ({
logName: `${task.title}.log`,
logPath: task.outLog,
}))
);
},
});
useEffect(() => {
if (id) {
run({ id });
}
}, [id, run]);
useEffect(() => {
const locationInfo: any = location?.state;
console.log(locationInfo);
setId(locationInfo.id);
}, [location, setId]);
return (
<div className={style.seeEnv}>
<div className={style.titleBox}>
<img
className={style.goBackIcon}
src={goback}
alt=""
onClick={() => navigate("/product/resourceCenter/userResources")}
/>
<div className={style.title}>{info.title}</div>
<div className={style.type}>
{info.type === "BATCH" ? "批式环境" : "流式环境"}
</div>
</div>
<div className={style.infoBox}>
<div className={style.baseTitle}>基础信息</div>
<div className={style.basicInfoBox}>
<BasicInfo infoList={infoListTop} />
<BasicInfo infoList={infoListBot} />
</div>
</div>
<div className={style.logsBox}>
<div className={style.logsTitle}>日志</div>
<div className={style.LogViewBox}>
<LogView logs={logs}></LogView>
</div>
</div>
</div>
);
};
export default SeeEnv;
.environment {
padding: 19px 24px 0;
}
.top {
display: flex;
......@@ -6,3 +7,14 @@
justify-content: space-between;
margin-bottom: 20px;
}
.tableBox {
height: calc(100vh - 177px);
}
.statusBox {
display: flex;
justify-content: flex-start;
align-items: center;
}
.status {
margin-left: 4px;
}
// 应用环境
import { useState } from "react";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import style from "./index.module.css";
import SearchInput from "@/components/BusinessComponents/SearchInput";
import MySelect from "@/components/mui/MySelect";
import MyButton from "@/components/mui/MyButton";
import MyTable from "@/components/mui/MyTableNew";
import useMyRequest from "@/hooks/useMyRequest";
import { getActorenvList } from "@/api/resourceCenter";
import Add from "@mui/icons-material/Add";
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 navigate = useNavigate();
const [addOpen, setAddopen] = useState(false);
const [title, setTitle] = useState("");
const [type, setType] = useState<"BATCH" | "FLOW" | "">("");
const [list, setList] = useState([]);
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 [totalElements, setTotalElements] = useState(0);
const headCells: Array<any> = [
{
id: "title",
label: "环境名称",
},
{
id: "type",
label: "环境类型",
width: 100,
},
{
id: "createdTime",
label: "创建时间",
width: 180,
},
{
id: "status",
label: "构建状态",
width: 150,
},
{
id: "caozuo",
label: "操作",
width: 140,
},
];
const { run: getList } = useMyRequest(getActorenvList, {
onSuccess: (res) => {
setList(res.data.content);
setCount(res.data.totalPages - 1);
setTotalElements(res.data.totalElements);
},
});
const pageChange = (value: number) => {
setPage(value - 1);
};
useEffect(() => {
if (!addOpen && !deleteOpen) {
getList({
page,
size,
title,
type,
});
}
}, [getList, page, size, title, type, addOpen, deleteOpen]);
const renderType = (item: any) => {
if (item.type === "BATCH") {
return "批式";
} else if (item.type === "FLOW") {
return "流式";
} else {
return "";
}
};
const renderStatus = (item: any) => {
if (item.status === "PENDING") {
return (
<div className={style.statusBox}>
<img src={jobRun} alt="" />
<span className={style.status}>准备构建</span>
</div>
);
} else if (item.status === "CREATING") {
return (
<div className={style.statusBox}>
<img src={jobRun} alt="" />
<span className={style.status}>正在构建</span>
</div>
);
} else if (item.status === "FAILED") {
return (
<div className={style.statusBox}>
<img src={jobFail} alt="" />
<span className={style.status}>构建失败</span>
</div>
);
} else if (item.status === "CREATED") {
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 hanleToSeeEnv = (item: any) => {
console.log("hanleToSeeEnv");
console.log(item.id);
navigate("/product/resourceCenter/userResources/seeEnv", {
state: { id: item.id },
});
};
const renderButtons = (item: any) => {
if (item.status === "FAILED") {
return (
<>
<MyButton
text="详情"
style={{
position: "relative",
left: "-10px",
minWidth: "10px",
height: "22px",
padding: "0 10px",
}}
variant="text"
size="medium"
onClick={() => hanleToSeeEnv(item)}
/>
<MyButton
onClick={() => handleDelete(item)}
text="删除"
style={{
position: "relative",
left: "-10px",
minWidth: "10px",
height: "22px",
padding: "0 10px",
}}
variant="text"
size="medium"
color="error"
/>
</>
);
} else {
return (
<MyButton
text="详情"
style={{
position: "relative",
left: "-10px",
minWidth: "10px",
height: "22px",
padding: "0 10px",
}}
variant="text"
size="medium"
onClick={() => hanleToSeeEnv(item)}
/>
);
}
};
return (
<div className={style.environment}>
<div className={style.top}>
<div className={style.topLeft}>
<SearchInput sx={{ width: 340, marginRight: "16px" }}></SearchInput>
<MySelect
options={[
{
label: "环境类型",
value: "a",
},
]}
sx={{ width: "150px", height: "32px" }}
></MySelect>
<SearchInput
sx={{ width: 340, marginRight: "16px" }}
onKeyUp={(e: any) => {
if (e.keyCode === 13) {
setTitle(e.target.value);
}
}}
></SearchInput>
{!addOpen && (
<MySelect
title="环境类型"
isTitle={true}
options={[
{
label: "批式",
value: "BATCH",
},
{
label: "流式",
value: "FLOW",
},
]}
value={type}
onChange={(e: any) => setType(e)}
sx={{ width: "150px", height: "32px" }}
></MySelect>
)}
</div>
<div className={style.topRight}>
<MyButton
text="构建应用环境"
img={
<span
style={{ fontSize: "14px", marginRight: "8px" }}
className="iconfont icon-dianzan"
></span>
}
onClick={() => setAddopen(true)}
startIcon={<Add />}
></MyButton>
</div>
</div>
UserResourcesEnvironment
<div className={style.tableBox}>
<MyTable
rows={list.map((item: any) => ({
...item,
type: renderType(item),
status: renderStatus(item),
caozuo: renderButtons(item),
createdTime: renderCreatedTime(item),
}))}
headCells={headCells}
fixedHead={true}
hasTableFooter={true}
page={page}
count={count}
pageChange={pageChange}
rowsPerPage={size}
handleChangeRowsPerPage={(e: number) => {
setSize(e);
setPage(0);
}}
nodataText="暂无应用环境"
paginationType="complex"
totalElements={totalElements}
></MyTable>
</div>
{addOpen && <AddEnvironment setAddopen={setAddopen}></AddEnvironment>}
{deleteOpen && (
<DeleteEnvironment
id={id}
open={deleteOpen}
setDeleteOpen={setDeleteOpen}
></DeleteEnvironment>
)}
</div>
);
};
......
.addOperatorBox {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #fff;
display: flex;
}
.left {
/* width: 412px; */
box-sizing: border-box;
background-color: rgba(247, 248, 250, 1);
}
.right {
flex: 1;
padding: 64px 44px 40px;
position: relative;
min-height: 100vh;
overflow: overlay;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.title {
color: rgba(30, 38, 51, 1);
margin-bottom: 32px;
font-size: 24px;
line-height: 32px;
font-weight: 550;
}
.content {
display: flex;
border-radius: 4px;
margin-bottom: 20px;
height: 600px;
}
.form {
width: 368px;
box-sizing: border-box;
padding: 16px 24px;
border: 1px solid #ebedf0;
border-right: none;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.newForm {
width: 368px;
box-sizing: border-box;
padding: 16px 32px 0px 0;
border-right: 1px solid #ebedf0;
}
.codeBox {
flex: 1;
display: flex;
flex-direction: column;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.codeTitle {
background-color: rgba(230, 233, 237, 1);
padding: 11px 20px;
color: rgba(30, 38, 51, 1);
font-size: 14px;
line-height: 22px;
font-weight: 550;
border-top-right-radius: 4px;
}
.code {
background-color: rgba(247, 248, 250, 1);
flex: 1;
}
.parameterConfigBox {
border: 1px solid #ebedf0;
margin-bottom: 24px;
}
.labelClassName {
line-height: 36px;
}
.operatorFormItem {
margin-bottom: 24px;
}
.descBox {
padding-left: 32px;
}
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-18 16:12:55
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 21:27:03
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/components/AddOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { useEffect, useState, useCallback, useMemo } from "react";
import classNames from "classnames";
import { observer } from "mobx-react";
import MyInput from "@/components/mui/MyInput";
import MySelect from "@/components/mui/MySelect";
import MyButton from "@/components/mui/MyButton";
import style from "./index.module.css";
import SwitchBatchFolw from "@/views/ResourceCenter/components/SwitchBatchFolw";
import Code from "@/components/CommonComponents/Code";
import RadioGroupOfButtonStyle from "@/components/CommonComponents/RadioGroupOfButtonStyle";
import { ITask } from "@/views/Project/ProjectSubmitWork/interface";
import BatchOperatorFlow from "@/views/Project/components/Flow/components/BatchOperatorFlow";
import OperatorList from "@/views/CustomOperator/components/OperatorList";
import FormItemBox from "@/components/mui/FormItemBox";
import { useStores } from "@/store";
interface IAddOperator {
setAddOpen: (val: boolean) => void;
}
type IBuildType = "ENVIRONMENT" | "OPERATOR";
const AddOperator = observer((props: IAddOperator) => {
const { setAddOpen } = props;
/** 创建类型 BATCH - 批算子; FLOW - 流算子*/
const [taskType, setTaskType] = useState<"BATCH" | "FLOW">("BATCH");
/** 创建类型 ENVIRONMENT - 基于环境; OPERATOR - 基于算子*/
const [batchBuildType, setBatchBuildType] =
useState<IBuildType>("ENVIRONMENT");
/** 算子数组 */
const [operatorList, setOperatorList] = useState<ITask[]>([]);
/** 流程编排 算子是否选中 */
const [inputActive, setInputActive] = useState(false);
/** 产品store */
const { productListStore } = useStores();
const [code, setCode] = useState("");
// const [formData, setFormData] = useState<any>();
const buildTypeList = useMemo(() => {
return [
{ value: "ENVIRONMENT", label: "基于应用环境" },
{ value: "OPERATOR", label: "基于流式算子" },
];
}, []);
/** 切换类型 */
const handleRadio = (val: IBuildType) => {
setBatchBuildType(val);
};
const handleSubmit = () => {
console.log(33);
};
return (
<div className={style.addOperatorBox}>
<div className={style.left}>
<SwitchBatchFolw
active={taskType}
setActive={setTaskType}
goBack={() => setAddOpen(false)}
></SwitchBatchFolw>
</div>
<div className={style.right}>
<div className={style.title}>
{taskType === "BATCH" ? "批式算子信息" : "流式算子信息"}
</div>
<div style={{ paddingBottom: "20px" }}>
<RadioGroupOfButtonStyle
RadiosBoxStyle={{ width: 236 }}
value={batchBuildType}
radioOptions={buildTypeList}
handleRadio={handleRadio}
/>
</div>
<div className={style.content}>
<div
className={classNames({
[style.form]: batchBuildType === "ENVIRONMENT",
[style.newForm]: batchBuildType != "ENVIRONMENT",
})}
>
<FormItemBox
label="算子名称"
labelClassName={style.labelClassName}
className={style.operatorFormItem}
itemFlex="column"
require
>
<MyInput
helperText="30字符以内,仅限字母、数字、中文"
placeholder="请输入算子名称"
/>
</FormItemBox>
<FormItemBox
label="算子版本"
labelClassName={style.labelClassName}
className={style.operatorFormItem}
itemFlex="column"
require
>
<MyInput />
</FormItemBox>
<FormItemBox
label="所属产品"
labelClassName={style.labelClassName}
className={style.operatorFormItem}
itemFlex="column"
require
>
<MySelect
fullWidth
options={productListStore?.productList || []}
/>
</FormItemBox>
{batchBuildType === "ENVIRONMENT" ? (
<FormItemBox
label="应用环境"
labelClassName={style.labelClassName}
className={style.operatorFormItem}
itemFlex="column"
require
>
<MySelect
fullWidth
options={[{ label: "cadd", value: "CADD" }]}
/>
</FormItemBox>
) : null}
{batchBuildType === "ENVIRONMENT" ? (
<FormItemBox
label="描述"
labelClassName={style.labelClassName}
className={style.operatorFormItem}
itemFlex="column"
>
<MyInput multiline rows={4} placeholder="请输入算子描述" />
</FormItemBox>
) : null}
</div>
{batchBuildType === "ENVIRONMENT" ? (
<div className={style.codeBox}>
<div className={style.codeTitle}>参数配置</div>
<div className={style.code}>
<Code
value={code}
onChange={(e: string) => setCode(e)}
height="535px"
/>
</div>
</div>
) : (
<div className={style.descBox}>
<FormItemBox
label="描述"
labelClassName={style.labelClassName}
className={style.operatorFormItem}
itemFlex="column"
>
<MyInput
style={{ width: "420px" }}
multiline
rows={10}
placeholder="请输入算子描述"
/>
</FormItemBox>
</div>
)}
</div>
<div className={style.parameterConfigBox}>
<div className={style.codeTitle}>
{batchBuildType === "ENVIRONMENT" ? "运行脚本" : "流程编排"}
</div>
{batchBuildType === "ENVIRONMENT" ? (
<div className={style.code}>
<Code
value={code}
onChange={(e: string) => setCode(e)}
height="350px"
/>
</div>
) : (
<div
id="customOperatorFlow"
style={{ position: "relative", height: 400 }}
>
<OperatorList
operatorList={operatorList}
setOperatorList={setOperatorList}
setInputActive={setInputActive}
/>
<BatchOperatorFlow
tasks={operatorList}
setTasks={setOperatorList}
type="edit"
// onFlowNodeClick={handleNodeClick}
flowNodeDraggable={true}
ListenState={!inputActive}
showVersion={true}
showControls={false}
/>
</div>
)}
</div>
<div className={style.buttonBox}>
<MyButton text="开始构建"></MyButton>
</div>
</div>
</div>
);
});
export default AddOperator;
......@@ -4,10 +4,13 @@
padding: 20px;
margin: 0 20px 20px 0;
background: linear-gradient(180deg, #f5f7fa 0%, #ffffff 100%);
box-shadow: 0px 6px 24px 0px rgba(3, 47, 105, 0.14);
box-shadow: 0px 3px 12px 0px rgba(3, 47, 105, 0.09);
border-radius: 6px;
border: 2px solid #ffffff;
}
.itemBox:hover {
box-shadow: 0px 6px 24px 0px rgba(3, 47, 105, 0.14);
}
.itemHeaderBox {
display: flex;
......
......@@ -2,29 +2,38 @@
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-17 18:35:09
* @LastEditTime: 2022-10-19 21:08:31
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import styles from "./index.module.css";
import { IOperatorInfo } from "../../interface";
interface IProps {
operatorInfo: IOperatorInfo;
}
const OperatorCard = (props: IProps) => {
const {
operatorInfo: { title = "", type, version },
} = props;
const OperatorCard = () => {
return (
<div className={styles.itemBox}>
<div className={styles.itemHeaderBox}>
<img alt="" style={{ width: 36, height: 36 }} />
<div>
<b className={styles.titleBox}>Desktop</b>
<b className={styles.titleBox}>{title}</b>
<span className={styles.operatorTypeBox}>批算子</span>
</div>
</div>
<div className={styles.itemContentBox}>
<p className={styles.infoBox}>
产品类型:<span>CADD</span>
所属产品:<span>{type}</span>
</p>
<p className={styles.infoBox}>
算子版本:<span>V1.0.0</span>
算子版本:<span>{`V${version}`}</span>
</p>
<p className={styles.infoBox}>
创建时间:<span>2022-10-11</span>
......
.detailsBox {
padding: 0 24px;
}
.titleBox {
font-size: 16px;
color: #1e2633;
line-height: 24px;
padding: 20px 0;
}
......@@ -2,33 +2,60 @@
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-17 19:27:00
* @LastEditTime: 2022-10-19 10:30:55
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import styles from "./index.module.css";
import BasicInfo from "../../../../components/BasinInfo";
import { useMemo, useState } from "react";
import RadioGroupOfButtonStyle from "@/components/CommonComponents/RadioGroupOfButtonStyle";
const OperatorDetails = () => {
const contentTypeList = useMemo(() => {
return [
{ value: "flowChart", label: "流程图" },
{ value: "parameterList", label: "参数列表" },
{ value: "runScript", label: "运行脚本" },
];
}, []);
/** 详情类型 */
const [contentType, setContentType] = useState<string>("flowChart");
/** 切换类型 */
const handleRadio = (val: string) => {
setContentType(val);
};
return (
<div className={styles.detailsBox}>
<div>
<h2>基础信息</h2>
<tr>
<td>所属产品</td>
<td>创建时间</td>
<td>创建人</td>
<td>算子版本</td>
<td>应用环境</td>
</tr>
<tr>
<td>所属产品</td>
<td>创建时间</td>
<td>创建人</td>
<td>算子版本</td>
<td>应用环境</td>
</tr>
<h2 className={styles.titleBox}>基础信息</h2>
<div style={{ border: "1px solid #EBEDF0" }}>
<BasicInfo
infoList={[
{ label: "33333331", value: "1" },
{
label: "1",
value: "42543253245325325432452345235432452323542352354235235321",
},
{ label: "1", value: "1" },
{ label: "1", value: "1" },
{ label: "1", value: "1" },
]}
/>
<BasicInfo infoList={[{ label: "2", value: "2" }]} />
</div>
<div style={{ padding: "26px 0 16px 0" }}>
<RadioGroupOfButtonStyle
RadiosBoxStyle={{ width: 254 }}
value={contentType}
radioOptions={contentTypeList}
handleRadio={handleRadio}
/>
</div>
<div>11</div>
</div>
);
};
......
......@@ -6,12 +6,11 @@
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 0 20px;
padding: 20px;
}
.searchSelectBox {
margin-left: 12px;
margin-right: 12px;
}
.contentBox {
......
......@@ -2,69 +2,114 @@
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-17 17:50:30
* @LastEditTime: 2022-10-19 21:11:50
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// 应用环境
import { useState } from "react";
import { useEffect, useState } from "react";
import { observer } from "mobx-react";
import SearchInput from "@/components/BusinessComponents/SearchInput";
import MySelect from "@/components/mui/MySelect";
import MyButton from "@/components/mui/MyButton";
import OperatorCard from "./components/OperatorCard";
import AddOperator from "./components/AddOperator";
import { useStores } from "@/store";
import { getOperatorList, IOperatorListParams } from "@/api/resourceCenter";
import styles from "./index.module.css";
import useMyRequest from "@/hooks/useMyRequest";
import { IOperatorInfo } from "./interface";
const WorkflowOperator = observer(() => {
const [addOpen, setAddOpen] = useState<boolean>(false);
/** 产品store */
const { productListStore } = useStores();
/** 算子列表参数 */
const [searchParams, setSearchParams] = useState<IOperatorListParams>({
keyword: "",
productId: "",
type: "",
});
const [list, setList] = useState<IOperatorInfo[]>();
const { run: getList } = useMyRequest(getOperatorList, {
onSuccess: (res) => {
console.log(res);
setList(res?.data);
},
});
useEffect(() => {
getList();
}, []);
const WorkflowOperator = () => {
const [addOpen, setAddopen] = useState(false);
return (
<div className={styles.indexBox}>
<div className={styles.headerBox}>
<div>
<SearchInput
sx={{ width: 340, marginRight: "16px" }}
placeholder="输入关键词搜索"
/>
<MySelect
options={[
{
label: "环境类型",
value: "a",
},
]}
placeholder="环境类型"
className={styles.searchSelectBox}
sx={{ width: "150px", height: "32px" }}
/>
<MySelect
options={[
{
label: "环境类型",
value: "a",
},
]}
placeholder="批/流类型"
className={styles.searchSelectBox}
sx={{ width: "150px", height: "32px" }}
/>
<>
<div className={styles.indexBox}>
<div className={styles.headerBox}>
<div>
<SearchInput
sx={{ width: 340, marginRight: "16px" }}
placeholder="输入关键词搜索"
value={searchParams.keyword}
onChange={(e) => {
setSearchParams({ ...searchParams, keyword: e.target.value });
}}
/>
<MySelect
title="所属产品"
isTitle={true}
options={productListStore?.productList || []}
value={searchParams.keyword}
onChange={(e) => {
setSearchParams({ ...searchParams, productId: e });
}}
className={styles.searchSelectBox}
sx={{ width: "150px", height: "32px" }}
/>
<MySelect
title="环境类型"
isTitle={true}
value={searchParams.keyword}
onChange={(e) => {
setSearchParams({ ...searchParams, type: e });
}}
options={[
{
label: "批式",
value: "BATCH",
},
{
label: "流式",
value: "FLOW",
},
]}
sx={{ width: "150px", height: "32px" }}
/>
</div>
<MyButton
text="构建算子"
img={
<span
style={{ fontSize: "14px", marginRight: "8px" }}
className="iconfont icon-dianzan"
></span>
}
onClick={() => setAddOpen(true)}
></MyButton>
</div>
<div className={styles.contentBox}>
{list?.map((item) => {
return <OperatorCard operatorInfo={item} />;
})}
</div>
<MyButton
text="构建算子"
img={
<span
style={{ fontSize: "14px", marginRight: "8px" }}
className="iconfont icon-dianzan"
></span>
}
></MyButton>
</div>
<div className={styles.contentBox}>
{[1, 2, 3, 4, 5].map((item) => {
return <OperatorCard />;
})}
</div>
</div>
{addOpen && <AddOperator setAddOpen={setAddOpen} />}
</>
);
};
});
export default WorkflowOperator;
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-19 20:50:18
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 21:09:14
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/interface.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export type IOperatorType = 'BATCH' | 'FLOW'
export interface IOperatorInfo{
title: string;
type: IOperatorType;
version: string;
productId: string;
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ import { useMemo, useState } from "react";
import classNames from "classnames";
import Tabs from "@/components/mui/MyTabs";
import WorkflowOperator from "./WorkflowOperator";
import OperatorDetails from "./WorkflowOperator/components/OperatorDetails";
const UserResources = () => {
const isPass = usePass();
const tabList = useMemo(() => {
......@@ -36,7 +37,9 @@ const UserResources = () => {
title="个人资源"
tabList={tabList}
defaultValue={"USERRESOURCES_ENVIRONMENT"}
tabPanelSx={{ padding: "0" }}
/>
{/* <OperatorDetails /> */}
</div>
);
};
......
.trHeaderBox {
display: flex;
}
.trHeaderBox > div {
position: relative;
padding-left: 24px;
height: 44px;
background: #f5f6f7;
color: #565c66;
line-height: 44px;
width: calc(100% / 5);
}
.trHeaderBox > div::after {
position: absolute;
top: 12px;
right: 0px;
content: "";
height: 20px;
width: 0;
border-left: 1px solid #dde1e6;
}
.trHeaderBox > div:last-child::after {
content: "";
border: none;
}
.trBodyBox {
display: flex;
}
.trBodyBox > div {
overflow: hidden;
text-overflow: ellipsis;
display: "-webkit-box";
padding-left: 24px;
height: 44px;
color: #1e2633;
background: #fff;
line-height: 44px;
}
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-18 14:52:48
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import MyTooltip from "@/components/mui/MyTooltip";
import { ReactElement, ReactNode } from "react";
import styles from "./index.module.css";
interface IProps {
infoList: IInfoItem[];
}
export interface IInfoItem {
label: string;
value: string | ReactNode | ReactElement;
flex?: number;
}
const BasicInfo = (props: IProps) => {
const { infoList } = props;
return (
<div className={styles.basicInfoBox}>
<div className={styles.trHeaderBox}>
{infoList.map((item) => {
return (
<div
style={{
width:
infoList?.length > 1
? `calc(100% / ${infoList.length})`
: "100%",
}}
>
{item.label}
</div>
);
})}
</div>
<div className={styles.trBodyBox}>
{infoList.map((item) => {
return (
<div
style={{
width:
infoList?.length > 1
? `calc(100% / ${infoList.length})`
: "100%",
}}
>
<MyTooltip
title={typeof item.value === "string" ? item.value : ""}
>
<span>{item.value}</span>
</MyTooltip>
</div>
);
})}
</div>
</div>
);
};
export default BasicInfo;
.logView {
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1002;
}
.logViewBox {
background: #282c34;
}
.close {
position: absolute;
right: 0;
top: -28px;
cursor: pointer;
color: #fff;
}
.logViewTop {
position: relative;
width: 100%;
background-color: #1d2126;
display: flex;
font-size: 12px;
color: #8a9099;
overflow: hidden;
}
.leftButton {
width: 48px;
height: 32px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-right: 1px solid #10141a;
background-color: #1d2126;
}
.rightButton {
width: 48px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-left: 1px solid #10141a;
background-color: #1d2126;
}
.middleFixed {
width: calc(100% - 98px);
position: relative;
overflow: hidden;
}
.middleDynamic {
display: flex;
position: absolute;
left: 0;
transition: left 0.4s ease-in-out;
}
.logTitle {
display: flex;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
flex-shrink: 0;
align-items: center;
height: 32px;
line-height: 20px;
padding: 0 20px;
cursor: pointer;
border-right: 1px solid #10141a;
}
.logTitle:hover {
background-color: #23272e;
}
.logName {
max-width: 90px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.logTitleSelected {
background: #282c34;
color: #ffffff;
}
.fileIcon {
width: 14px !important;
margin-right: 4px;
}
.logViewContent {
position: relative;
box-sizing: border-box;
height: calc(100vh - 148px);
padding: 24px 32px 0;
color: #d1d6de;
white-space: pre-wrap;
word-break: break-word;
overflow: overlay;
font-size: 14px;
line-height: 22px;
}
.logViewContentMask {
height: 24px;
width: calc(100% - 64px);
background-color: #282c34;
position: absolute;
top: 32px;
left: 32px;
z-index: 1005;
}
.logViewContent::-webkit-scrollbar-track {
background-color: #282c34;
}
.logViewBottom {
display: flex;
align-items: center;
justify-content: end;
height: 76px;
padding-right: 24px;
}
.gradientBox {
position: absolute;
right: 49px;
width: 28px;
height: 32px;
background: linear-gradient(90deg, rgba(29, 33, 38, 0) 0%, #1d2126 100%);
}
import { useState, useEffect } from "react";
import classnames from "classnames";
import style from "./index.module.css";
import MyTooltip from "@/components/mui/MyTooltip";
import InsertDriveFileOutlinedIcon from "@mui/icons-material/InsertDriveFileOutlined";
import CloudEController from "@/api/fileserver/CloudEController";
import useMyRequest from "@/hooks/useMyRequest";
import { getPublicProject } from "@/api/resourceCenter";
import { getDataFileToken, hpczone } from "@/api/project_api";
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import useWindowSize from "@/hooks/useWindowSize";
type LogViewProps = {
logs: any[];
};
const LogView = (props: LogViewProps) => {
const { logs } = props;
const [hpczoneList, setHpczoneList] = useState<Array<any>>([]);
const [publicProjectId, setPublicProjectId] = useState("");
const [publicZoneId, setPublicZoneId] = useState("");
const [fileToken, setFileToken] = useState("");
const { width, height } = useWindowSize();
// 当前选择的日志
const [logCurrent, setLogCurrent] = useState<number>(0);
// 当前日志的内容文本
const [logText, setLogText] = useState("");
// 当前日志路径
const [logPath, setLogPath] = useState("");
const [displacement, setDisplacement] = useState(0);
const [middleDynamicWidth, setMiddleDynamicWidth] = useState(0);
const [leftButtonColor, setLeftButtonColor] = useState("#585D62");
const [rightButtonColor, setRightButtonColor] = useState("#585D62");
const { run: getPublicProjectFn } = useMyRequest(getPublicProject, {
onSuccess: (res: any) => {
setPublicProjectId(res.data[0].id);
setPublicZoneId(res.data[0].zoneId);
getDataFileTokenFn({ id: res.data[0].id });
},
});
const { run: getDataFileTokenFn } = useMyRequest(getDataFileToken, {
onSuccess: (res: any) => {
setFileToken(res.data);
},
});
useEffect(() => {
getPublicProjectFn();
}, [getPublicProjectFn]);
const { run: hpczoneFn } = useMyRequest(hpczone, {
onSuccess: (res: any) => {
setHpczoneList(res.data);
},
});
useEffect(() => {
hpczoneFn();
}, [hpczoneFn]);
useEffect(() => {
setLogPath(logs[logCurrent]?.logPath);
}, [logs, logCurrent]);
// 请求日志文本
useEffect(() => {
if (logPath) {
const path = logPath.slice(12);
let origin = "";
hpczoneList.forEach((item) => {
if (item.id === publicZoneId) {
origin = item?.storageConfig?.fileServerEndPoint;
}
});
if (path && fileToken && publicProjectId && origin) {
CloudEController.JobFileDownloadText(
path,
fileToken as string,
publicProjectId as string,
origin
)?.then((res) => {
setLogText(res.data);
});
} else {
setLogText("");
}
} else {
setLogText("");
}
}, [logPath, fileToken, hpczoneList, publicProjectId, publicZoneId]);
// 选择日志时改变日志路径
useEffect(() => {
setLogPath(logs[logCurrent]?.logPath);
}, [logCurrent, logs]);
//获取盒子的总宽度,用于滑动效果判断
useEffect(() => {
const box = document.getElementById("middleDynamic");
setMiddleDynamicWidth(box ? box.offsetWidth : 0);
}, []);
useEffect(() => {
if (middleDynamicWidth < width - 97) {
setLeftButtonColor("#585D62");
setRightButtonColor("#585D62");
}
if (displacement === 0) {
setLeftButtonColor("#585D62");
} else {
setLeftButtonColor("#C0C5CD");
}
if (
middleDynamicWidth > width - 97 &&
displacement !== -middleDynamicWidth + width - 97
) {
setRightButtonColor("#C0C5CD");
} else {
setRightButtonColor("#585D62");
}
}, [width, middleDynamicWidth, displacement]);
// 下载当前日志
// const handleDownLoad = () => {
// const path = logPath.slice(12);
// CloudEController.JobFileDownload(
// path,
// fileToken as string,
// publicProjectId as string
// );
// };
const rightClick = () => {
if (middleDynamicWidth < width - 97) {
return;
}
if (-displacement > middleDynamicWidth - width * 1.8 + 97) {
setDisplacement(-middleDynamicWidth + width - 97);
return;
}
const newDisplacement = displacement - width * 0.8;
setDisplacement(newDisplacement);
};
const leftClick = () => {
if (-displacement < width * 0.8) {
setDisplacement(0);
return;
}
const newDisplacement = displacement + width * 0.8;
setDisplacement(newDisplacement);
};
return (
<div className={style.logViewBox}>
<div className={style.logViewContentMask}></div>
<div className={style.logViewTop}>
<div
className={style.leftButton}
onClick={leftClick}
style={{
color: leftButtonColor,
cursor: leftButtonColor === "#585D62" ? "default" : "pointer",
}}
>
<ChevronLeftIcon />
</div>
<div className={style.middleFixed}>
<div
className={style.middleDynamic}
id="middleDynamic"
style={{ left: `${displacement}px` }}
>
{logs.map((item: any, index: number) => {
return (
<MyTooltip
title={item.logName}
placement="bottom"
arrow={false}
enterDelay={1000}
>
<div
key={index}
onClick={() => {
setLogCurrent(index);
}}
className={classnames({
[style.logTitle]: true,
[style.logTitleSelected]: index === logCurrent,
})}
>
<InsertDriveFileOutlinedIcon className={style.fileIcon} />
<span className={style.logName}>{item.logName}</span>
</div>
</MyTooltip>
);
})}
</div>
</div>
<div className={style.gradientBox}></div>
<div
className={style.rightButton}
onClick={rightClick}
style={{
color: rightButtonColor,
cursor: rightButtonColor === "#585D62" ? "default" : "pointer",
}}
>
<ChevronRightIcon />
</div>
</div>
<div className={style.logViewContent}>{logText}</div>
{/* <div className={style.logViewBottom}>
<MyButton text="下载当前日志" onClick={handleDownLoad} />
</div> */}
</div>
);
};
export default LogView;
This diff is collapsed.
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