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
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-13 09:56:57 * @Date: 2022-06-13 09:56:57
* @LastEditors: 吴永生 15770852798@163.com * @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 * @FilePath: /bkunyun/src/api/api_manager.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -52,6 +52,9 @@ const RESTAPI = { ...@@ -52,6 +52,9 @@ const RESTAPI = {
API_GET_PUBLIC_ENV:`${BACKEND_API_URI_PREFIX}/cpp/common/public/env`, // 获取公共环境 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_GET_PUBLIC_PROJECT:`${BACKEND_API_URI_PREFIX}/cpp/common/public/project`, // 获取公共环境
API_ACTORENV_BUILDENV:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/buildenv`, // 新增应用环境 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; export default RESTAPI;
...@@ -260,13 +260,14 @@ class CloudEController { ...@@ -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)) { if (ApiUtils.getAuthorizationHeaders(headers)) {
url = url + urlToken(filetoken, projectId); url = url + urlToken(filetoken, projectId);
headers["Cache-Control"] = "no-cache"; headers["Cache-Control"] = "no-cache";
headers["Content-Type"] = "multipart/form-data"; headers["Content-Type"] = "multipart/form-data";
let origin = fileServerEndPoint ? fileServerEndPoint : APIOPTION()
return axios.get( return axios.get(
APIOPTION() + "/download" + url + "&showhidden=false", origin + "/download" + url + "&showhidden=false",
{ {
headers: headers, 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 request from "@/utils/axios/service";
import Api from "./api_manager"; import Api from "./api_manager";
...@@ -12,6 +20,13 @@ type addActorenvBuildenvParams = { ...@@ -12,6 +20,13 @@ type addActorenvBuildenvParams = {
publicProjectId: string, publicProjectId: string,
} }
/** 获取算子列表请求参数 */
export interface IOperatorListParams {
keyword: string;
productId: string;
type: string;
}
// 获取公共项目 // 获取公共项目
const getPublicProject = () => { const getPublicProject = () => {
return request({ return request({
...@@ -34,7 +49,47 @@ const addActorenvBuildenv = (params: addActorenvBuildenvParams) => { ...@@ -34,7 +49,47 @@ const addActorenvBuildenv = (params: addActorenvBuildenvParams) => {
return request({ return request({
url: Api.API_ACTORENV_BUILDENV, url: Api.API_ACTORENV_BUILDENV,
method: "post", 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 { ...@@ -42,4 +97,8 @@ export {
getPublicEnv, getPublicEnv,
getPublicProject, getPublicProject,
addActorenvBuildenv, 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"; ...@@ -7,9 +7,10 @@ import NotFound from "@/views/404";
import useMyRequest from "@/hooks/useMyRequest"; import useMyRequest from "@/hooks/useMyRequest";
import { useEffect } from "react"; import { useEffect } from "react";
import { menu } from "@/api/routes_api"; import { menu } from "@/api/routes_api";
import { IProductOption } from "@/store/modules/productList";
const useMyRouter = () => { const useMyRouter = () => {
const { permissionStore, menuStore } = useStores(); const { permissionStore, menuStore, productListStore } = useStores();
const userInfo = useMyRequest(current); const userInfo = useMyRequest(current);
const menuInfo = useMyRequest(menu); const menuInfo = useMyRequest(menu);
...@@ -42,16 +43,42 @@ const useMyRouter = () => { ...@@ -42,16 +43,42 @@ const useMyRouter = () => {
} }
}); });
} }
for (let item of menuInfo.res.data) { for (let item of menuInfo.res.data) {
let childrenRoutes: any = []
for (let route of item.routes) { for (let route of item.routes) {
route.element = elements[route.element] || NotFound; route.element = elements[route.element] || NotFound;
route.path = `/product/${item.id}${route.path}`; 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); menuStore.initMenu(menuInfo.res.data);
permissionStore.initAllRoutes(); 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 // 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 * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2021-12-04 15:46:25 * @Date: 2021-12-04 15:46:25
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-21 18:00:58 * @LastEditTime: 2022-10-19 18:41:04
* @FilePath: /lionet-slb-pc/src/components/SearchView/components/Collapse.tsx * @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 * @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"; ...@@ -13,6 +13,7 @@ import FormHelperText from "@mui/material/FormHelperText";
import Select, { SelectChangeEvent, SelectProps } from "@mui/material/Select"; import Select, { SelectChangeEvent, SelectProps } from "@mui/material/Select";
import { createTheme, ThemeProvider } from "@mui/material"; import { createTheme, ThemeProvider } from "@mui/material";
import selectActive from "@/assets/project/selectActive.svg"; import selectActive from "@/assets/project/selectActive.svg";
import { useState } from "react";
export interface IOption { export interface IOption {
label: string; label: string;
...@@ -192,7 +193,10 @@ export default function MySelect(props: IProps) { ...@@ -192,7 +193,10 @@ export default function MySelect(props: IProps) {
}, },
}); });
const [insideValue, setInsideValue] = useState<string>("");
const handleChange = (e: SelectChangeEvent<unknown>) => { const handleChange = (e: SelectChangeEvent<unknown>) => {
setInsideValue(e.target.value as string);
onChange && onChange(e.target.value as string); onChange && onChange(e.target.value as string);
}; };
...@@ -214,7 +218,7 @@ export default function MySelect(props: IProps) { ...@@ -214,7 +218,7 @@ export default function MySelect(props: IProps) {
size="small" size="small"
multiple={multiple} multiple={multiple}
{...other} {...other}
value={value || ""} value={value || insideValue || ""}
onChange={handleChange} onChange={handleChange}
> >
{options.length {options.length
......
...@@ -40,7 +40,7 @@ interface IMyTableProps { ...@@ -40,7 +40,7 @@ interface IMyTableProps {
totalElements?: number; // 数据总量 不止是列表渲染的长度 totalElements?: number; // 数据总量 不止是列表渲染的长度
sortState?: sortState; // 排序状态 sortState?: sortState; // 排序状态
setSortState?: any; // 设置排序状态 setSortState?: any; // 设置排序状态
paginationType?: "simple" | "complex"; // 分页组件的类型 simple简式 complex复杂、带每页数量切换、总数等 paginationType?: "simple" | "complex"; // 分页组件的类型 simple简式 complex复杂、带每页数量切换、总数等
rowsPerPage?: number; // 每页多少条数据 rowsPerPage?: number; // 每页多少条数据
handleChangeRowsPerPage?: any; // 每页多少条数据变化 handleChangeRowsPerPage?: any; // 每页多少条数据变化
nodataText?: any; // 无数据文案 nodataText?: any; // 无数据文案
......
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-05-31 10:18:13 * @Date: 2022-05-31 10:18:13
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-06-24 14:32:32 * @LastEditTime: 2022-10-18 10:54:54
* @FilePath: /bkunyun/src/router/index.ts * @FilePath: /bkunyun/src/router/index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
import { AnyMap } from "immer/dist/internal"; import { AnyMap } from "immer/dist/internal";
...@@ -13,6 +13,7 @@ import MenuLayout from "@/views/MenuLayout"; ...@@ -13,6 +13,7 @@ import MenuLayout from "@/views/MenuLayout";
import * as React from "react"; import * as React from "react";
import NotFound from "@/views/404"; import NotFound from "@/views/404";
import Demo from "@/views/demo"; import Demo from "@/views/demo";
import SeeEnv from "@/views/ResourceCenter/UserResources/UserResourcesEnvironment/SeeEnv";
import ProjectSetting from "@/views/Project/ProjectSetting"; import ProjectSetting from "@/views/Project/ProjectSetting";
import ProjectData from "@/views/Project/ProjectData"; import ProjectData from "@/views/Project/ProjectData";
import ProjectWorkbench from "@/views/Project/ProjectWorkbench"; import ProjectWorkbench from "@/views/Project/ProjectWorkbench";
...@@ -52,7 +53,9 @@ export const elements: { ...@@ -52,7 +53,9 @@ export const elements: {
}) => JSX.Element | any; }) => JSX.Element | any;
} = { } = {
Demo: Demo, Demo: Demo,
SeeTemplate: Demo,
UserResources: UserResources, UserResources: UserResources,
SeeEnv: SeeEnv,
ProjectSetting: ProjectSetting, ProjectSetting: ProjectSetting,
ProjectData: ProjectData, ProjectData: ProjectData,
ProjectWorkbench: ProjectWorkbench, ProjectWorkbench: ProjectWorkbench,
......
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-09 20:41:05 * @Date: 2022-06-09 20:41:05
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-06-14 20:55:22 * @LastEditTime: 2022-10-19 16:21:50
* @FilePath: /bkunyun/src/store/index.ts * @FilePath: /bkunyun/src/store/index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -12,10 +12,11 @@ import permissionStore from "./modules/permission"; ...@@ -12,10 +12,11 @@ import permissionStore from "./modules/permission";
import menuStore from "./modules/menu"; import menuStore from "./modules/menu";
import currentProjectStore from "./modules/currentProject"; import currentProjectStore from "./modules/currentProject";
import fileListStore from "./modules/fileList"; import fileListStore from "./modules/fileList";
import productListStore from './modules/productList'
configure({ enforceActions: "always" }); configure({ enforceActions: "always" });
export const stores = { permissionStore, menuStore, currentProjectStore, fileListStore }; export const stores = { permissionStore, menuStore, currentProjectStore, fileListStore, productListStore };
/** Store类型 */ /** Store类型 */
export type Stores = typeof stores; 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) => { ...@@ -164,10 +164,10 @@ const OperatorList = observer((props: IProps) => {
const dom = document.getElementById("customOperatorFlow"); const dom = document.getElementById("customOperatorFlow");
const clientX = e.clientX; const clientX = e.clientX;
const clientY = e.clientY; const clientY = e.clientY;
const upperLeftPointX = Number(dom?.offsetLeft); const upperLeftPointX = Number(dom?.getBoundingClientRect()?.left);
const upperLeftPointY = Number(dom?.offsetTop); const upperLeftPointY = Number(dom?.getBoundingClientRect()?.top);
const lowerRightX = Number(upperLeftPointX) + Number(dom?.offsetWidth); const lowerRightX = Number(upperLeftPointX) + Number(dom?.clientWidth);
const lowerRightY = Number(upperLeftPointY) + Number(dom?.offsetHeight); const lowerRightY = Number(upperLeftPointY) + Number(dom?.clientHeight);
if ( if (
clientX > upperLeftPointX && clientX > upperLeftPointX &&
clientY > upperLeftPointY && clientY > upperLeftPointY &&
...@@ -178,7 +178,7 @@ const OperatorList = observer((props: IProps) => { ...@@ -178,7 +178,7 @@ const OperatorList = observer((props: IProps) => {
const newOperatorItem = getNewOperatorItem( const newOperatorItem = getNewOperatorItem(
newDragItem, newDragItem,
clientX - upperLeftPointX, clientX - upperLeftPointX,
clientY - upperLeftPointY - 42 clientY - upperLeftPointY
); );
const newVal = cloneDeep(operatorList); const newVal = cloneDeep(operatorList);
newVal.push(newOperatorItem); newVal.push(newOperatorItem);
......
...@@ -85,7 +85,8 @@ const MenuLayout = observer(() => { ...@@ -85,7 +85,8 @@ const MenuLayout = observer(() => {
key={"sidebar" + index} key={"sidebar" + index}
className={classnames({ className={classnames({
[style.listItem]: true, [style.listItem]: true,
[style.active]: `/v3${item.path}` === pathname, [style.active]: pathname.indexOf(`/v3${item.path}`) !== -1,
// [style.active]: `/v3${item.path}` === pathname,
})} })}
style={ style={
`/v3${item.path}` === pathname `/v3${item.path}` === pathname
......
...@@ -8,10 +8,9 @@ import CloudEController from "@/api/fileserver/CloudEController"; ...@@ -8,10 +8,9 @@ import CloudEController from "@/api/fileserver/CloudEController";
import { useStores } from "@/store"; import { useStores } from "@/store";
import { toJS } from "mobx"; import { toJS } from "mobx";
import FullScreenDrawer from "@/components/CommonComponents/FullScreenDrawer"; import FullScreenDrawer from "@/components/CommonComponents/FullScreenDrawer";
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import useWindowSize from '@/hooks/useWindowSize' import useWindowSize from "@/hooks/useWindowSize";
type LogViewProps = { type LogViewProps = {
logs: any[]; logs: any[];
...@@ -20,6 +19,7 @@ type LogViewProps = { ...@@ -20,6 +19,7 @@ type LogViewProps = {
const LogView = (props: LogViewProps) => { const LogView = (props: LogViewProps) => {
const { logs, setShowLogView } = props; const { logs, setShowLogView } = props;
console.log(logs);
const { currentProjectStore } = useStores(); const { currentProjectStore } = useStores();
const { width, height } = useWindowSize(); const { width, height } = useWindowSize();
const fileToken = toJS(currentProjectStore.currentProjectInfo.filetoken); const fileToken = toJS(currentProjectStore.currentProjectInfo.filetoken);
...@@ -27,95 +27,98 @@ const LogView = (props: LogViewProps) => { ...@@ -27,95 +27,98 @@ const LogView = (props: LogViewProps) => {
// 当前选择的日志 // 当前选择的日志
const [logCurrent, setLogCurrent] = useState<number>(0); const [logCurrent, setLogCurrent] = useState<number>(0);
// 当前日志的内容文本 // 当前日志的内容文本
const [logText, setLogText] = useState('') const [logText, setLogText] = useState("");
// 当前日志路径 // 当前日志路径
const [logPath, setLogPath] = useState('') const [logPath, setLogPath] = useState("");
const [displacement, setDisplacement] = useState(0) const [displacement, setDisplacement] = useState(0);
const [middleDynamicWidth, setMiddleDynamicWidth] = useState(0) const [middleDynamicWidth, setMiddleDynamicWidth] = useState(0);
const [leftButtonColor, setLeftButtonColor] = useState('#585D62') const [leftButtonColor, setLeftButtonColor] = useState("#585D62");
const [rightButtonColor, setRightButtonColor] = useState('#585D62') const [rightButtonColor, setRightButtonColor] = useState("#585D62");
useEffect(() => { useEffect(() => {
setLogPath(logs[logCurrent]?.logPath) setLogPath(logs[logCurrent]?.logPath);
}, [logs]); }, [logs]);
// 请求日志文本 // 请求日志文本
useEffect(() => { useEffect(() => {
if (logPath) { if (logPath) {
const path = logPath.slice(12) const path = logPath.slice(12);
CloudEController.JobFileDownloadText( CloudEController.JobFileDownloadText(
path, path,
fileToken as string, fileToken as string,
projectId as string projectId as string
)?.then((res) => { )?.then((res) => {
setLogText(res.data) setLogText(res.data);
}) });
} else { } else {
setLogText("") setLogText("");
} }
}, [logPath]); }, [logPath]);
// 选择日志时改变日志路径 // 选择日志时改变日志路径
useEffect(() => { useEffect(() => {
setLogPath(logs[logCurrent]?.logPath) setLogPath(logs[logCurrent]?.logPath);
}, [logCurrent]); }, [logCurrent]);
//获取盒子的总宽度,用于滑动效果判断 //获取盒子的总宽度,用于滑动效果判断
useEffect(() => { useEffect(() => {
const box = document.getElementById('middleDynamic') const box = document.getElementById("middleDynamic");
setMiddleDynamicWidth(box ? box.offsetWidth : 0) setMiddleDynamicWidth(box ? box.offsetWidth : 0);
}, []) }, []);
useEffect(() => { useEffect(() => {
if (middleDynamicWidth < width - 97) { if (middleDynamicWidth < width - 97) {
setLeftButtonColor('#585D62') setLeftButtonColor("#585D62");
setRightButtonColor('#585D62') setRightButtonColor("#585D62");
} }
if (displacement === 0) { if (displacement === 0) {
setLeftButtonColor('#585D62') setLeftButtonColor("#585D62");
} else { } else {
setLeftButtonColor('#C0C5CD') setLeftButtonColor("#C0C5CD");
} }
if (middleDynamicWidth > width - 97 && displacement !== -middleDynamicWidth + width - 97) { if (
setRightButtonColor('#C0C5CD') middleDynamicWidth > width - 97 &&
displacement !== -middleDynamicWidth + width - 97
) {
setRightButtonColor("#C0C5CD");
} else { } else {
setRightButtonColor('#585D62') setRightButtonColor("#585D62");
} }
}, [width, middleDynamicWidth, displacement]) }, [width, middleDynamicWidth, displacement]);
// 下载当前日志 // 下载当前日志
const handleDownLoad = () => { const handleDownLoad = () => {
const path = logPath.slice(12) const path = logPath.slice(12);
CloudEController.JobFileDownload( CloudEController.JobFileDownload(
path, path,
fileToken as string, fileToken as string,
projectId as string projectId as string
); );
} };
const rightClick = () => { const rightClick = () => {
if (middleDynamicWidth < width - 97) { if (middleDynamicWidth < width - 97) {
return return;
} }
if (-displacement > middleDynamicWidth - width * 1.8 + 97) { if (-displacement > middleDynamicWidth - width * 1.8 + 97) {
setDisplacement(-middleDynamicWidth + width - 97) setDisplacement(-middleDynamicWidth + width - 97);
return return;
} }
const newDisplacement = displacement - width * 0.8; const newDisplacement = displacement - width * 0.8;
setDisplacement(newDisplacement) setDisplacement(newDisplacement);
} };
const leftClick = () => { const leftClick = () => {
if (-displacement < width * 0.8) { if (-displacement < width * 0.8) {
setDisplacement(0) setDisplacement(0);
return return;
} }
const newDisplacement = displacement + width * 0.8; const newDisplacement = displacement + width * 0.8;
setDisplacement(newDisplacement) setDisplacement(newDisplacement);
} };
return ( return (
<FullScreenDrawer handleClose={setShowLogView} zIndex={1002}> <FullScreenDrawer handleClose={setShowLogView} zIndex={1002}>
...@@ -125,14 +128,22 @@ const LogView = (props: LogViewProps) => { ...@@ -125,14 +128,22 @@ const LogView = (props: LogViewProps) => {
<div <div
className={style.leftButton} className={style.leftButton}
onClick={leftClick} onClick={leftClick}
style={{ color: leftButtonColor, cursor: leftButtonColor === '#585D62' ? 'default' : 'pointer' }} style={{
color: leftButtonColor,
cursor: leftButtonColor === "#585D62" ? "default" : "pointer",
}}
> >
<ChevronLeftIcon /> <ChevronLeftIcon />
</div> </div>
<div className={style.middleFixed}> <div className={style.middleFixed}>
<div className={style.middleDynamic} id='middleDynamic' style={{ left: `${displacement}px` }}> <div
className={style.middleDynamic}
id="middleDynamic"
style={{ left: `${displacement}px` }}
>
{logs.map((item: any, index: number) => { {logs.map((item: any, index: number) => {
return <MyTooltip return (
<MyTooltip
title={item.logName} title={item.logName}
placement="bottom" placement="bottom"
arrow={false} arrow={false}
...@@ -140,15 +151,19 @@ const LogView = (props: LogViewProps) => { ...@@ -140,15 +151,19 @@ const LogView = (props: LogViewProps) => {
> >
<div <div
key={index} key={index}
onClick={() => { setLogCurrent(index) }} onClick={() => {
setLogCurrent(index);
}}
className={classnames({ className={classnames({
[style.logTitle]: true, [style.logTitle]: true,
[style.logTitleSelected]: index === logCurrent, [style.logTitleSelected]: index === logCurrent,
})}> })}
>
<InsertDriveFileOutlinedIcon className={style.fileIcon} /> <InsertDriveFileOutlinedIcon className={style.fileIcon} />
<span className={style.logName}>{item.logName}</span> <span className={style.logName}>{item.logName}</span>
</div> </div>
</MyTooltip> </MyTooltip>
);
})} })}
</div> </div>
</div> </div>
...@@ -156,20 +171,21 @@ const LogView = (props: LogViewProps) => { ...@@ -156,20 +171,21 @@ const LogView = (props: LogViewProps) => {
<div <div
className={style.rightButton} className={style.rightButton}
onClick={rightClick} onClick={rightClick}
style={{ color: rightButtonColor, cursor: rightButtonColor === '#585D62' ? 'default' : 'pointer' }} style={{
color: rightButtonColor,
cursor: rightButtonColor === "#585D62" ? "default" : "pointer",
}}
> >
<ChevronRightIcon /> <ChevronRightIcon />
</div> </div>
</div> </div>
<div className={style.logViewContent}> <div className={style.logViewContent}>{logText}</div>
{logText}
</div>
<div className={style.logViewBottom}> <div className={style.logViewBottom}>
<MyButton text='下载当前日志' onClick={handleDownLoad} /> <MyButton text="下载当前日志" onClick={handleDownLoad} />
</div> </div>
</div> </div>
</FullScreenDrawer> </FullScreenDrawer>
) );
} };
export default LogView export default LogView;
\ No newline at end of file
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
display: flex; display: flex;
border-radius: 4px; border-radius: 4px;
margin-bottom: 24px; margin-bottom: 24px;
height: 600px; height: 580px;
} }
.form { .form {
width: 368px; width: 368px;
...@@ -89,6 +89,12 @@ ...@@ -89,6 +89,12 @@
position: relative; position: relative;
margin-bottom: 24px; margin-bottom: 24px;
} }
.tips {
color: rgba(255, 78, 78, 1);
margin-top: 2px;
line-height: 20px;
font-size: 12px;
}
.uploadBox { .uploadBox {
border-radius: 4px; border-radius: 4px;
border: 1px dashed #dde1e6; border: 1px dashed #dde1e6;
...@@ -97,7 +103,42 @@ ...@@ -97,7 +103,42 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: 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 { .formItemHaveHelperText {
margin-bottom: 10px; 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"; ...@@ -10,8 +10,13 @@ import { useDropzone } from "react-dropzone";
import SwitchBatchFolw from "@/views/ResourceCenter/components/SwitchBatchFolw"; import SwitchBatchFolw from "@/views/ResourceCenter/components/SwitchBatchFolw";
import { getLoaclStorageOfKey } from "@/api/fileserver/utils"; import { getLoaclStorageOfKey } from "@/api/fileserver/utils";
import Code from "@/components/CommonComponents/Code"; import Code from "@/components/CommonComponents/Code";
import { useMessage } from "@/components/MySnackbar";
import * as Base64 from "js-base64"; import * as Base64 from "js-base64";
import CloseIcon from "@mui/icons-material/Close";
import { getDataFileToken, hpczone } from "@/api/project_api"; 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 { import {
getPublicEnv, getPublicEnv,
getPublicProject, getPublicProject,
...@@ -26,6 +31,7 @@ type IAddEnvironmentProps = { ...@@ -26,6 +31,7 @@ type IAddEnvironmentProps = {
const AddEnvironment = (props: IAddEnvironmentProps) => { const AddEnvironment = (props: IAddEnvironmentProps) => {
const { setAddopen } = props; const { setAddopen } = props;
const Message = useMessage();
let tokenInfo = getTokenInfo(); let tokenInfo = getTokenInfo();
const [hpczoneList, setHpczoneList] = useState<Array<any>>([]); const [hpczoneList, setHpczoneList] = useState<Array<any>>([]);
const [publicProjectId, setPublicProjectId] = useState(""); const [publicProjectId, setPublicProjectId] = useState("");
...@@ -37,15 +43,25 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -37,15 +43,25 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
const [desc, setDesc] = useState(""); const [desc, setDesc] = useState("");
const [baseEnvId, setBaseEnvId] = useState(""); const [baseEnvId, setBaseEnvId] = useState("");
const [filePaths, setFilePaths] = useState<Array<string>>([]); const [filePaths, setFilePaths] = useState<Array<string>>([]);
const [isUploading, setIsUploading] = useState(false);
const [fileName, setFileName] = useState("");
// const [bashScript, setBashScript] = useState(''); // const [bashScript, setBashScript] = useState('');
const [envList, setEnvList] = useState<Array<any>>([]); const [envList, setEnvList] = useState<Array<any>>([]);
const [progress, setProgress] = useState("0%"); const [progress, setProgress] = useState("0%");
const [code, setCode] = useState(""); const [code, setCode] = useState("");
console.log(code); const [upload, setUpload] = useState<any>(() => {});
const [nameHelper, setNameHelper] = useState({ const [nameHelper, setNameHelper] = useState({
error: false, error: false,
text: "30字符以内,仅限字母、数字、中文", text: "30字符以内,仅限字母、数字、中文",
}); });
const [descHelper, setDescHelper] = useState({
error: false,
text: "",
});
const [filePathsHelper, setFilePathsHelper] = useState({
error: false,
text: "",
});
const onDrop = useCallback( const onDrop = useCallback(
(acceptedFiles: any) => { (acceptedFiles: any) => {
let origin = ""; let origin = "";
...@@ -56,6 +72,10 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -56,6 +72,10 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
}); });
const fileInfo = acceptedFiles[0]; const fileInfo = acceptedFiles[0];
const { path } = fileInfo; const { path } = fileInfo;
if (path.indexOf(".zip") === -1) {
Message.error("请上传压缩文件");
return;
}
const userInfo = getLoaclStorageOfKey("userInfo"); const userInfo = getLoaclStorageOfKey("userInfo");
const homeDirectoryMountPoint = userInfo?.homeDirectoryMountPoint; const homeDirectoryMountPoint = userInfo?.homeDirectoryMountPoint;
const url = const url =
...@@ -80,17 +100,21 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -80,17 +100,21 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
setProgress(`0%`); setProgress(`0%`);
}, },
onError: function (error: string) { onError: function (error: string) {
setIsUploading(false);
console.log("Failed because: " + error); console.log("Failed because: " + error);
}, },
onProgress: function (bytesUploaded: number, bytesTotal: number) { onProgress: function (bytesUploaded: number, bytesTotal: number) {
setProgress(`${bytesUploaded / bytesTotal}%`); setProgress(`${(bytesUploaded * 100) / bytesTotal}%`);
console.log(`${bytesUploaded / bytesTotal}%`);
}, },
onSuccess: function () { onSuccess: function () {
setIsUploading(false);
setFilePaths([`/ProjectData/${homeDirectoryMountPoint}/${path}`]); setFilePaths([`/ProjectData/${homeDirectoryMountPoint}/${path}`]);
setFileName(path);
}, },
}); });
setIsUploading(true);
upload.start(); upload.start();
setUpload(upload);
}, },
[ [
fileToken, fileToken,
...@@ -98,13 +122,13 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -98,13 +122,13 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
tokenInfo?.access_token, tokenInfo?.access_token,
hpczoneList, hpczoneList,
publicZoneId, publicZoneId,
Message,
] ]
); );
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
const { run: getPublicEnvFn } = useMyRequest(getPublicEnv, { const { run: getPublicEnvFn } = useMyRequest(getPublicEnv, {
onSuccess: (res: any) => { onSuccess: (res: any) => {
console.log(res);
let arr = res.data.map((item: any) => { let arr = res.data.map((item: any) => {
return { return {
label: item.title, label: item.title,
...@@ -122,7 +146,6 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -122,7 +146,6 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
const { run: getPublicProjectFn } = useMyRequest(getPublicProject, { const { run: getPublicProjectFn } = useMyRequest(getPublicProject, {
onSuccess: (res: any) => { onSuccess: (res: any) => {
console.log(res);
setPublicProjectId(res.data[0].id); setPublicProjectId(res.data[0].id);
setPublicZoneId(res.data[0].zoneId); setPublicZoneId(res.data[0].zoneId);
getDataFileTokenFn({ id: res.data[0].id }); getDataFileTokenFn({ id: res.data[0].id });
...@@ -131,16 +154,19 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -131,16 +154,19 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
const { run: getDataFileTokenFn } = useMyRequest(getDataFileToken, { const { run: getDataFileTokenFn } = useMyRequest(getDataFileToken, {
onSuccess: (res: any) => { onSuccess: (res: any) => {
console.log(res);
setFileToken(res.data); setFileToken(res.data);
}, },
}); });
const { run: addActorenvBuildenvFn } = useMyRequest(addActorenvBuildenv, { const { run: addActorenvBuildenvFn, loading } = useMyRequest(
onSuccess: (res: any) => { addActorenvBuildenv,
console.log(res); {
onSuccess: () => {
Message.success("开始构建应用环境");
setAddopen(false);
}, },
}); }
);
useEffect(() => { useEffect(() => {
getPublicProjectFn(); getPublicProjectFn();
...@@ -162,10 +188,12 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -162,10 +188,12 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
const handleNameChange = (e: any) => { const handleNameChange = (e: any) => {
setName(e.target.value); setName(e.target.value);
checkName(e.target.value);
}; };
const handleDescChange = (e: any) => { const handleDescChange = (e: any) => {
setDesc(e.target.value); setDesc(e.target.value);
checkDesc(e.target.value);
}; };
const handelBaseEnvIdChange = (e: any) => { const handelBaseEnvIdChange = (e: any) => {
setBaseEnvId(e); setBaseEnvId(e);
...@@ -175,7 +203,80 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -175,7 +203,80 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
setComputeType(envList[index].computeType); 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 = () => { const handleSubmit = () => {
if (
!checkName(name) &&
!checkDesc(desc) &&
!checkFile(filePaths) &&
baseEnvId &&
code
) {
addActorenvBuildenvFn({ addActorenvBuildenvFn({
title: name, title: name,
desc, desc,
...@@ -186,6 +287,15 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -186,6 +287,15 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
publicProjectId, publicProjectId,
computeType, computeType,
}); });
} else {
Message.error("请完善环境信息");
}
};
const handleAbort = () => {
upload.abort(true);
setIsUploading(false);
setFilePaths([]);
}; };
return ( return (
...@@ -227,11 +337,12 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -227,11 +337,12 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
<MyInput <MyInput
value={desc} value={desc}
id="desc" id="desc"
// label="项目描述"
multiline multiline
rows={4} rows={4}
placeholder="请输入项目描述" placeholder="请输入项目描述"
onChange={handleDescChange} onChange={handleDescChange}
error={descHelper.error}
helperText={descHelper.text}
/> />
<span <span
style={{ style={{
...@@ -262,11 +373,72 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -262,11 +373,72 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
<span className={style.download}>下载模板</span> <span className={style.download}>下载模板</span>
</div> </div>
<div className={style.formItem}> <div className={style.formItem}>
<div className={style.uploadBox} {...getRootProps()}> {filePaths.length === 0 && !isUploading && (
<div
className={classNames({
[style.uploadBox]: true,
[style.isDragActive]: isDragActive,
})}
{...getRootProps()}
>
<input {...getInputProps()} /> <input {...getInputProps()} />
<img
className={style.uploaderIcon}
src={uploaderIcon}
alt=""
/>
<span>点击选择环境包或将文件</span> <span>点击选择环境包或将文件</span>
<span>拖到此处上传</span> <span>拖到此处上传</span>
</div> </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> </div>
<div className={style.codeBox}> <div className={style.codeBox}>
...@@ -283,7 +455,11 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -283,7 +455,11 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
</div> </div>
</div> </div>
<div className={style.button}> <div className={style.button}>
<MyButton text="开始构建" onClick={() => handleSubmit()}></MyButton> <MyButton
text="开始构建"
onClick={() => handleSubmit()}
loading={loading}
></MyButton>
</div> </div>
</div> </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;
.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 { .environment {
padding: 19px 24px 0;
} }
.top { .top {
display: flex; display: flex;
...@@ -6,3 +7,14 @@ ...@@ -6,3 +7,14 @@
justify-content: space-between; justify-content: space-between;
margin-bottom: 20px; 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 style from "./index.module.css";
import SearchInput from "@/components/BusinessComponents/SearchInput"; import SearchInput from "@/components/BusinessComponents/SearchInput";
import MySelect from "@/components/mui/MySelect"; import MySelect from "@/components/mui/MySelect";
import MyButton from "@/components/mui/MyButton"; 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 AddEnvironment from "./AddEnvironment";
import DeleteEnvironment from "./DeleteEnvironment";
const UserResourcesEnvironment = () => { const UserResourcesEnvironment = () => {
const navigate = useNavigate();
const [addOpen, setAddopen] = useState(false); 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 ( return (
<div className={style.environment}> <div className={style.environment}>
<div className={style.top}> <div className={style.top}>
<div className={style.topLeft}> <div className={style.topLeft}>
<SearchInput sx={{ width: 340, marginRight: "16px" }}></SearchInput> <SearchInput
sx={{ width: 340, marginRight: "16px" }}
onKeyUp={(e: any) => {
if (e.keyCode === 13) {
setTitle(e.target.value);
}
}}
></SearchInput>
{!addOpen && (
<MySelect <MySelect
title="环境类型"
isTitle={true}
options={[ options={[
{ {
label: "环境类型", label: "批式",
value: "a", value: "BATCH",
},
{
label: "流式",
value: "FLOW",
}, },
]} ]}
value={type}
onChange={(e: any) => setType(e)}
sx={{ width: "150px", height: "32px" }} sx={{ width: "150px", height: "32px" }}
></MySelect> ></MySelect>
)}
</div> </div>
<div className={style.topRight}> <div className={style.topRight}>
<MyButton <MyButton
text="构建应用环境" text="构建应用环境"
img={
<span
style={{ fontSize: "14px", marginRight: "8px" }}
className="iconfont icon-dianzan"
></span>
}
onClick={() => setAddopen(true)} onClick={() => setAddopen(true)}
startIcon={<Add />}
></MyButton> ></MyButton>
</div> </div>
</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>} {addOpen && <AddEnvironment setAddopen={setAddopen}></AddEnvironment>}
{deleteOpen && (
<DeleteEnvironment
id={id}
open={deleteOpen}
setDeleteOpen={setDeleteOpen}
></DeleteEnvironment>
)}
</div> </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 @@ ...@@ -4,10 +4,13 @@
padding: 20px; padding: 20px;
margin: 0 20px 20px 0; margin: 0 20px 20px 0;
background: linear-gradient(180deg, #f5f7fa 0%, #ffffff 100%); 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-radius: 6px;
border: 2px solid #ffffff; border: 2px solid #ffffff;
} }
.itemBox:hover {
box-shadow: 0px 6px 24px 0px rgba(3, 47, 105, 0.14);
}
.itemHeaderBox { .itemHeaderBox {
display: flex; display: flex;
......
...@@ -2,29 +2,38 @@ ...@@ -2,29 +2,38 @@
* @Author: 吴永生 15770852798@163.com * @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11 * @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com * @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 * @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
import styles from "./index.module.css"; 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 ( return (
<div className={styles.itemBox}> <div className={styles.itemBox}>
<div className={styles.itemHeaderBox}> <div className={styles.itemHeaderBox}>
<img alt="" style={{ width: 36, height: 36 }} /> <img alt="" style={{ width: 36, height: 36 }} />
<div> <div>
<b className={styles.titleBox}>Desktop</b> <b className={styles.titleBox}>{title}</b>
<span className={styles.operatorTypeBox}>批算子</span> <span className={styles.operatorTypeBox}>批算子</span>
</div> </div>
</div> </div>
<div className={styles.itemContentBox}> <div className={styles.itemContentBox}>
<p className={styles.infoBox}> <p className={styles.infoBox}>
产品类型:<span>CADD</span> 所属产品:<span>{type}</span>
</p> </p>
<p className={styles.infoBox}> <p className={styles.infoBox}>
算子版本:<span>V1.0.0</span> 算子版本:<span>{`V${version}`}</span>
</p> </p>
<p className={styles.infoBox}> <p className={styles.infoBox}>
创建时间:<span>2022-10-11</span> 创建时间:<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 @@ ...@@ -2,33 +2,60 @@
* @Author: 吴永生 15770852798@163.com * @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11 * @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com * @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 * @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
import styles from "./index.module.css"; 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 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 ( return (
<div className={styles.detailsBox}> <div className={styles.detailsBox}>
<div> <h2 className={styles.titleBox}>基础信息</h2>
<h2>基础信息</h2> <div style={{ border: "1px solid #EBEDF0" }}>
<tr> <BasicInfo
<td>所属产品</td> infoList={[
<td>创建时间</td> { label: "33333331", value: "1" },
<td>创建人</td> {
<td>算子版本</td> label: "1",
<td>应用环境</td> value: "42543253245325325432452345235432452323542352354235235321",
</tr> },
<tr> { label: "1", value: "1" },
<td>所属产品</td> { label: "1", value: "1" },
<td>创建时间</td> { label: "1", value: "1" },
<td>创建人</td> ]}
<td>算子版本</td> />
<td>应用环境</td> <BasicInfo infoList={[{ label: "2", value: "2" }]} />
</tr> </div>
<div style={{ padding: "26px 0 16px 0" }}>
<RadioGroupOfButtonStyle
RadiosBoxStyle={{ width: 254 }}
value={contentType}
radioOptions={contentTypeList}
handleRadio={handleRadio}
/>
</div> </div>
<div>11</div>
</div> </div>
); );
}; };
......
...@@ -6,12 +6,11 @@ ...@@ -6,12 +6,11 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 20px; padding: 20px;
padding: 0 20px;
} }
.searchSelectBox { .searchSelectBox {
margin-left: 12px; margin-right: 12px;
} }
.contentBox { .contentBox {
......
...@@ -2,49 +2,91 @@ ...@@ -2,49 +2,91 @@
* @Author: 吴永生 15770852798@163.com * @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11 * @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com * @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 * @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @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 SearchInput from "@/components/BusinessComponents/SearchInput";
import MySelect from "@/components/mui/MySelect"; import MySelect from "@/components/mui/MySelect";
import MyButton from "@/components/mui/MyButton"; import MyButton from "@/components/mui/MyButton";
import OperatorCard from "./components/OperatorCard"; 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 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 ( return (
<>
<div className={styles.indexBox}> <div className={styles.indexBox}>
<div className={styles.headerBox}> <div className={styles.headerBox}>
<div> <div>
<SearchInput <SearchInput
sx={{ width: 340, marginRight: "16px" }} sx={{ width: 340, marginRight: "16px" }}
placeholder="输入关键词搜索" placeholder="输入关键词搜索"
value={searchParams.keyword}
onChange={(e) => {
setSearchParams({ ...searchParams, keyword: e.target.value });
}}
/> />
<MySelect <MySelect
options={[ title="所属产品"
{ isTitle={true}
label: "环境类型", options={productListStore?.productList || []}
value: "a", value={searchParams.keyword}
}, onChange={(e) => {
]} setSearchParams({ ...searchParams, productId: e });
placeholder="环境类型" }}
className={styles.searchSelectBox} className={styles.searchSelectBox}
sx={{ width: "150px", height: "32px" }} sx={{ width: "150px", height: "32px" }}
/> />
<MySelect <MySelect
title="环境类型"
isTitle={true}
value={searchParams.keyword}
onChange={(e) => {
setSearchParams({ ...searchParams, type: e });
}}
options={[ options={[
{ {
label: "环境类型", label: "批式",
value: "a", value: "BATCH",
},
{
label: "流式",
value: "FLOW",
}, },
]} ]}
placeholder="批/流类型"
className={styles.searchSelectBox}
sx={{ width: "150px", height: "32px" }} sx={{ width: "150px", height: "32px" }}
/> />
</div> </div>
...@@ -56,15 +98,18 @@ const WorkflowOperator = () => { ...@@ -56,15 +98,18 @@ const WorkflowOperator = () => {
className="iconfont icon-dianzan" className="iconfont icon-dianzan"
></span> ></span>
} }
onClick={() => setAddOpen(true)}
></MyButton> ></MyButton>
</div> </div>
<div className={styles.contentBox}> <div className={styles.contentBox}>
{[1, 2, 3, 4, 5].map((item) => { {list?.map((item) => {
return <OperatorCard />; return <OperatorCard operatorInfo={item} />;
})} })}
</div> </div>
</div> </div>
{addOpen && <AddOperator setAddOpen={setAddOpen} />}
</>
); );
}; });
export default WorkflowOperator; 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"; ...@@ -5,6 +5,7 @@ import { useMemo, useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
import Tabs from "@/components/mui/MyTabs"; import Tabs from "@/components/mui/MyTabs";
import WorkflowOperator from "./WorkflowOperator"; import WorkflowOperator from "./WorkflowOperator";
import OperatorDetails from "./WorkflowOperator/components/OperatorDetails";
const UserResources = () => { const UserResources = () => {
const isPass = usePass(); const isPass = usePass();
const tabList = useMemo(() => { const tabList = useMemo(() => {
...@@ -36,7 +37,9 @@ const UserResources = () => { ...@@ -36,7 +37,9 @@ const UserResources = () => {
title="个人资源" title="个人资源"
tabList={tabList} tabList={tabList}
defaultValue={"USERRESOURCES_ENVIRONMENT"} defaultValue={"USERRESOURCES_ENVIRONMENT"}
tabPanelSx={{ padding: "0" }}
/> />
{/* <OperatorDetails /> */}
</div> </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