Commit 41e296fd authored by wuyongsheng's avatar wuyongsheng

Merge branch 'feat-20220801' into 'release'

Feat 20220801

See merge request !51
parents e3101834 87a6abe5
...@@ -344,7 +344,7 @@ module.exports = function (webpackEnv) { ...@@ -344,7 +344,7 @@ module.exports = function (webpackEnv) {
// Handle node_modules packages that contain sourcemaps // Handle node_modules packages that contain sourcemaps
shouldUseSourceMap && { shouldUseSourceMap && {
enforce: "pre", enforce: "pre",
exclude: /@babel(?:\/|\\{1,2})runtime/, exclude: [/@babel(?:\/|\\{1,2})runtime/, /node_modules/],
test: /\.(js|mjs|jsx|ts|tsx|css)$/, test: /\.(js|mjs|jsx|ts|tsx|css)$/,
loader: require.resolve("source-map-loader"), loader: require.resolve("source-map-loader"),
}, },
......
<!--
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-08-02 11:43:28
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-08-10 10:40:49
* @FilePath: /bkunyun/public/index.html
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="cn"> <html lang="cn">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/facicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
...@@ -24,7 +32,7 @@ ...@@ -24,7 +32,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>平台</title> <title>北鲲云-工作流平台</title>
<script src="%PUBLIC_URL%/RDKit_minimal.js"></script> <script src="%PUBLIC_URL%/RDKit_minimal.js"></script>
<script> <script>
window window
......
...@@ -41,6 +41,8 @@ const RESTAPI = { ...@@ -41,6 +41,8 @@ const RESTAPI = {
API_SAVE_USERSPEC:`${BACKEND_API_URI_PREFIX}/cpp/workflow/saveuserspec`, // 保存用户自定义工作流模板 API_SAVE_USERSPEC:`${BACKEND_API_URI_PREFIX}/cpp/workflow/saveuserspec`, // 保存用户自定义工作流模板
API_OVERVIEW_GET:`${BACKEND_API_URI_PREFIX}/cpp/basicInformation`, // 获取概览基本信息 API_OVERVIEW_GET:`${BACKEND_API_URI_PREFIX}/cpp/basicInformation`, // 获取概览基本信息
API_TASK_OVERVIEW_LIST:`${BACKEND_API_URI_PREFIX}/cpp/workflowJobInformation`, // 查询任务概览 API_TASK_OVERVIEW_LIST:`${BACKEND_API_URI_PREFIX}/cpp/workflowJobInformation`, // 查询任务概览
API_OPERATOR_LISTSTREAMACTORS:`${BACKEND_API_URI_PREFIX}/cpp/workflow/liststreamactors`, // 获取流算子列表,可用于模糊查询,返回所有版本流算子
API_SAVE_BATCHACTOR:`${BACKEND_API_URI_PREFIX}/cpp/workflow/savebatchactor`, // 保存批算子
}; };
export default RESTAPI; export default RESTAPI;
...@@ -30,6 +30,15 @@ type IGetDatasetItemsListParams = { ...@@ -30,6 +30,15 @@ type IGetDatasetItemsListParams = {
sort?: string; // 排序 sort?: string; // 排序
} }
type IGetDatasetSizeParams = {
type?: string; // 产品id
projectId: string;
token: string;
filetoken: string;
path: string; // 数据集路径
name: string; // 数据集名称
}
type ISaveDatasetParams = { type ISaveDatasetParams = {
type?: string; type?: string;
projectId: string; projectId: string;
...@@ -229,6 +238,34 @@ class CloudEController { ...@@ -229,6 +238,34 @@ class CloudEController {
} }
} }
// 获取文件文本内容(字符串)
static JobFileDownloadText(url: any, filetoken: string, projectId: string) {
if (ApiUtils.getAuthorizationHeaders(headers)) {
url = url + urlToken(filetoken, projectId);
headers["Cache-Control"] = "no-cache";
headers["Content-Type"] = "multipart/form-data";
return axios.get(
APIOPTION() + "/download" + url + "&showhidden=false",
{
headers: headers,
}
);
}
}
// 获取dataset条数
static GetDatasetSize(params: IGetDatasetSizeParams) {
if (ApiUtils.getAuthorizationHeaders(headers)) {
headers["Cache-Control"] = "no-cache";
let url = getUrlThroughParams(params, ['filetoken','path','token'], '?')
return axios.get(
`${APIOPTION()}:5003/size${url}`,
{
headers: headers,
}
);
}
}
// 获取分子列表 // 获取分子列表
static GetDatasetItemsList(params: IGetDatasetItemsListParams) { static GetDatasetItemsList(params: IGetDatasetItemsListParams) {
......
...@@ -270,6 +270,44 @@ const getTaskOverview=(params:getTaskOverviewParams)=>{ ...@@ -270,6 +270,44 @@ const getTaskOverview=(params:getTaskOverviewParams)=>{
}) })
} }
// 获取流算子列表,可用于模糊查询,返回所有版本流算子
type getOperatorListParams = {
productId: string;
keyword?: string;
page?: number;
size?: number;
};
const getOperatorList=(params:getOperatorListParams)=>{
return request({
url:Api.API_OPERATOR_LISTSTREAMACTORS,
method:"get",
params,
})
}
type saveBatchQuery = {
productId: string;
batchName: string;
batchVersion: string;
description?: string;
isEdit?: string;
}
type saveBatchBody = Array<any>
type saveBatchParams = {
query: saveBatchQuery;
body: saveBatchBody;
}
// 提交工作流
const saveBatchActor = (params: saveBatchParams) => {
return request({
url: Api.API_SAVE_BATCHACTOR,
method: "post",
params: params.query,
data: params.body,
});
};
export { export {
current, current,
...@@ -292,5 +330,7 @@ export { ...@@ -292,5 +330,7 @@ export {
submitWorkFlow, submitWorkFlow,
getworkFlowTaskInfo, getworkFlowTaskInfo,
getOverviewInfo, getOverviewInfo,
getTaskOverview getTaskOverview,
getOperatorList,
saveBatchActor
}; };
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-07-05 14:00:37 * @Date: 2022-07-05 14:00:37
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-10 13:54:04 * @LastEditTime: 2022-08-08 16:34:52
* @FilePath: /bkunyun/src/api/workbench_api.ts * @FilePath: /bkunyun/src/api/workbench_api.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 request from "@/utils/axios/service"; import request from "@/utils/axios/service";
import Api from "./api_manager"; import Api from "./api_manager";
import { IGetOperatorListParams, IFetchOperatorListParams } from "./workbenchInterface"; import { IGetOperatorListParams } from "./workbenchInterface";
function current() { function current() {
return request({ return request({
...@@ -139,15 +139,6 @@ const fetchOperatorList = (params: IGetOperatorListParams) => { ...@@ -139,15 +139,6 @@ const fetchOperatorList = (params: IGetOperatorListParams) => {
}; };
// 获取指定版本算子
const fetchVersionOperator = (params: IFetchOperatorListParams) => {
return request({
url: Api.API_VERSION_OPERATOR,
method: "get",
params,
});
};
// 保存用户自定义工作流模板 // 保存用户自定义工作流模板
const saveUserSpec = (params: any) => { const saveUserSpec = (params: any) => {
return request({ return request({
...@@ -168,6 +159,5 @@ export { ...@@ -168,6 +159,5 @@ export {
deleteWorkflowJob, deleteWorkflowJob,
cancelWorkflowJob, cancelWorkflowJob,
fetchOperatorList, fetchOperatorList,
fetchVersionOperator,
saveUserSpec saveUserSpec
}; };
.FSBox { .FSBox {
width: 900px; width: 900px;
height: 650px;
position: relative; position: relative;
} }
......
...@@ -12,16 +12,20 @@ import AddIcon from "@mui/icons-material/Add"; ...@@ -12,16 +12,20 @@ import AddIcon from "@mui/icons-material/Add";
import AddProject from "@/views/Project/components/AddProject"; import AddProject from "@/views/Project/components/AddProject";
import React, { useState } from "react"; import React, { useState } from "react";
import MyButton from "@/components/mui/MyButton"; import MyButton from "@/components/mui/MyButton";
import { toJS } from "mobx";
import { observer } from "mobx-react-lite";
import { useStores } from "@/store/index";
const NoProject = () => { const NoProject = observer((props: any) => {
// 新建弹窗显示控制 // 新建弹窗显示控制
const [addOpen, setAddOpen] = useState(false); const [addOpen, setAddOpen] = useState(false);
const { currentProjectStore } = useStores();
let projectList = toJS(currentProjectStore.projectList)
return ( return (
<div className={style.noProject}> <div className={style.noProject}>
<img src={noData} alt="" className={style.noDataImg} /> <img src={noData} alt="" className={style.noDataImg} />
<div className={style.text1}>当前产品暂无项目</div> <div className={style.text1}>{projectList.length > 0 ? "当前未选中任何项目" : "当前产品暂无项目"}</div>
<div className={style.text2}>请先创建项目</div> <div className={style.text2}>{projectList.length > 0 ? "或创建新项目" : "请先创建项目"}</div>
<MyButton <MyButton
text='创建项目' text='创建项目'
variant="contained" variant="contained"
...@@ -35,6 +39,6 @@ const NoProject = () => { ...@@ -35,6 +39,6 @@ const NoProject = () => {
<AddProject addOpen={addOpen} setAddOpen={setAddOpen} /> <AddProject addOpen={addOpen} setAddOpen={setAddOpen} />
</div> </div>
); );
}; });
export default NoProject; export default NoProject;
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
align-items: center; align-items: center;
border: 1px solid #e6e8eb; border: 1px solid #e6e8eb;
border-radius: 4px; border-radius: 4px;
background-color: #F0F2F5; background-color: #f0f2f5;
cursor: pointer; cursor: pointer;
height: 32px; height: 32px;
box-sizing: border-box; box-sizing: border-box;
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
} }
.radio { .radio {
position: relative; position: relative;
min-width: 64px; min-width: 63px;
height: 28px; height: 28px;
box-sizing: border-box; box-sizing: border-box;
font-size: 14px; font-size: 14px;
...@@ -28,18 +28,8 @@ ...@@ -28,18 +28,8 @@
white-space: nowrap; white-space: nowrap;
} }
.radio:not(:last-child)::before {
position: absolute;
width: 1px;
height: 16px;
top: 6px;
right: 0;
content: '';
background-color: #D1D6DE;
}
.radioActive { .radioActive {
color: #1370ff; color: #1370ff;
background-color: #fff; background-color: #fff;
box-shadow: 2px 4px 12px 0px rgba(0,27,63,0.06); box-shadow: 2px 4px 12px 0px rgba(0, 27, 63, 0.06);
} }
...@@ -43,7 +43,7 @@ const getTransitionComponent = (transition: "grow" | "fade") => { ...@@ -43,7 +43,7 @@ const getTransitionComponent = (transition: "grow" | "fade") => {
const MySnackbarProvider = ({ const MySnackbarProvider = ({
vertical = "top", vertical = "top",
horizontal = "center", horizontal = "center",
autoHideDuration = 890000, autoHideDuration = 3000,
snackerClasses, snackerClasses,
ClickAwayListenerProps, ClickAwayListenerProps,
ContentProps, ContentProps,
...@@ -76,20 +76,20 @@ const MySnackbarProvider = ({ ...@@ -76,20 +76,20 @@ const MySnackbarProvider = ({
} = useMySnackbar(); } = useMySnackbar();
// .MuiAlert-filledInfo // .MuiAlert-filledInfo
const getColorStyle = useMemo(()=>{ const getColorStyle = useMemo(() => {
if(messageInfo.severity === 'success'){ if (messageInfo.severity === "success") {
return '#02AB83' return "#02AB83";
} }
if(messageInfo.severity === 'info'){ if (messageInfo.severity === "info") {
return '#1370FF' return "#1370FF";
} }
if(messageInfo.severity === 'warning'){ if (messageInfo.severity === "warning") {
return '#FFB919' return "#FFB919";
} }
if(messageInfo.severity === 'error'){ if (messageInfo.severity === "error") {
return '#FF4E4E' return "#FF4E4E";
} }
},[messageInfo.severity]) }, [messageInfo.severity]);
const theme = createTheme({ const theme = createTheme({
components: { components: {
...@@ -98,20 +98,17 @@ const MySnackbarProvider = ({ ...@@ -98,20 +98,17 @@ const MySnackbarProvider = ({
root: { root: {
color: getColorStyle, color: getColorStyle,
"& .MuiAlert-icon": { "& .MuiAlert-icon": {
color: getColorStyle color: getColorStyle,
},
}, },
}
}, },
}, },
}, },
}); });
return ( return (
<Fragment> <Fragment>
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<Snackbar <Snackbar
open={open} open={open}
autoHideDuration={autoHideDuration} autoHideDuration={autoHideDuration}
......
...@@ -122,12 +122,16 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => { ...@@ -122,12 +122,16 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => {
"& .MuiPaper-root": { "& .MuiPaper-root": {
// 设置最大宽度, 实际宽度让子元素撑大 // 设置最大宽度, 实际宽度让子元素撑大
maxWidth: "1920px", maxWidth: "1920px",
borderRadius: "8px"
}, },
}, },
}} }}
> >
{isHideHeader ? null : ( {isHideHeader ? null : (
<DialogTitle id="alert-dialog-title"> <DialogTitle
id="alert-dialog-title"
sx={{ padding: "20px 24px" }}
>
<div <div
style={{ style={{
display: "flex", display: "flex",
...@@ -136,10 +140,10 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => { ...@@ -136,10 +140,10 @@ const MyDialog: React.FunctionComponent<IDialogProps> = (props) => {
fontWeight: 600, fontWeight: 600,
}} }}
> >
<span style={{fontSize: 16, lineHeight: '24px', color: '#1E2633'}}>{title}</span> <span style={{ fontSize: 16, lineHeight: '24px', color: '#1E2633' }}>{title}</span>
<CloseIcon <CloseIcon
onClick={onClose} onClick={onClose}
sx={{ color: "#C2C6CC", cursor: "pointer", ":hover": { background: "#f0f2f5", borderRadius:'2px' } }} sx={{ color: "#C2C6CC", cursor: "pointer", ":hover": { background: "#f0f2f5", borderRadius: '2px' } }}
/> />
</div> </div>
</DialogTitle> </DialogTitle>
......
...@@ -68,7 +68,8 @@ const MyPopconfirm = (props: IMyPopconfirmProps) => { ...@@ -68,7 +68,8 @@ const MyPopconfirm = (props: IMyPopconfirmProps) => {
zIndex: 2000, zIndex: 2000,
bgcolor: "#fff", bgcolor: "#fff",
minWidth: "200px", minWidth: "200px",
borderRadius: "2px", borderRadius: "4px",
fontSize:"14px",
padding: "20px 16px", padding: "20px 16px",
boxShadow: "0px 3px 10px 0px rgba(0, 24, 57, 0.14)", boxShadow: "0px 3px 10px 0px rgba(0, 24, 57, 0.14)",
}} }}
......
...@@ -11,7 +11,7 @@ import Radio from "@mui/material/Radio"; ...@@ -11,7 +11,7 @@ import Radio from "@mui/material/Radio";
import RadioGroup, { RadioGroupProps } from "@mui/material/RadioGroup"; import RadioGroup, { RadioGroupProps } from "@mui/material/RadioGroup";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl"; import FormControl from "@mui/material/FormControl";
import FormHelperText from '@mui/material/FormHelperText'; import FormHelperText from "@mui/material/FormHelperText";
interface IMyRadioProps extends RadioGroupProps { interface IMyRadioProps extends RadioGroupProps {
value: any; value: any;
...@@ -20,7 +20,7 @@ interface IMyRadioProps extends RadioGroupProps { ...@@ -20,7 +20,7 @@ interface IMyRadioProps extends RadioGroupProps {
variant?: "standard" | "outlined" | "filled"; variant?: "standard" | "outlined" | "filled";
error?: boolean; error?: boolean;
helperText?: string; helperText?: string;
}; }
type ICheckBoxOption = { type ICheckBoxOption = {
value: any; value: any;
...@@ -45,7 +45,14 @@ export const optionsTransform = ( ...@@ -45,7 +45,14 @@ export const optionsTransform = (
}; };
export default function MyRadio(props: IMyRadioProps) { export default function MyRadio(props: IMyRadioProps) {
const { value, options, onChange , error = false, helperText, variant} = props; const {
value,
options,
onChange,
error = false,
helperText,
variant,
} = props;
return ( return (
<FormControl fullWidth variant={variant} error={error}> <FormControl fullWidth variant={variant} error={error}>
...@@ -62,7 +69,7 @@ export default function MyRadio(props: IMyRadioProps) { ...@@ -62,7 +69,7 @@ export default function MyRadio(props: IMyRadioProps) {
<FormControlLabel <FormControlLabel
key={option.value} key={option.value}
value={option.value} value={option.value}
control={<Radio />} control={<Radio sx={{ color: "rgba(209, 214, 222, 1)" }} />}
label={option.label} label={option.label}
disabled={option.disabled} disabled={option.disabled}
/> />
......
...@@ -128,7 +128,7 @@ export default function MySelect(props: IProps) { ...@@ -128,7 +128,7 @@ export default function MySelect(props: IProps) {
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<FormControl fullWidth={fullWidth} variant={variant}> <FormControl fullWidth={fullWidth} variant={variant} error={error}>
{isTitle ? ( {isTitle ? (
<InputLabel id="demo-simple-select-label"> <InputLabel id="demo-simple-select-label">
{title || "请选择"} {title || "请选择"}
......
...@@ -11,7 +11,8 @@ import Switch, { SwitchProps } from "@mui/material/Switch"; ...@@ -11,7 +11,8 @@ import Switch, { SwitchProps } from "@mui/material/Switch";
import { ThemeProvider, createTheme } from "@mui/material/styles"; import { ThemeProvider, createTheme } from "@mui/material/styles";
interface IMySwitchProps extends SwitchProps { interface IMySwitchProps extends SwitchProps {
value: boolean; value?: boolean;
defaultChecked?: boolean;
onChange?: any; onChange?: any;
disabled?: boolean; disabled?: boolean;
} }
...@@ -52,12 +53,13 @@ const theme = createTheme({ ...@@ -52,12 +53,13 @@ const theme = createTheme({
}); });
const MySwitch = (props: IMySwitchProps) => { const MySwitch = (props: IMySwitchProps) => {
const { value, onChange, disabled = false, size, ...other } = props; const { value, defaultChecked, onChange, disabled = false, size, ...other } = props;
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<Switch <Switch
checked={value} checked={value}
disabled={disabled} disabled={disabled}
defaultChecked={defaultChecked}
onChange={onChange} onChange={onChange}
sx={ sx={
size === "medium" size === "medium"
......
/* /*
* @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-07-04 20:18:17 * @LastEditTime: 2022-08-10 11:06:12
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSetting/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
*/ */
...@@ -44,6 +44,9 @@ const theme = createTheme({ ...@@ -44,6 +44,9 @@ const theme = createTheme({
minWidth: "20px", minWidth: "20px",
marginRight: "32px", marginRight: "32px",
color: "#8A9099", color: "#8A9099",
":hover": {
color: "#1E2633",
},
selected: { selected: {
color: "#1976d2", color: "#1976d2",
}, },
...@@ -95,7 +98,14 @@ const Tabs = (props: IProps) => { ...@@ -95,7 +98,14 @@ const Tabs = (props: IProps) => {
) : ( ) : (
"" ""
)} )}
<Typography sx={{ fontSize: "14px", lineHeight: "22px", height: "22px", fontWeight: "400" }}> <Typography
sx={{
fontSize: "14px",
lineHeight: "22px",
height: "22px",
fontWeight: "400",
}}
>
{item.label} {item.label}
</Typography> </Typography>
</Box> </Box>
...@@ -118,7 +128,7 @@ const Tabs = (props: IProps) => { ...@@ -118,7 +128,7 @@ const Tabs = (props: IProps) => {
<Tab <Tab
key={key} key={key}
label={labelRender(item, key)} label={labelRender(item, key)}
value={item.value} value={item.value || ""}
id={item.value} id={item.value}
disabled={item.disabled} disabled={item.disabled}
/> />
...@@ -130,7 +140,11 @@ const Tabs = (props: IProps) => { ...@@ -130,7 +140,11 @@ const Tabs = (props: IProps) => {
?.filter((item) => !item.hide) ?.filter((item) => !item.hide)
.map((item) => { .map((item) => {
return ( return (
<TabPanel sx={tabPanelSx} value={item.value} key={item.value}> <TabPanel
sx={tabPanelSx}
value={item.value || ""}
key={item.value}
>
{item.component} {item.component}
</TabPanel> </TabPanel>
); );
......
...@@ -38,22 +38,36 @@ ...@@ -38,22 +38,36 @@
} }
.li { .li {
background-color: RGBA(240, 242, 245, 1); background-color: RGBA(240, 242, 245, 1);
padding: 7px 7px 7px 28px; padding: 7px 9px;
color: rgba(30, 38, 51, 1); color: rgba(30, 38, 51, 1);
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
margin-bottom: 12px; margin-bottom: 12px;
position: relative; position: relative;
display: flex;
justify-content: space-between;
align-items: center;
}
.nameVersion {
flex: 1;
/* text-align: left; */
margin-left: 13px;
word-wrap: break-word;
/* text-overflow: clip; */
max-width: 140px;
} }
.name { .name {
margin-right: 8px; margin-right: 8px;
} }
.version {
white-space: nowrap;
}
.icon { .icon {
width: 6px; width: 6px;
height: 10px; height: 10px;
position: absolute; /* position: absolute;
top: 13px; top: 13px;
left: 9px; left: 9px; */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
...@@ -67,3 +81,12 @@ ...@@ -67,3 +81,12 @@
height: 2px; height: 2px;
background-color: rgba(86, 92, 102, 1); background-color: rgba(86, 92, 102, 1);
} }
.loading {
display: flex;
justify-content: center;
align-items: center;
line-height: 20px;
font-size: 12px;
color: rgba(86, 92, 102, 1);
padding: 4px;
}
import MyDialog from "@/components/mui/MyDialog";
import MyInput from "@/components/mui/MyInput";
import { checkIsNumberLetterChinese } from "@/utils/util";
import _ from "lodash";
import { useState } from "react";
import useMyRequest from "@/hooks/useMyRequest";
import { saveBatchActor } from "@/api/project_api";
import { useStores } from "@/store";
import { useMessage } from "@/components/MySnackbar";
import { toJS } from "mobx";
import { ITask } from "@/views/Project/ProjectSubmitWork/interface";
interface IProps {
saveFormDialog: boolean;
setSaveFormDialog: (val: boolean) => void;
operatorList: ITask[];
setShowCustomOperator: any;
}
const SaveOperator = (props: IProps) => {
const {
saveFormDialog,
setSaveFormDialog,
operatorList,
setShowCustomOperator,
} = props;
const { currentProjectStore } = useStores();
const Message = useMessage();
const productId = toJS(currentProjectStore.currentProductInfo.id);
const [title, setTitle] = useState("");
const [version, setVersion] = useState("1.0.0");
const [description, setDescription] = useState("");
// 保存批算子
const { run: saveBatchActorRun, loading } = useMyRequest(saveBatchActor, {
onSuccess: (res) => {
Message.success("保存算子成功");
sessionStorage.setItem("operatorList", "[]");
setSaveFormDialog(false);
setShowCustomOperator();
},
});
const [titleHelper, setTitleHelper] = useState({
// 算子名称错误提示
error: false,
helperText: "",
});
const [versionHelper, setVersionHelper] = useState({
// 算子版本错误提示
error: false,
helperText: "",
});
// 关闭表单弹窗
const handleCloseDialog = () => {
setSaveFormDialog(false);
};
// 算子名称
const handleTitleChange = (e: any) => {
const title = e.target.value;
setTitle(title);
checkTitle(title);
};
// 算子版本
const handleVersionChange = (e: any) => {
let version = e.target.value;
setVersion(version);
checkVersion(version);
};
// 算子描述
const handleDescriptionChange = (e: any) => {
let description = e.target.value;
if (description.length < 301) {
setDescription(description);
}
};
// 校验算子名称
const checkTitle = (title: string) => {
if (!title) {
setTitleHelper({
error: true,
helperText: "必须输入算子名称",
});
return false;
} else if (title.length > 15) {
setTitleHelper({
error: true,
helperText: "格式不正确,必须在15字符以内,仅限大小写字母、数字、中文",
});
return false;
} else if (!checkIsNumberLetterChinese(title)) {
setTitleHelper({
error: true,
helperText: "格式不正确,必须在15字符以内,仅限大小写字母、数字、中文",
});
return false;
} else {
setTitleHelper({
error: false,
helperText: "",
});
return true;
}
};
// 校验版本号格式
const checkVersion = (version: string) => {
if (/^[1-9]\d?(\.(0|[1-9]\d?)){2}$/.test(version)) {
setVersionHelper({
error: false,
helperText: "",
});
return true;
} else {
setVersionHelper({
error: true,
helperText: "格式不正确,必须为X.Y.Z格式,且XYZ必须为0~99的正整数",
});
return false;
}
};
// 坐标转换
const positionTransform = () => {
const copyOperatorList: ITask[] = _.cloneDeep(operatorList);
const positionXArr = copyOperatorList.map((operatorLi) => {
return operatorLi.position.x;
});
const positionYArr = copyOperatorList.map((operatorLi) => {
return operatorLi.position.y;
});
let minPositionX = Math.min(...positionXArr);
let minPositionY = Math.min(...positionYArr);
let startingPointX = minPositionX - 40;
let startingPointY = minPositionY - 10;
return copyOperatorList.map((operatorLi) => {
return {
...operatorLi,
position: {
x: operatorLi.position.x - startingPointX,
y: operatorLi.position.y - startingPointY,
},
};
});
};
// 表单弹窗确定,新建算子
const handleOncofirm = () => {
if (checkTitle(title) && checkVersion(version)) {
saveBatchActorRun({
query: {
productId: productId as string,
batchName: title,
batchVersion: version,
description: description,
},
body: positionTransform(),
});
}
};
return (
<MyDialog
open={saveFormDialog}
title="保存算子"
onClose={handleCloseDialog}
onConfirm={handleOncofirm}
loading={loading}
>
<div style={{ width: "388px" }}>
<MyInput
value={title}
label="算子名称"
onChange={handleTitleChange}
required
error={titleHelper.error}
helperText={titleHelper.helperText}
style={{ margin: "20px 0" }}
></MyInput>
<MyInput
value={version}
label="版本号"
onChange={handleVersionChange}
error={versionHelper.error}
helperText={versionHelper.helperText}
style={{ marginBottom: "20px" }}
></MyInput>
<div style={{ position: "relative" }}>
<MyInput
value={description}
id="desc"
label="模板描述"
placeholder="模板描述"
onChange={handleDescriptionChange}
multiline
rows={4}
/>
<span
style={{
position: "absolute",
bottom: "7px",
right: "12px",
color: description.length >= 300 ? "#d32f2f" : "#C2C6CC",
}}
>
{description.length}/300
</span>
</div>
</div>
</MyDialog>
);
};
export default SaveOperator;
...@@ -3,33 +3,188 @@ import { observer } from "mobx-react-lite"; ...@@ -3,33 +3,188 @@ import { observer } from "mobx-react-lite";
import FullScreenDrawer from "@/components/CommonComponents/FullScreenDrawer"; import FullScreenDrawer from "@/components/CommonComponents/FullScreenDrawer";
import MyButton from "@/components/mui/MyButton"; import MyButton from "@/components/mui/MyButton";
import OperatorList from "./components/OperatorList"; import OperatorList from "./components/OperatorList";
import Flow from "../Project/components/Flow"; // import Flow from "../Project/components/Flow";
import { useMessage } from "@/components/MySnackbar";
import BatchOperatorFlow from "../Project/components/Flow/components/BatchOperatorFlow";
import SaveOperator from "./components/SaveOperator";
import { ITask } from "../Project/ProjectSubmitWork/interface";
import _ from "lodash";
import styles from "./index.module.css"; import styles from "./index.module.css";
type IProps = { type IProps = {
setShowCustomOperator: any; setShowCustomOperator: any;
initOperatorList: ITask[];
}; };
const CustomOperator = observer((props: IProps) => { const CustomOperator = observer((props: IProps) => {
const { setShowCustomOperator } = props; const { setShowCustomOperator, initOperatorList } = props;
const Message = useMessage();
const [operatorList, setOperatorList] = useState<ITask[]>(initOperatorList);
const [saveFormDialog, setSaveFormDialog] = useState(false);
// const [showCustomOperator, setShowCustomOperator] = useState(false); // const [showCustomOperator, setShowCustomOperator] = useState(false);
/** 设置选中唯一标识符 */
const handleNodeClick = useCallback((val: string) => {
// setSelectTaskId(val);
// console.log(val);
}, []);
// 判断 每个流算子必须至少有一条连接线。
const checkHasOneLine = (sourceArr: string[], targetArr: string[]) => {
console.log("checkHasOneLine");
const all = _.uniq([...sourceArr, ...targetArr]);
if (all.length === operatorList.length) {
console.log("checkHasOneLine1");
return true;
} else {
console.log("checkHasOneLine2");
return false;
}
// _.uniq([2, 1, 2]);
};
// 判断 每个起始算子(可以有多个起始点)的输入必须为文件的路径输入或数据集的路径输入。
const checkIn = (targetArr: string[]) => {
console.log("checkIn");
const uniqTargetArr = _.uniq(targetArr);
if (uniqTargetArr.length === operatorList.length) {
// 流节点连成一个圈了
return false;
}
let check = true;
operatorList.forEach((flowNode) => {
if (uniqTargetArr.indexOf(flowNode.id) === -1) {
// 该节点的输入没有连线 也就是说这个节点是起点
const inArr = flowNode.parameters.filter(
(parameter) => parameter.parameterGroup === "in"
);
if (inArr.length > 0) {
if (
!inArr.some((inItem) => {
return (
(inItem.domType || "").toLowerCase() === "dataset" ||
(inItem.domType || "").toLowerCase() === "path" ||
(inItem.domType || "").toLowerCase() === "file"
);
})
) {
check = false;
}
} else {
// 起点没有输入
check = false;
}
}
});
return check;
};
// 判断 起码有一个结尾算子(可以有多个结尾点)的输出必须为文件保存或数据集保存。
const checkOut = (sourceArr: string[]) => {
console.log("checkOut");
const uniqSourceArr = _.uniq(sourceArr);
if (uniqSourceArr.length === operatorList.length) {
// 流节点连成一个圈了
return false;
}
let check = true;
operatorList.forEach((flowNode) => {
if (uniqSourceArr.indexOf(flowNode.id) === -1) {
// 该节点的输入没有连线 也就是说这个节点是起点
const outArr = flowNode.parameters.filter(
(parameter) => parameter.parameterGroup === "out"
);
if (outArr.length > 0) {
if (
!outArr.some((outItem) => {
return (
(outItem.domType || "").toLowerCase() === "dataset" ||
(outItem.domType || "").toLowerCase() === "file" ||
(outItem.domType || "").toLowerCase() === "path"
);
})
) {
check = false;
}
} else {
// 起点没有输入
check = false;
}
}
});
return check;
};
useEffect(() => {
sessionStorage.setItem("operatorList", JSON.stringify(operatorList));
}, [operatorList]);
const handleCheck = () => {
if (operatorList.length === 0) {
Message.error("内容不能为空!");
return;
}
let sourceArr: string[] = [];
let targetArr: string[] = [];
operatorList.forEach((flowNode) => {
flowNode.edges.forEach((edge) => {
edge.source && sourceArr.push(edge.source);
edge.target && targetArr.push(edge.target);
});
});
console.log("operatorList", operatorList);
console.log("sourceArr", sourceArr);
console.log("targetArr", targetArr);
if (!checkHasOneLine([...sourceArr], [...targetArr])) {
console.log("checkHasOneLine");
Message.error("内容校验未通过,请检查!");
return;
}
if (!checkIn([...targetArr])) {
console.log("checkIn");
Message.error("内容校验未通过,请检查!");
return;
}
if (!checkOut([...sourceArr])) {
console.log("checkOut");
Message.error("内容校验未通过,请检查!");
return;
}
setSaveFormDialog(true);
};
return ( return (
<FullScreenDrawer handleClose={setShowCustomOperator} zIndex={1100}> <FullScreenDrawer handleClose={setShowCustomOperator} zIndex={1100}>
<div className={styles.customOperator}> <div className={styles.customOperator}>
<div className={styles.coTop}> <div className={styles.coTop}>
<div className={styles.coTitle}>添加算子</div> <div className={styles.coTitle}>添加算子</div>
<MyButton text="添加"></MyButton> <MyButton
text="添加"
onClick={() => {
handleCheck();
}}
></MyButton>
<SaveOperator
saveFormDialog={saveFormDialog}
setSaveFormDialog={setSaveFormDialog}
operatorList={operatorList}
setShowCustomOperator={setShowCustomOperator}
></SaveOperator>
</div> </div>
<div className={styles.coContent}> <div className={styles.coContent} id="customOperatorFlow">
<OperatorList /> <OperatorList
<Flow operatorList={operatorList}
showControls={false} setOperatorList={setOperatorList}
// tasks={templateConfigInfo} />
// setTasks={setTemplateConfigInfo} <BatchOperatorFlow
tasks={operatorList}
setTasks={setOperatorList}
type="edit" type="edit"
// onFlowNodeClick={handleNodeClick} onFlowNodeClick={handleNodeClick}
// ListenState={!saveFormDialog} flowNodeDraggable={true}
ListenState={!saveFormDialog}
showVersion={true}
showControls={false}
/> />
</div> </div>
</div> </div>
......
...@@ -23,11 +23,12 @@ ...@@ -23,11 +23,12 @@
color: #565c66; color: #565c66;
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
border-left: 3px solid #fff;
} }
.listItem:hover { .listItem:hover {
background-color: #EEF1F5; background-color: #eef1f5;
} }
.routerIcon{ .routerIcon {
vertical-align: middle; vertical-align: middle;
margin-right: 12px; margin-right: 12px;
line-height: 22px; line-height: 22px;
......
...@@ -62,7 +62,7 @@ const MenuLayout = observer(() => { ...@@ -62,7 +62,7 @@ const MenuLayout = observer(() => {
onClick={() => item.type === "page" && navigate(item.path)} onClick={() => item.type === "page" && navigate(item.path)}
> >
<img className={style.routerIcon} src={routerIcon(item.id || '', `/v3${item.path}` === pathname) || undefined} alt='' /> <img className={style.routerIcon} src={routerIcon(item.id || '', `/v3${item.path}` === pathname) || undefined} alt='' />
<span style={{ verticalAlign: 'middle' }}>{item.name}</span> <span style={{ verticalAlign: 'middle', fontWeight: '500' }}>{item.name}</span>
</li> </li>
); );
} }
......
...@@ -97,7 +97,7 @@ const AddFolder = (props: IAddFolderProps) => { ...@@ -97,7 +97,7 @@ const AddFolder = (props: IAddFolderProps) => {
<MyButton <MyButton
text="新建文件夹" text="新建文件夹"
variant="outlined" variant="outlined"
size="small" size="medium"
onClick={() => setDeleteDialogOpen(true)} onClick={() => setDeleteDialogOpen(true)}
disabled={ disabled={
selectIds.length !== 0 || !isPass("PROJECT_DATA_ADDDIR", "USER") selectIds.length !== 0 || !isPass("PROJECT_DATA_ADDDIR", "USER")
......
...@@ -159,7 +159,7 @@ const SeeDataset = observer((props: ISeeDatasetProps) => { ...@@ -159,7 +159,7 @@ const SeeDataset = observer((props: ISeeDatasetProps) => {
}; };
return ( return (
<FullScreenDrawer handleClose={props.handleClose}> <FullScreenDrawer handleClose={props.handleClose} zIndex={1100}>
<div <div
className={classNames({ className={classNames({
[style.datasetBox]: true, [style.datasetBox]: true,
......
...@@ -164,12 +164,12 @@ const Save = (props: ISaveProps) => { ...@@ -164,12 +164,12 @@ const Save = (props: ISaveProps) => {
value={showDpath} value={showDpath}
required required
label="保存路径" label="保存路径"
InputProps={{
endAdornment: (
<img
onClick={() => { onClick={() => {
setFileSelectOpen(true); setFileSelectOpen(true);
}} }}
InputProps={{
endAdornment: (
<img
src={fileSelectIcon} src={fileSelectIcon}
alt="选择输出路径" alt="选择输出路径"
style={{ cursor: "pointer" }} style={{ cursor: "pointer" }}
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
.uploderBoxLeft { .uploderBoxLeft {
width: 300px; width: 300px;
} }
.dropTitle { .dropTitle {
line-height: 22px; line-height: 22px;
font-size: 14px; font-size: 14px;
...@@ -14,6 +15,7 @@ ...@@ -14,6 +15,7 @@
font-weight: 600; font-weight: 600;
} }
.dropBox { .dropBox {
cursor: pointer;
height: 300px; height: 300px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -22,6 +24,9 @@ ...@@ -22,6 +24,9 @@
border: 1px dashed #d1d6de; border: 1px dashed #d1d6de;
border-radius: 4px; border-radius: 4px;
} }
.dropBox:hover {
background-color: #f7f8fa;
}
.dropBoxDragActive { .dropBoxDragActive {
background-color: #f7f8fa; background-color: #f7f8fa;
} }
...@@ -54,12 +59,12 @@ ...@@ -54,12 +59,12 @@
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
} }
.fileIconBoxText{ .fileIconBoxText {
display: block; display: block;
width: 260px; width: 260px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
-o-text-overflow:ellipsis; -o-text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.noFile { .noFile {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
position: relative; position: relative;
} }
.projectDataStickyTop { .projectDataStickyTop {
padding: 28px 24px; padding: 28px 24px 64px;
position: relative; position: relative;
} }
.projectDataTitle { .projectDataTitle {
...@@ -56,9 +56,10 @@ ...@@ -56,9 +56,10 @@
.projectDataStickyBox { .projectDataStickyBox {
height: 64px; height: 64px;
position: sticky; width: calc(100vw - 220px);
bottom: -1px; position: fixed;
border: 1px solid #ebedf0; bottom: 0px;
border-top: 1px solid #ebedf0;
z-index: 100; z-index: 100;
background-color: #fff; background-color: #fff;
display: flex; display: flex;
......
...@@ -515,7 +515,7 @@ const ProjectData = observer(() => { ...@@ -515,7 +515,7 @@ const ProjectData = observer(() => {
<MyButton <MyButton
text="上传文件" text="上传文件"
variant="contained" variant="contained"
size="small" size="medium"
style={{ marginRight: "12px" }} style={{ marginRight: "12px" }}
onClick={() => setUploaderDialogOpen(true)} onClick={() => setUploaderDialogOpen(true)}
disabled={ disabled={
......
.logView {
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .5);
z-index: 1002;
}
.logViewBox {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 900px;
height: 600px;
background: #282C34;
box-shadow: 2px 4px 20px 0px rgba(0, 15, 45, 0.1200);
border-radius: 8px;
}
.close {
position: absolute;
right: 0;
top: -28px;
cursor: pointer;
color: #fff;
}
.logViewTop {
/* height: 30px; */
background-color: #1D2126;
border-radius: 8px 8px 0 0;
display: flex;
font-size: 12px;
color: #8A9099;
overflow: auto;
}
.logViewTop::-webkit-scrollbar-track {
background-color: #282C34;
}
.logTitle {
display: flex;
align-items: center;
height: 30px;
line-height: 20px;
padding: 0 24px;
cursor: pointer;
}
.logTitleSelected {
background: #282C34;
color: #FFFFFF;
}
.fileIcon{
width: 14px!important;
margin-right: 4px;
}
.logViewContent {
position: relative;
box-sizing: border-box;
height: 510px;
padding: 16px 24px 0;
color: #D1D6DE;
white-space:pre-wrap;
word-break: break-word;
overflow: auto;
font-size: 14px;
line-height: 22px;
}
.logViewContentMask{
height: 16px;
width: 852px;
background-color: #282C34;
position: absolute;
top: 30px;
left: 24px;
z-index: 1005;
}
.logViewContent::-webkit-scrollbar-track {
background-color: #282C34;
}
.logViewBottom {
display: flex;
align-items: center;
justify-content: end;
height: 60px;
padding-right: 24px;
}
\ No newline at end of file
import { useState, useCallback, useEffect, useMemo } from "react";
import classnames from "classnames";
import style from "./index.module.css";
import CloseIcon from "@mui/icons-material/Close";
import MyButton from "@/components/mui/MyButton";
import InsertDriveFileOutlinedIcon from "@mui/icons-material/InsertDriveFileOutlined";
import CloudEController from "@/api/fileserver/CloudEController";
import { useStores } from "@/store";
import { toJS } from "mobx";
type LogViewProps = {
isshow: boolean;
handleClose: () => void;
logs: any[];
};
const LogView = (props: LogViewProps) => {
const { isshow, handleClose, logs } = props;
const { currentProjectStore } = useStores();
const fileToken = toJS(currentProjectStore.currentProjectInfo.filetoken);
const projectId = toJS(currentProjectStore.currentProjectInfo.id);
// 当前选择的日志
const [logCurrent, setLogCurrent] = useState<number>(0);
// 当前日志的内容文本
const [logText, setLogText] = useState('')
// 当前日志路径
const [logPath, setLogPath] = useState('')
useEffect(() => {
setLogPath(logs[logCurrent]?.logPath)
}, [logs]);
// 请求日志文本
useEffect(() => {
if (logPath) {
const path = logPath.slice(12)
CloudEController.JobFileDownloadText(
path,
fileToken as string,
projectId as string
)?.then((res) => {
setLogText(res.data)
})
}else{
setLogText("")
}
}, [logPath]);
// 选择日志时改变日志路径
useEffect(() => {
setLogPath(logs[logCurrent]?.logPath)
}, [logCurrent]);
// 下载当前日志
const handleDownLoad=()=>{
const path = logPath.slice(12)
CloudEController.JobFileDownload(
path,
fileToken as string,
projectId as string
);
}
return <div className={style.logView} style={isshow ? {} : { display: "none" }}>
<div className={style.logViewBox}>
<CloseIcon onClick={handleClose} className={style.close} />
<div className={style.logViewContentMask}></div>
<div className={style.logViewTop}>
{logs.map((item: any, index: number) => {
return <div
key={index}
onClick={() => { setLogCurrent(index) }}
className={classnames({
[style.logTitle]: true,
[style.logTitleSelected]: index === logCurrent,
})}>
<InsertDriveFileOutlinedIcon className={style.fileIcon} />
<span>{item.logName}</span>
</div>
})}
</div>
<div className={style.logViewContent}>
{logText}
</div>
<div className={style.logViewBottom}>
<MyButton text='下载当前日志' onClick={handleDownLoad} />
</div>
</div>
</div>
}
export default LogView
\ No newline at end of file
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
margin: 6px 0; margin: 6px 0;
} }
.outputLiLeft { .outputLiLeft {
cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
color: rgba(19, 112, 255, 1); color: rgba(19, 112, 255, 1);
......
...@@ -50,7 +50,7 @@ const TaskCard = (props: TaskCardProps) => { ...@@ -50,7 +50,7 @@ const TaskCard = (props: TaskCardProps) => {
const goToProjectData = (path: string) => { const goToProjectData = (path: string) => {
const lastIndex = path.lastIndexOf("/"); const lastIndex = path.lastIndexOf("/");
if (lastIndex !== -1) { if (lastIndex !== -1) {
path = path.slice(0, lastIndex + 1); path = path.slice(0, lastIndex);
} }
path = path.slice(12); path = path.slice(12);
if (path) { if (path) {
...@@ -66,7 +66,7 @@ const TaskCard = (props: TaskCardProps) => { ...@@ -66,7 +66,7 @@ const TaskCard = (props: TaskCardProps) => {
// 跳转详情页 // 跳转详情页
const gotoDetail = (id: string) => { const gotoDetail = (id: string) => {
navigate(`/product/cadd/projectJobDetail`, { navigate(`/product/cadd/projectJobDetail`, {
state: { taskId: id }, state: { taskId: id, from: 'projectOverview' },
}); });
} }
// 渲染状态图标 // 渲染状态图标
......
...@@ -45,6 +45,8 @@ const ProjectOverview = observer(() => { ...@@ -45,6 +45,8 @@ const ProjectOverview = observer(() => {
}); });
useEffect(() => { useEffect(() => {
// 切项目时重置为初始7
setDay("7");
if (currentProjectStore.currentProjectInfo.id) { if (currentProjectStore.currentProjectInfo.id) {
getOverview({ getOverview({
id: currentProjectStore.currentProjectInfo.id as string, id: currentProjectStore.currentProjectInfo.id as string,
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
line-height: 22px; line-height: 22px;
font-size: 14px; font-size: 14px;
font-weight: 550; font-weight: 550;
margin-bottom: 12px; margin-bottom: 8px;
} }
.projectInfoName::after { .projectInfoName::after {
content: "*"; content: "*";
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
color: #ff4e4e; color: #ff4e4e;
margin-bottom: 8px; margin-bottom: 20px;
} }
.deleteText2 { .deleteText2 {
color: #1e2633; color: #1e2633;
......
...@@ -276,20 +276,20 @@ const BaseInfo = observer(() => { ...@@ -276,20 +276,20 @@ const BaseInfo = observer(() => {
const projectList = await getProjectList(); const projectList = await getProjectList();
currentProjectStore.setProjectList(projectList); currentProjectStore.setProjectList(projectList);
// 项目删完了 // 项目删完了
if (projectList.length === 0) { // if (projectList.length === 0) {
currentProjectStore.changeProject({}); currentProjectStore.changeProject({});
localStorage.setItem("fileServerEndPoint", ""); localStorage.setItem("fileServerEndPoint", "");
setProjectInfo({}); setProjectInfo({});
} else { // } else {
projectList[0].filetoken = getFiletokenAccordingToId(projectList[0].id); // projectList[0].filetoken = getFiletokenAccordingToId(projectList[0].id);
currentProjectStore.changeProject(projectList[0]); // currentProjectStore.changeProject(projectList[0]);
setFileServerEndPointLocalStorage(projectList[0].zoneId); // setFileServerEndPointLocalStorage(projectList[0].zoneId);
getFiletokenAccordingToId(projectList[0].id).then((res) => { // getFiletokenAccordingToId(projectList[0].id).then((res) => {
projectList[0].filetoken = res; // projectList[0].filetoken = res;
currentProjectStore.changeProject(projectList[0]); // currentProjectStore.changeProject(projectList[0]);
}); // });
setProjectInfo(projectList[0]); // setProjectInfo(projectList[0]);
} // }
}, },
}); });
...@@ -429,6 +429,9 @@ const BaseInfo = observer(() => { ...@@ -429,6 +429,9 @@ const BaseInfo = observer(() => {
className={style.updateButton} className={style.updateButton}
onClick={handleClickUpdate} onClick={handleClickUpdate}
loading={updateLoading} loading={updateLoading}
sx={{
height:"32px"
}}
> >
保存修改 保存修改
</LoadingButton> </LoadingButton>
...@@ -459,10 +462,7 @@ const BaseInfo = observer(() => { ...@@ -459,10 +462,7 @@ const BaseInfo = observer(() => {
> >
<div className={style.deleteBox}> <div className={style.deleteBox}>
<div className={style.deleteText1}> <div className={style.deleteText1}>
您要删除本项目。已删除的项目将无法恢复!您真的确定吗? 删除项目会导致数据丢失,已删除的项目将无法恢复。
</div>
<div className={style.deleteText2}>
此操作可能会导致数据丢失。为防止意外操作,我们要求您确认您的操作。
</div> </div>
<div className={style.deleteText3}> <div className={style.deleteText3}>
请输入“ 请输入“
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @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: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-29 11:28:09 * @LastEditTime: 2022-08-11 18:05:06
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSetting/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
*/ */
...@@ -37,15 +37,7 @@ const ChangePermission = observer((props: IProps) => { ...@@ -37,15 +37,7 @@ const ChangePermission = observer((props: IProps) => {
useEffect(() => { useEffect(() => {
if (permissionDialog?.isShow) { if (permissionDialog?.isShow) {
http.get<IResponse<any>>("/cpp/project/listroles").then((res) => { http.get<IResponse<any>>("/cpp/project/listroles").then((res) => {
const arr = []; setSelectOptions(res.data);
const { data } = res;
for (const key in data) {
arr.push({
label: String(data[key]),
value: key,
});
}
setSelectOptions(arr);
}); });
} }
}, [http, permissionDialog]); }, [http, permissionDialog]);
......
...@@ -164,7 +164,7 @@ const ProjectMembers = observer(() => { ...@@ -164,7 +164,7 @@ const ProjectMembers = observer(() => {
variant="contained" variant="contained"
onClick={onAddMember} onClick={onAddMember}
startIcon={<Add />} startIcon={<Add />}
size="small" size="medium"
/> />
) : null} ) : null}
</Box> </Box>
......
...@@ -39,12 +39,9 @@ const ProjectSetting = observer(() => { ...@@ -39,12 +39,9 @@ const ProjectSetting = observer(() => {
if (currentProjectStore.currentProjectInfo.name) { if (currentProjectStore.currentProjectInfo.name) {
return ( return (
<div style={{ padding: 24 }}> <div style={{ padding:'28px 24px 24px' }}>
<div style={{ display: "flex", alignItems: "center" }}> <div style={{ display: "flex", alignItems: "center" }}>
<img src={projectImg} alt="项目logo" /> <span style={{ fontSize: "18px", lineHeight: "26px", fontWeight: "600", color: "#1E2633" }}>项目设置</span>
<span style={{ marginLeft: 12 }}>
{currentProjectStore.currentProjectInfo.name}
</span>
</div> </div>
<Box sx={{ width: "100%", typography: "body1" }}> <Box sx={{ width: "100%", typography: "body1" }}>
<Tabs tabList={tabList} /> <Tabs tabList={tabList} />
......
...@@ -16,17 +16,30 @@ ...@@ -16,17 +16,30 @@
line-height: 20px; line-height: 20px;
color: #565c66; color: #565c66;
} }
.formItemBox {
background: #ffffff;
box-shadow: 0px 3px 10px 0px rgba(0, 24, 57, 0.04);
border-radius: 4px;
border: 1px solid #ebedf0;
margin-bottom: 24px;
}
/* .taskBox {
margin-bottom: 24px;
} */
.backgroundTitle { .backgroundTitle {
background-color: rgba(245, 246, 247, 1); background-color: rgba(245, 246, 247, 1);
height: 48px; height: 48px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
padding: 12px 16px; padding: 12px 24px;
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
} }
.backgroundTitleTextIcon { .backgroundTitleTextIcon {
position: absolute;
top: 16px;
right: 24px;
visibility: hidden; visibility: hidden;
} }
.backgroundTitleTextIconShow { .backgroundTitleTextIconShow {
...@@ -37,7 +50,6 @@ ...@@ -37,7 +50,6 @@
font-weight: 600; font-weight: 600;
line-height: 24px; line-height: 24px;
color: rgba(30, 38, 51, 1); color: rgba(30, 38, 51, 1);
margin-left: 12px;
} }
.taskDescIcon { .taskDescIcon {
margin-left: 8px; margin-left: 8px;
...@@ -46,7 +58,7 @@ ...@@ -46,7 +58,7 @@
margin-left: 8px; margin-left: 8px;
} }
.formItems { .formItems {
padding: 20px 44px 40px 44px; padding: 16px 24px 4px;
} }
.formItem { .formItem {
margin-bottom: 20px; margin-bottom: 20px;
...@@ -69,18 +81,19 @@ ...@@ -69,18 +81,19 @@
} }
.taskConfigBox { .taskConfigBox {
padding: 24px 44px 40px 44px; padding: 16px 24px 4px;
} }
.flowTitle { .flowTitle {
line-height: 16px; line-height: 16px;
margin: 3px 0 27px; margin: 3px 0 27px;
color: rgba(30, 38, 51, 1); color: rgba(19, 112, 255, 1);
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
border-left: 3px solid rgba(19, 112, 255, 1); border-left: 3px solid rgba(19, 112, 255, 1);
padding-left: 3px; padding-left: 3px;
display: flex; display: flex;
align-items: center; align-items: center;
padding-left: 8px;
} }
.parameter { .parameter {
margin-bottom: 20px; margin-bottom: 20px;
......
...@@ -215,18 +215,17 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -215,18 +215,17 @@ const ConfigForm = (props: ConfigFormProps) => {
</div> </div>
<MyTooltip title={parameter.description} placement="right"> <MyTooltip title={parameter.description} placement="right">
<div className={styles.parameterContent}> <div className={styles.parameterContent}>
{parameter.domType.toLowerCase() === "file" && ( {(parameter.domType || "").toLowerCase() === "file" && (
<MyInput <MyInput
onFocus={() => setSelectedBatchNodeId(batchId || "")}
onBlur={() => setSelectedBatchNodeId("")}
value={parameter.value || ""}
InputProps={{
endAdornment: (
<img
onClick={() => { onClick={() => {
setSelectedBatchNodeId(batchId || "");
setFileSelectType("file"); setFileSelectType("file");
handleOpenFileSelect(taskId, parameter.name); handleOpenFileSelect(taskId, parameter.name);
}} }}
value={parameter.value || ""}
InputProps={{
endAdornment: (
<img
src={fileSelectIcon} src={fileSelectIcon}
alt="" alt=""
className={styles.fileSelectImg} className={styles.fileSelectImg}
...@@ -239,18 +238,17 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -239,18 +238,17 @@ const ConfigForm = (props: ConfigFormProps) => {
size="medium" size="medium"
></MyInput> ></MyInput>
)} )}
{parameter.domType.toLowerCase() === "path" && ( {(parameter.domType || "").toLowerCase() === "path" && (
<MyInput <MyInput
onFocus={() => setSelectedBatchNodeId(batchId || "")}
onBlur={() => setSelectedBatchNodeId("")}
value={parameter.value || ""}
InputProps={{
endAdornment: (
<img
onClick={() => { onClick={() => {
setSelectedBatchNodeId(batchId || "");
setFileSelectType("path"); setFileSelectType("path");
handleOpenFileSelect(taskId, parameter.name); handleOpenFileSelect(taskId, parameter.name);
}} }}
value={parameter.value || ""}
InputProps={{
endAdornment: (
<img
src={fileSelectIcon} src={fileSelectIcon}
alt="" alt=""
className={styles.fileSelectImg} className={styles.fileSelectImg}
...@@ -263,18 +261,17 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -263,18 +261,17 @@ const ConfigForm = (props: ConfigFormProps) => {
size="medium" size="medium"
></MyInput> ></MyInput>
)} )}
{parameter.domType.toLowerCase() === "dataset" && ( {(parameter.domType || "").toLowerCase() === "dataset" && (
<MyInput <MyInput
onFocus={() => setSelectedBatchNodeId(taskId)}
onBlur={() => setSelectedBatchNodeId("")}
value={parameter.value || ""}
InputProps={{
endAdornment: (
<img
onClick={() => { onClick={() => {
setSelectedBatchNodeId(taskId);
setFileSelectType("dataset"); setFileSelectType("dataset");
handleOpenFileSelect(taskId, parameter.name); handleOpenFileSelect(taskId, parameter.name);
}} }}
value={parameter.value || ""}
InputProps={{
endAdornment: (
<img
src={fileSelectIcon} src={fileSelectIcon}
alt="" alt=""
className={styles.fileSelectImg} className={styles.fileSelectImg}
...@@ -287,7 +284,7 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -287,7 +284,7 @@ const ConfigForm = (props: ConfigFormProps) => {
size="medium" size="medium"
></MyInput> ></MyInput>
)} )}
{parameter.domType.toLowerCase() === "input" && ( {(parameter.domType || "").toLowerCase() === "input" && (
<MyInput <MyInput
onFocus={() => { onFocus={() => {
setSelectedBatchNodeId(batchId || ""); setSelectedBatchNodeId(batchId || "");
...@@ -304,7 +301,7 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -304,7 +301,7 @@ const ConfigForm = (props: ConfigFormProps) => {
size="medium" size="medium"
></MyInput> ></MyInput>
)} )}
{parameter.domType.toLowerCase() === "select" && ( {(parameter.domType || "").toLowerCase() === "select" && (
<MySelect <MySelect
onFocus={() => setSelectedBatchNodeId(batchId || "")} onFocus={() => setSelectedBatchNodeId(batchId || "")}
onBlur={() => setSelectedBatchNodeId("")} onBlur={() => setSelectedBatchNodeId("")}
...@@ -326,7 +323,8 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -326,7 +323,8 @@ const ConfigForm = (props: ConfigFormProps) => {
fullWidth fullWidth
></MySelect> ></MySelect>
)} )}
{parameter.domType.toLowerCase() === "multipleselect" && ( {(parameter.domType || "").toLowerCase() ===
"multipleselect" && (
<MySelect <MySelect
onFocus={() => setSelectedBatchNodeId(batchId || "")} onFocus={() => setSelectedBatchNodeId(batchId || "")}
onBlur={() => setSelectedBatchNodeId("")} onBlur={() => setSelectedBatchNodeId("")}
...@@ -349,7 +347,7 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -349,7 +347,7 @@ const ConfigForm = (props: ConfigFormProps) => {
fullWidth fullWidth
></MySelect> ></MySelect>
)} )}
{parameter.domType.toLowerCase() === "radio" && ( {(parameter.domType || "").toLowerCase() === "radio" && (
<MyRadio <MyRadio
value={parameter.value} value={parameter.value}
onChange={(e: any) => onChange={(e: any) =>
...@@ -362,7 +360,7 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -362,7 +360,7 @@ const ConfigForm = (props: ConfigFormProps) => {
helperText={parameter.helperText} helperText={parameter.helperText}
></MyRadio> ></MyRadio>
)} )}
{parameter.domType.toLowerCase() === "checkbox" && ( {(parameter.domType || "").toLowerCase() === "checkbox" && (
<MyCheckBox <MyCheckBox
value={parameter.value} value={parameter.value}
onChange={(e: any) => onChange={(e: any) =>
...@@ -398,14 +396,21 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -398,14 +396,21 @@ const ConfigForm = (props: ConfigFormProps) => {
{templateConfigInfo?.description || ""} {templateConfigInfo?.description || ""}
</div> </div>
</div> </div>
<div className={styles.formItemBox}>
<div <div
className={classnames({ className={classnames({
[styles.backgroundTitle]: true, [styles.backgroundTitle]: true,
[styles.backgroundTitlePass]: true,
})} })}
> >
<img src="" alt="" />
<span className={styles.backgroundTitleText}>基础信息</span> <span className={styles.backgroundTitleText}>基础信息</span>
<img
className={classnames({
[styles.backgroundTitleTextIcon]: true,
[styles.backgroundTitleTextIconShow]: !nameHelp.error,
})}
src={jobSueIcon}
alt=""
/>
</div> </div>
<div className={styles.formItems}> <div className={styles.formItems}>
<div className={styles.formItem}> <div className={styles.formItem}>
...@@ -439,13 +444,14 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -439,13 +444,14 @@ const ConfigForm = (props: ConfigFormProps) => {
<div className={styles.formItem}> <div className={styles.formItem}>
<MyInput <MyInput
value={outputPath || ""} value={outputPath || ""}
InputProps={{
endAdornment: (
<img
onClick={() => { onClick={() => {
setSelectedBatchNodeId("");
setFileSelectType("path"); setFileSelectType("path");
handleOpenFileSelect(); handleOpenFileSelect();
}} }}
InputProps={{
endAdornment: (
<img
src={fileSelectIcon} src={fileSelectIcon}
alt="选择输出路径" alt="选择输出路径"
className={styles.fileSelectImg} className={styles.fileSelectImg}
...@@ -458,23 +464,15 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -458,23 +464,15 @@ const ConfigForm = (props: ConfigFormProps) => {
</div> </div>
</div> </div>
</div> </div>
</div>
{renderTasks.map((task, taskIndex) => { {renderTasks.map((task, taskIndex) => {
return ( return (
<div className={styles.taskBox} key={task.id + taskIndex}> <div className={styles.formItemBox} key={task.id + taskIndex}>
<div <div
className={classnames({ className={classnames({
[styles.backgroundTitle]: true, [styles.backgroundTitle]: true,
[styles.backgroundTitlePass]: true,
})} })}
> >
<img
className={classnames({
[styles.backgroundTitleTextIcon]: true,
[styles.backgroundTitleTextIconShow]: task.isCheck,
})}
src={jobSueIcon}
alt=""
/>
<span <span
id={`point${task.id}`} id={`point${task.id}`}
className={styles.backgroundTitleText} className={styles.backgroundTitleText}
...@@ -486,10 +484,25 @@ const ConfigForm = (props: ConfigFormProps) => { ...@@ -486,10 +484,25 @@ const ConfigForm = (props: ConfigFormProps) => {
<img className={styles.taskDescIcon} src={tipsIcon} alt="" /> <img className={styles.taskDescIcon} src={tipsIcon} alt="" />
</MyTooltip> </MyTooltip>
)} )}
<img
className={classnames({
[styles.backgroundTitleTextIcon]: true,
[styles.backgroundTitleTextIconShow]: task.isCheck,
})}
src={jobSueIcon}
alt=""
/>
</div> </div>
<div className={styles.taskConfigBox}> <div className={styles.taskConfigBox}>
{randerParameters(task.parameters, task.id, task.id)} {randerParameters(task.parameters, task.id, task.id)}
{task.flows.map((flow) => { {task.flows.map((flow) => {
if (
flow.parameters.filter(
(parameter) => parameter.hidden === false
).length === 0
) {
return null;
}
return ( return (
<div className={styles.flowConfigBox} key={flow.id}> <div className={styles.flowConfigBox} key={flow.id}>
<div className={styles.flowTitle}> <div className={styles.flowTitle}>
......
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-21 15:25:25 * @Date: 2022-06-21 15:25:25
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-12 14:09:20 * @LastEditTime: 2022-08-09 16:07:33
* @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/WorkFlow/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/WorkFlow/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 { useEffect } from "react";
import Flow from "../../components/Flow"; import Flow from "../../components/Flow";
import { ITemplateConfig } from "../interface"; import { ITemplateConfig } from "../interface";
...@@ -17,6 +18,21 @@ interface IProps { ...@@ -17,6 +18,21 @@ interface IProps {
const WorkFlow = (props: IProps) => { const WorkFlow = (props: IProps) => {
const { templateConfigInfo, setSelectedBatchNodeId, selectedBatchNodeId } = const { templateConfigInfo, setSelectedBatchNodeId, selectedBatchNodeId } =
props; props;
/** 页面刷新提醒 */
const pageRefreshTips = (e: any) => {
const event: any = window.event || e;
event.returnValue = "是否离开网站?";
};
/** 监听页面刷新事件 */
useEffect(() => {
window.addEventListener("beforeunload", pageRefreshTips, false);
return () => {
window.removeEventListener("beforeunload", pageRefreshTips, false);
};
});
return ( return (
<Flow <Flow
tasks={templateConfigInfo?.tasks} tasks={templateConfigInfo?.tasks}
......
...@@ -30,13 +30,14 @@ ...@@ -30,13 +30,14 @@
line-height: 22px; line-height: 22px;
font-size: 14px; font-size: 14px;
color: rgba(30, 38, 51, 1); color: rgba(30, 38, 51, 1);
font-weight: 600; font-weight: 700;
padding-right: 20px; padding-right: 20px;
border-right: 1px solid rgba(235, 237, 240, 1); border-right: 1px solid rgba(209, 214, 222, 1);
} }
.swHeaderLable { .swHeaderLable {
color: rgba(138, 144, 153, 1); color: rgba(138, 144, 153, 1);
font-size: 12px; font-size: 12px;
line-height: 22px;
} }
.swHeaderValue { .swHeaderValue {
color: rgba(30, 38, 51, 1); color: rgba(30, 38, 51, 1);
......
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-21 20:03:56 * @Date: 2022-06-21 20:03:56
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-20 15:00:18 * @LastEditTime: 2022-08-08 16:41:20
* @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/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
*/ */
...@@ -70,16 +70,16 @@ const ProjectSubmitWork = observer(() => { ...@@ -70,16 +70,16 @@ const ProjectSubmitWork = observer(() => {
let value: any = undefined; let value: any = undefined;
if (parameter.defaultValue) { if (parameter.defaultValue) {
if ( if (
parameter.domType.toLowerCase() === "multipleselect" || (parameter.domType || "").toLowerCase() === "multipleselect" ||
parameter.domType.toLowerCase() === "checkbox" (parameter.domType || "").toLowerCase() === "checkbox"
) { ) {
value = parameter.defaultValue.split(","); value = parameter.defaultValue.split(",");
} else { } else {
value = parameter.defaultValue; value = parameter.defaultValue;
} }
} else if ( } else if (
parameter.domType.toLowerCase() === "multipleselect" || (parameter.domType || "").toLowerCase() === "multipleselect" ||
parameter.domType.toLowerCase() === "checkbox" (parameter.domType || "").toLowerCase() === "checkbox"
) { ) {
value = []; value = [];
} else { } else {
...@@ -179,9 +179,9 @@ const ProjectSubmitWork = observer(() => { ...@@ -179,9 +179,9 @@ const ProjectSubmitWork = observer(() => {
value = parameter.value; value = parameter.value;
} }
if ( if (
parameter.domType.toLowerCase() === "path" || (parameter.domType || "").toLowerCase() === "path" ||
parameter.domType.toLowerCase() === "dataset" || (parameter.domType || "").toLowerCase() === "dataset" ||
parameter.domType.toLowerCase() === "file" (parameter.domType || "").toLowerCase() === "file"
) { ) {
value = `/${value}`; value = `/${value}`;
} }
...@@ -240,8 +240,8 @@ const ProjectSubmitWork = observer(() => { ...@@ -240,8 +240,8 @@ const ProjectSubmitWork = observer(() => {
<ArrowBackIosNewIcon <ArrowBackIosNewIcon
sx={{ sx={{
color: "rgba(194, 198, 204, 1)", color: "rgba(194, 198, 204, 1)",
width: "12px", width: "14px",
height: "12px", height: "14px",
}} }}
/> />
</IconButton> </IconButton>
...@@ -258,8 +258,8 @@ const ProjectSubmitWork = observer(() => { ...@@ -258,8 +258,8 @@ const ProjectSubmitWork = observer(() => {
<div className={styles.swTemplateUpdateTimeBox}> <div className={styles.swTemplateUpdateTimeBox}>
<span className={styles.swHeaderLable}>更新时间:</span> <span className={styles.swHeaderLable}>更新时间:</span>
<span className={styles.swHeaderValue}> <span className={styles.swHeaderValue}>
{templateConfigInfo?.updateTime {templateConfigInfo?.updatedTime
? moment(templateConfigInfo?.updateTime).format( ? moment(templateConfigInfo?.updatedTime).format(
"YYYY-MM-DD HH:mm:ss" "YYYY-MM-DD HH:mm:ss"
) )
: "-"} : "-"}
......
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-21 20:03:56 * @Date: 2022-06-21 20:03:56
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-12 11:51:17 * @LastEditTime: 2022-08-08 15:55:45
* @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/interface.ts * @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/interface.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
*/ */
...@@ -56,7 +56,7 @@ export interface ITask { ...@@ -56,7 +56,7 @@ export interface ITask {
export interface ITemplateConfig { // 模板信息 export interface ITemplateConfig { // 模板信息
title: string; // 标题 title: string; // 标题
version: string; // 版本 version: string; // 版本
updateTime?: string; // 更新时间 updatedTime?: string; // 更新时间
description: string; // 模板描述 description: string; // 模板描述
language: string; language: string;
languageVersion: string; languageVersion: string;
......
...@@ -60,9 +60,10 @@ ...@@ -60,9 +60,10 @@
.tabBoxTime { .tabBoxTime {
font-size: 12px; font-size: 12px;
line-height: 20px;
font-weight: 400; font-weight: 400;
color: #565C66; color: #565C66;
margin-left: 4px; margin-left: 8px;
} }
.tabBoxMiddle { .tabBoxMiddle {
...@@ -82,9 +83,10 @@ ...@@ -82,9 +83,10 @@
.tabBoxStatusText { .tabBoxStatusText {
font-size: 12px; font-size: 12px;
line-height: 20px;
font-weight: 400; font-weight: 400;
color: #1E2633; color: #1E2633;
margin: 0px 16px 0px 6px; margin: 0px 16px 0px 8px;
white-space: nowrap; white-space: nowrap;
} }
......
...@@ -79,7 +79,7 @@ const ProjectMembers = observer(() => { ...@@ -79,7 +79,7 @@ const ProjectMembers = observer(() => {
const [size, setSize] = useState(10); const [size, setSize] = useState(10);
const [rowsPerPage, setRowsPerPage] = useState(10); const [rowsPerPage, setRowsPerPage] = useState(10);
const [count, setCount] = useState(0); const [count, setCount] = useState(0);
const [loading,setLoading]=useState(false) const [loading, setLoading] = useState(false)
/** 简单弹窗 */ /** 简单弹窗 */
const [jobData, setJobData] = useState(""); const [jobData, setJobData] = useState("");
const [openDialog, setOpenDialog] = useState(false); const [openDialog, setOpenDialog] = useState(false);
...@@ -209,6 +209,8 @@ const ProjectMembers = observer(() => { ...@@ -209,6 +209,8 @@ const ProjectMembers = observer(() => {
const renderStatusIcon = (data: string) => { const renderStatusIcon = (data: string) => {
switch (data) { switch (data) {
case "SUBMITTED":
return jobRun;
case "RUNNING": case "RUNNING":
return jobRun; return jobRun;
case "ABORTED": case "ABORTED":
...@@ -224,6 +226,8 @@ const ProjectMembers = observer(() => { ...@@ -224,6 +226,8 @@ const ProjectMembers = observer(() => {
const renderStatusText = (data: string) => { const renderStatusText = (data: string) => {
switch (data) { switch (data) {
case "SUBMITTED":
return "正在启动";
case "RUNNING": case "RUNNING":
return "正在运行"; return "正在运行";
case "ABORTED": case "ABORTED":
...@@ -240,6 +244,8 @@ const ProjectMembers = observer(() => { ...@@ -240,6 +244,8 @@ const ProjectMembers = observer(() => {
/** 渲染字体颜色 */ /** 渲染字体颜色 */
const renderTextColor = (data: any) => { const renderTextColor = (data: any) => {
switch (data) { switch (data) {
case "SUBMITTED":
return "#1370FF";
case "RUNNING": case "RUNNING":
return "#1370FF"; return "#1370FF";
case "ABORTED": case "ABORTED":
...@@ -256,6 +262,8 @@ const ProjectMembers = observer(() => { ...@@ -256,6 +262,8 @@ const ProjectMembers = observer(() => {
/** 渲染进度条颜色 */ /** 渲染进度条颜色 */
const renderProgressColor = useCallback((data: any) => { const renderProgressColor = useCallback((data: any) => {
switch (data) { switch (data) {
case "SUBMITTED":
return "info";
case "RUNNING": case "RUNNING":
return "info"; return "info";
case "ABORTED": case "ABORTED":
...@@ -273,7 +281,7 @@ const ProjectMembers = observer(() => { ...@@ -273,7 +281,7 @@ const ProjectMembers = observer(() => {
const rowClick = useCallback( const rowClick = useCallback(
(id: string) => { (id: string) => {
navigate(`/product/cadd/projectJobDetail`, { navigate(`/product/cadd/projectJobDetail`, {
state: { taskId: id }, state: { taskId: id, from: 'workbenchList' },
}); });
}, },
[navigate] [navigate]
...@@ -419,7 +427,7 @@ const ProjectMembers = observer(() => { ...@@ -419,7 +427,7 @@ const ProjectMembers = observer(() => {
/> />
</Box> </Box>
<div <div
style={{ color: renderTextColor(item.state) }} style={{ color: renderTextColor(item.state), margin: '0px' }}
className={styles.tabBoxStatusText} className={styles.tabBoxStatusText}
> >
{item.completeNum + "/" + item.totalNum} {item.completeNum + "/" + item.totalNum}
......
...@@ -2,21 +2,36 @@ ...@@ -2,21 +2,36 @@
flex: 1; flex: 1;
background-color: #fff; background-color: #fff;
border-radius: 16px 0 0 0; border-radius: 16px 0 0 0;
padding: 24px 32px; padding-top: 24px;
box-sizing: border-box; box-sizing: border-box;
}
.headerBox {
padding: 0 32px;
}
.headerBoxShadow {
box-shadow: 0 5px 4px -4px rgb(0, 0, 0, .15);
}
.templateBox {
padding: 0 32px;
height: calc(100vh - 168px);
overflow: auto; overflow: auto;
} }
.templateList { .templateList {
/* height: 2000px; */ /* height: 2000px; */
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
flex-wrap: wrap; flex-wrap: wrap;
} }
.templateLi { .templateLi {
height: 146px; height: 170px;
box-sizing: border-box; box-sizing: border-box;
padding: 16px 20px; padding: 16px 20px;
cursor: pointer; /* cursor: pointer; */
border: 1px solid rgba(235, 237, 240, 1); border: 1px solid rgba(235, 237, 240, 1);
border-radius: 4px; border-radius: 4px;
min-width: 20%; min-width: 20%;
...@@ -24,37 +39,45 @@ ...@@ -24,37 +39,45 @@
margin-right: 16px; margin-right: 16px;
margin-bottom: 16px; margin-bottom: 16px;
} }
.templateLiCustom { .templateLiCustom {
height: 194px; height: 170px;
} }
.templateLiHidden { .templateLiHidden {
visibility: hidden; visibility: hidden;
} }
.addCustomTemplate { .addCustomTemplate {
height: 194px; height: 170px;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
} }
.addCustomTemplateText { .addCustomTemplateText {
margin-top: 12px; margin-top: 12px;
line-height: 22px; line-height: 22px;
font-size: 14px; font-size: 14px;
color: rgba(138, 144, 153, 1); color: rgba(138, 144, 153, 1);
} }
.templateLi:hover { .templateLi:hover {
box-shadow: 6px 8px 22px 0px rgba(0, 24, 57, 0.08); box-shadow: 6px 8px 22px 0px rgba(0, 24, 57, 0.08);
} }
.templateLi:nth-child(4n) { .templateLi:nth-child(4n) {
margin-right: 0; margin-right: 0;
} }
.templateLiTop { .templateLiTop {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.templateTitle { .templateTitle {
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
...@@ -64,14 +87,17 @@ ...@@ -64,14 +87,17 @@
text-overflow: ellipsis; text-overflow: ellipsis;
line-height: 22px; line-height: 22px;
} }
.templateLiInfo { .templateLiInfo {
margin-bottom: 8px; margin-bottom: 8px;
} }
.templateLiInfoText { .templateLiInfoText {
line-height: 20px; line-height: 20px;
font-size: 12px; font-size: 12px;
color: rgba(19, 112, 255, 1); color: rgba(19, 112, 255, 1);
} }
.templateLiDesc { .templateLiDesc {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
...@@ -82,8 +108,9 @@ ...@@ -82,8 +108,9 @@
font-size: 12px; font-size: 12px;
color: rgba(138, 144, 153, 1); color: rgba(138, 144, 153, 1);
} }
.templateLiEditBox { .templateLiEditBox {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
margin-top: 16px; /* margin-top: 16px; */
} }
...@@ -6,6 +6,7 @@ import RadioGroupOfButtonStyle from "@/components/CommonComponents/RadioGroupOfB ...@@ -6,6 +6,7 @@ import RadioGroupOfButtonStyle from "@/components/CommonComponents/RadioGroupOfB
import SearchIcon from "@mui/icons-material/Search"; import SearchIcon from "@mui/icons-material/Search";
import MyButton from "@/components/mui/MyButton"; import MyButton from "@/components/mui/MyButton";
import OutlinedInput from "@mui/material/OutlinedInput"; import OutlinedInput from "@mui/material/OutlinedInput";
import MySwitch from "@/components/mui/MySwitch";
import FullScreenDrawer from "@/components/CommonComponents/FullScreenDrawer"; import FullScreenDrawer from "@/components/CommonComponents/FullScreenDrawer";
import Checkbox from "@mui/material/Checkbox"; import Checkbox from "@mui/material/Checkbox";
import useMyRequest from "@/hooks/useMyRequest"; import useMyRequest from "@/hooks/useMyRequest";
...@@ -17,6 +18,7 @@ import { useMessage } from "@/components/MySnackbar"; ...@@ -17,6 +18,7 @@ import { useMessage } from "@/components/MySnackbar";
import { import {
getAddWorkbenchTemplate, getAddWorkbenchTemplate,
addWorkbenchTemplate, addWorkbenchTemplate,
deleteWorkbenchTemplate,
} from "@/api/workbench_api"; } from "@/api/workbench_api";
type IAddTemplateProps = { type IAddTemplateProps = {
...@@ -50,6 +52,18 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -50,6 +52,18 @@ const AddTemplate = (props: IAddTemplateProps) => {
const [templateType, setTemplateType] = useState("public"); const [templateType, setTemplateType] = useState("public");
// 滚轮是否到顶,判断是否显示阴影
const [isTop, setIsTop] = useState(true)
// 滚动滚轮时监听是否到顶
const onscroll = (e: any) => {
if (e.target.scrollTop <= 0) {
setIsTop(true)
} else {
setIsTop(false)
}
}
const handleRadio = (value: string) => { const handleRadio = (value: string) => {
setTemplateType(value); setTemplateType(value);
}; };
...@@ -68,25 +82,36 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -68,25 +82,36 @@ const AddTemplate = (props: IAddTemplateProps) => {
// 项目管理员-添加工作流模板-提交 // 项目管理员-添加工作流模板-提交
const { run: addTemplate } = useMyRequest(addWorkbenchTemplate, { const { run: addTemplate } = useMyRequest(addWorkbenchTemplate, {
onSuccess: (result: any) => { onSuccess: (result: any) => {
Message.success("添加成功"); // Message.success("添加成功");
setSelectTemplateData([]); // setSelectTemplateData([]);
setShowAddTemplate(); // setShowAddTemplate();
getTemplateInfo({ // getTemplateInfo({
projectId: projectId as string, // projectId: projectId as string,
}); // });
}, },
}); });
const handleAddTemplate = () => { // 删除模板
if (selectTemplateData.length === 0) { const { run: delTemplate } = useMyRequest(deleteWorkbenchTemplate, {
Message.error("请选择要添加的模板"); onSuccess: (result: any) => {
} else { // setOpenDialog(false);
addTemplate({ // getTemplateInfo({
projectId: projectId as string, // projectId: currentProjectStore.currentProjectInfo.id as string,
workflowSpecIds: selectTemplateData, // title: templateName,
// });
},
}); });
}
}; // const handleAddTemplate = () => {
// if (selectTemplateData.length === 0) {
// Message.error("请选择要添加的模板");
// } else {
// addTemplate({
// projectId: projectId as string,
// workflowSpecIds: selectTemplateData,
// });
// }
// };
// 添加工作流模板-获取模板列表 // 添加工作流模板-获取模板列表
const { run: getAddTemplateList } = useMyRequest(getAddWorkbenchTemplate, { const { run: getAddTemplateList } = useMyRequest(getAddWorkbenchTemplate, {
...@@ -147,6 +172,23 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -147,6 +172,23 @@ const AddTemplate = (props: IAddTemplateProps) => {
title, title,
]); ]);
//模板启用切换
const templateSwitch = (e: any, id: string) => {
if (e.target.checked) {
let arr = []
arr.push(id)
addTemplate({
projectId: projectId as string,
workflowSpecIds: arr,
});
} else {
delTemplate({
projectId: projectId as string,
workflowSpecId: id,
});
}
}
useEffect(() => { useEffect(() => {
getAddTemplateListFun(); getAddTemplateListFun();
}, [getAddTemplateListFun]); }, [getAddTemplateListFun]);
...@@ -164,6 +206,12 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -164,6 +206,12 @@ const AddTemplate = (props: IAddTemplateProps) => {
return ( return (
<FullScreenDrawer handleClose={setShowAddTemplate}> <FullScreenDrawer handleClose={setShowAddTemplate}>
<div className={style.content}> <div className={style.content}>
<div
className={classNames({
[style.headerBox]: true,
[style.headerBoxShadow]: !isTop,
})}
>
<Typography <Typography
sx={{ fontSize: "18px", fontWeight: "600", color: "#1E2633" }} sx={{ fontSize: "18px", fontWeight: "600", color: "#1E2633" }}
> >
...@@ -174,7 +222,8 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -174,7 +222,8 @@ const AddTemplate = (props: IAddTemplateProps) => {
display: "flex", display: "flex",
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "center", alignItems: "center",
marginBottom: "20px", paddingBottom: "20px",
paddingTop: "20px",
}} }}
> >
<OutlinedInput <OutlinedInput
...@@ -184,7 +233,7 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -184,7 +233,7 @@ const AddTemplate = (props: IAddTemplateProps) => {
}} }}
placeholder="输入关键词搜索" placeholder="输入关键词搜索"
size="small" size="small"
sx={{ width: 340, height: 32, marginTop: "20px" }} sx={{ width: 340, height: 32 }}
endAdornment={<SearchIcon style={{ color: "#8A9099" }} />} endAdornment={<SearchIcon style={{ color: "#8A9099" }} />}
/> />
<Box <Box
...@@ -199,7 +248,7 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -199,7 +248,7 @@ const AddTemplate = (props: IAddTemplateProps) => {
radioOptions={radioOptions} radioOptions={radioOptions}
handleRadio={handleRadio} handleRadio={handleRadio}
></RadioGroupOfButtonStyle> ></RadioGroupOfButtonStyle>
<MyButton {/* <MyButton
onClick={handleAddTemplate} onClick={handleAddTemplate}
size={"small"} size={"small"}
style={{ style={{
...@@ -211,9 +260,11 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -211,9 +260,11 @@ const AddTemplate = (props: IAddTemplateProps) => {
? "" ? ""
: `(${selectTemplateData.length})`) : `(${selectTemplateData.length})`)
} }
/> /> */}
</Box> </Box>
</Box> </Box>
</div>
<div className={style.templateBox} onScroll={onscroll} >
{templateType === "public" && addTemplateList.length === 0 && ( {templateType === "public" && addTemplateList.length === 0 && (
<Box <Box
sx={{ sx={{
...@@ -261,11 +312,11 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -261,11 +312,11 @@ const AddTemplate = (props: IAddTemplateProps) => {
> >
<div className={style.templateLiTop}> <div className={style.templateLiTop}>
<span className={style.templateTitle}>{item.title}</span> <span className={style.templateTitle}>{item.title}</span>
<Checkbox {/* <Checkbox
size="small" size="small"
sx={{ padding: "0px" }} sx={{ padding: "0px" }}
checked={selectTemplateData.includes(item.id)} checked={selectTemplateData.includes(item.id)}
/> /> */}
</div> </div>
<div className={style.templateLiInfo}> <div className={style.templateLiInfo}>
<span <span
...@@ -279,8 +330,17 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -279,8 +330,17 @@ const AddTemplate = (props: IAddTemplateProps) => {
</span> </span>
</div> </div>
<div className={style.templateLiDesc}>{item.description}</div> <div className={style.templateLiDesc}>{item.description}</div>
{templateType !== "public" && (
<div className={style.templateLiEditBox}> <div className={style.templateLiEditBox}>
<MySwitch
defaultChecked={item.favorited}
onChange={(e: any) =>
templateSwitch(e, item.id)
}
></MySwitch>
</div>
{/* {templateType !== "public" && (
<MyButton <MyButton
onClick={() => handleEditTemplate(item)} onClick={() => handleEditTemplate(item)}
size={"small"} size={"small"}
...@@ -291,7 +351,7 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -291,7 +351,7 @@ const AddTemplate = (props: IAddTemplateProps) => {
text="编辑模板" text="编辑模板"
/> />
</div> </div>
)} )} */}
</div> </div>
); );
})} })}
...@@ -310,6 +370,7 @@ const AddTemplate = (props: IAddTemplateProps) => { ...@@ -310,6 +370,7 @@ const AddTemplate = (props: IAddTemplateProps) => {
})} })}
</div> </div>
</div> </div>
</div>
{customTemplateInfo?.show ? ( {customTemplateInfo?.show ? (
<WorkFlowEdit <WorkFlowEdit
id={customTemplateInfo.id || ""} id={customTemplateInfo.id || ""}
......
...@@ -28,6 +28,7 @@ const TemplateBox = (props: any) => { ...@@ -28,6 +28,7 @@ const TemplateBox = (props: any) => {
); );
return ( return (
<Box className={styles.template}>
<Box className={styles.templateBlock}> <Box className={styles.templateBlock}>
<Box> <Box>
<Box <Box
...@@ -81,7 +82,7 @@ const TemplateBox = (props: any) => { ...@@ -81,7 +82,7 @@ const TemplateBox = (props: any) => {
</Typography> </Typography>
</Box> </Box>
<Typography className={styles.templateDescText}> <Typography className={styles.templateDescText}>
{info.description} {info.description ? info.description : "此模板暂无描述。"}
</Typography> </Typography>
</Box> </Box>
<Box <Box
...@@ -92,8 +93,8 @@ const TemplateBox = (props: any) => { ...@@ -92,8 +93,8 @@ const TemplateBox = (props: any) => {
> >
{isPass("PROJECT_WORKBENCH_FLOES_USE", "MANAGER") && ( {isPass("PROJECT_WORKBENCH_FLOES_USE", "MANAGER") && (
<MyButton <MyButton
size={"small"} size="medium"
text={"删除模版"} text="删除模版"
onClick={() => { onClick={() => {
props.startDialog(info.id); props.startDialog(info.id);
}} }}
...@@ -102,14 +103,15 @@ const TemplateBox = (props: any) => { ...@@ -102,14 +103,15 @@ const TemplateBox = (props: any) => {
)} )}
{isPass("PROJECT_WORKBENCH_FLOES_USE", "USER") && ( {isPass("PROJECT_WORKBENCH_FLOES_USE", "USER") && (
<MyButton <MyButton
size={"small"} size="medium"
text={"使用模版"} text="使用模版"
onClick={() => addTemplateBlock(info.id)} onClick={() => addTemplateBlock(info.id)}
style={{ marginLeft: "12px" }} style={{ marginLeft: "12px" }}
/> />
)} )}
</Box> </Box>
</Box> </Box>
</Box>
); );
}; };
......
...@@ -16,10 +16,15 @@ ...@@ -16,10 +16,15 @@
margin-bottom: 24px; margin-bottom: 24px;
} }
.template {
padding: 0 8px;
box-sizing: border-box;
}
.templateBlock { .templateBlock {
height: 194px; height: 194px;
background: #ffffff; background: #ffffff;
box-shadow: 0px 3px 10px 0px rgba(0,24,57,0.0400); box-shadow: 0px 3px 10px 0px rgba(0, 24, 57, 0.0400);
border-radius: 4px; border-radius: 4px;
border: 1px solid #ebedf0; border: 1px solid #ebedf0;
padding: 16px 20px; padding: 16px 20px;
...@@ -32,32 +37,20 @@ ...@@ -32,32 +37,20 @@
} }
@media screen and (max-width:1220px) { @media screen and (max-width:1220px) {
.templateBlock { .template {
width: 49%; width: 50%;
margin-right: 2%;
}
.templateBlock:nth-child(2n){
margin-right: 0;
} }
} }
@media screen and (min-width:1220px) and (max-width:1600px) { @media screen and (min-width:1220px) and (max-width:1600px) {
.templateBlock { .template {
width: 32.423%; width: 33.33333%;
margin-right: 1.365%;
}
.templateBlock:nth-child(3n){
margin-right: 0;
} }
} }
@media screen and (min-width:1600px) { @media screen and (min-width:1600px) {
.templateBlock { .template {
width: 24%; width: 25%;
margin-right: 1.333%;
}
.templateBlock:nth-child(4n){
margin-right: 0;
} }
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @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: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-26 18:34:46 * @LastEditTime: 2022-08-09 16:32:42
* @FilePath: /bkunyun/src/views/Project/ProjectSetting/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSetting/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
*/ */
...@@ -106,27 +106,23 @@ const ProjectMembers = observer(() => { ...@@ -106,27 +106,23 @@ const ProjectMembers = observer(() => {
if (e.keyCode === 13) { if (e.keyCode === 13) {
setTemplateName(e.target.value); setTemplateName(e.target.value);
} }
} };
return ( return (
<Box className={styles.headerBox}> <Box className={styles.headerBox}>
<Box className={styles.tabBox}> <Box className={styles.tabBox}>
<SearchInput <SearchInput onKeyUp={handleKeyWordChangeKeyUp} sx={{ width: 340 }} />
onKeyUp={handleKeyWordChangeKeyUp} {isPass("PROJECT_WORKBENCH_FLOES_ADD", "MANAGER") && (
sx={{ width: 340 }}
/>
{templateList.length > 0 &&
isPass("PROJECT_WORKBENCH_FLOES_ADD", "MANAGER") && (
<MyButton <MyButton
text={"添加工作流模版"} text={"添加工作流模版"}
img={<Add />} img={<Add />}
onClick={addTemplateBlock} onClick={addTemplateBlock}
size={"small"} size={"medium"}
/> />
)} )}
</Box> </Box>
{templateList.length === 0 && templateName.length > 0 && ( {templateList.length === 0 && (
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
...@@ -140,12 +136,12 @@ const ProjectMembers = observer(() => { ...@@ -140,12 +136,12 @@ const ProjectMembers = observer(() => {
<Typography <Typography
sx={{ fontSize: "12px", fontWeight: "400", color: "#8A9099" }} sx={{ fontSize: "12px", fontWeight: "400", color: "#8A9099" }}
> >
暂未开启模版 {templateName ? "暂未相应搜索结果" : "暂未开启模版"}
</Typography> </Typography>
</Box> </Box>
)} )}
{templateList.length > 0 && ( {templateList.length > 0 && (
<Box sx={{ display: "flex", flexWrap: "wrap" }}> <Box sx={{ display: "flex", flexWrap: "wrap", margin: "0 -8px" }}>
{templateList && {templateList &&
templateList.length > 0 && templateList.length > 0 &&
templateList.map((item, key) => { templateList.map((item, key) => {
...@@ -155,34 +151,16 @@ const ProjectMembers = observer(() => { ...@@ -155,34 +151,16 @@ const ProjectMembers = observer(() => {
})} })}
</Box> </Box>
)} )}
{templateList.length === 0 &&
templateName.length === 0 &&
isPass("PROJECT_WORKBENCH_FLOES_ADD", "MANAGER") && (
<Box className={styles.addNewTemplate} onClick={addTemplateBlock}>
<Add
sx={{
color: "#565C66",
fontSize: "20px",
width: "30px",
height: "30px",
}}
/>
<Typography
sx={{
fontSize: "14px",
fontWeight: "400",
color: "#8A9099",
marginTop: "15px",
}}
>
添加工作流模版
</Typography>
</Box>
)}
{showAddTemplate && ( {showAddTemplate && (
<AddTemplate <AddTemplate
setShowAddTemplate={() => setShowAddTemplate(false)} setShowAddTemplate={() => {
setShowAddTemplate(false);
getTemplateInfo({
projectId: projectIdData as string,
title: templateName,
});
}}
getTemplateInfo={getTemplateInfo} getTemplateInfo={getTemplateInfo}
productId={productId as string} productId={productId as string}
projectId={projectIdData as string} projectId={projectIdData as string}
......
...@@ -214,7 +214,7 @@ const AddProject = (props: IAddProjectProps) => { ...@@ -214,7 +214,7 @@ const AddProject = (props: IAddProjectProps) => {
id="desc" id="desc"
label="项目描述" label="项目描述"
multiline multiline
rows={5} rows={4}
placeholder="请输入项目描述" placeholder="请输入项目描述"
onChange={handleDescChange} onChange={handleDescChange}
helperText={descCheck.help} helperText={descCheck.help}
...@@ -224,7 +224,7 @@ const AddProject = (props: IAddProjectProps) => { ...@@ -224,7 +224,7 @@ const AddProject = (props: IAddProjectProps) => {
position: "absolute", position: "absolute",
bottom: "7px", bottom: "7px",
right: "12px", right: "12px",
color: desc.length >= 100 ? "#d32f2f" : "#C2C6CC" color: desc.length >= 300 ? "#d32f2f" : "#C2C6CC"
}} }}
> >
{desc.length}/300 {desc.length}/300
......
...@@ -21,6 +21,11 @@ const CurrentProject = observer(() => { ...@@ -21,6 +21,11 @@ const CurrentProject = observer(() => {
document.addEventListener("click", (e) => { document.addEventListener("click", (e) => {
setProjectListOpen(false); setProjectListOpen(false);
}); });
return () => {
document.removeEventListener("click", (e) => {
setProjectListOpen(false);
});
};
}, []); }, []);
const handleShowProjectList = (event: React.MouseEvent<HTMLElement>) => { const handleShowProjectList = (event: React.MouseEvent<HTMLElement>) => {
...@@ -85,6 +90,7 @@ const CurrentProject = observer(() => { ...@@ -85,6 +90,7 @@ const CurrentProject = observer(() => {
<Fade {...TransitionProps} timeout={350}> <Fade {...TransitionProps} timeout={350}>
<div> <div>
<ProjectListPopper <ProjectListPopper
setProjectListOpen={setProjectListOpen}
handleClickOpen={openAddProject} handleClickOpen={openAddProject}
handleChangeCurrentProject={handleChangeCurrentProject} handleChangeCurrentProject={handleChangeCurrentProject}
/> />
......
...@@ -39,3 +39,12 @@ ...@@ -39,3 +39,12 @@
.batchRotate { .batchRotate {
transform: translateX(-50%) rotate(-90deg); transform: translateX(-50%) rotate(-90deg);
} }
.handleBox::before{
content: "";
position: absolute;
left: -4px;
top: -4px;
width: 14px;
height: 14px;
}
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-07-12 11:20:29 * @Date: 2022-07-12 11:20:29
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-22 10:43:28 * @LastEditTime: 2022-08-09 11:24:38
* @FilePath: /bkunyun/src/views/Project/components/Flow/components/BatchNode.tsx * @FilePath: /bkunyun/src/views/Project/components/Flow/components/BatchNode.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
*/ */
...@@ -65,13 +65,14 @@ const BatchNode = (props: IBatchNode) => { ...@@ -65,13 +65,14 @@ const BatchNode = (props: IBatchNode) => {
return ( return (
<MyTooltip title={item.name} key={uuid()}> <MyTooltip title={item.name} key={uuid()}>
<Handle <Handle
className={styles.handleBox}
id={item.name} id={item.name}
style={{ style={{
background: "#fff ", background: "#fff ",
border: item.error border: item.error
? "1px solid #FF4E4E" ? "1px solid #FF4E4E"
: "1px solid #D1D6DE", : "1px solid #D1D6DE",
left: index * 20 + 20, left: index * 24 + 20,
}} }}
type="target" type="target"
position={Position.Top} position={Position.Top}
...@@ -95,11 +96,12 @@ const BatchNode = (props: IBatchNode) => { ...@@ -95,11 +96,12 @@ const BatchNode = (props: IBatchNode) => {
return ( return (
<MyTooltip title={item.name} key={uuid()}> <MyTooltip title={item.name} key={uuid()}>
<Handle <Handle
className={styles.handleBox}
id={item.name} id={item.name}
style={{ style={{
background: "#fff ", background: "#fff ",
border: "1px solid #D1D6DE", border: "1px solid #D1D6DE",
left: index * 20 + 20, left: index * 24 + 20,
}} }}
type="source" type="source"
position={Position.Bottom} position={Position.Bottom}
......
// 自定义批算子时使用的流程图
import ReactFlow, {
Controls,
Background,
useNodesState,
useEdgesState,
ReactFlowProps,
Node,
Connection,
Edge,
} from "react-flow-renderer";
import { useCallback, useEffect, useMemo, useState } from "react";
import { uuid } from "@/utils/util";
import { IParameter, ITask } from "../../../../ProjectSubmitWork/interface";
import { ILine } from "../../interface";
import BatchNode from "../BatchNode";
import FlowNode from "../FlowNode";
import { getCustomTemplateParameterCheckResult } from "@/views/WorkFlowEdit/util";
import { useMessage } from "@/components/MySnackbar";
import styles from "./index.module.css";
interface IProps extends ReactFlowProps {
tasks?: ITask[];
/** 类型, edit为编辑类型 */
type?: "edit" | "default";
/** 设置组件数据 组件为编辑状态使用 */
setTasks?: (val: ITask[]) => void;
/** 点击流程node 节点 返回唯一标识符 */
onFlowNodeClick?: (val: string) => void;
/** 监听事件的状态 */
ListenState?: boolean;
/** 流节点是否可以拖拽 */
flowNodeDraggable?: boolean;
// 是否显示Controls(放大缩小全屏等按钮)
showControls?: boolean;
showVersion?: boolean; // 在流程图中是否显示算子版本
}
const BatchOperatorFlow = (props: IProps) => {
const {
tasks,
type: flowType = "default",
setTasks,
onFlowNodeClick,
ListenState = true,
flowNodeDraggable = false,
showControls = true,
showVersion = false,
...other
} = props;
console.log(tasks);
/** 自定义的节点类型 */
const nodeTypes = useMemo(() => {
return { batchNode: BatchNode, flowNode: FlowNode };
}, []);
/** 内部维护的选择的flow节点Id */
const [inSideFlowNodeId, setInSideFlowNodeId] = useState<string>("");
/** 选中的线 */
const [selectedEdge, setSelectedEdge] = useState<Edge>();
const Message = useMessage();
/** 原始数据删除线 */
const tasksDeleteLine = useCallback(
(connection: Connection | Edge | any) => {
const result =
(tasks?.length &&
tasks.map((item) => {
// /** 删除batch起始的edges中的一项 === 等于删除了一根连线 */
if (item.id === connection.source) {
const newEdges =
(item.edges?.length &&
item.edges?.filter((every) => every.id !== connection.id)) ||
[];
return {
...item,
edges: newEdges,
};
} else {
return item;
}
})) ||
[];
return result;
},
[tasks]
);
/** 删除流节点或者线 */
const deleteSelectFlowNode = useCallback(
(e: any) => {
if (e.keyCode === 8 && ListenState) {
/** 删除流节点逻辑 */
if (inSideFlowNodeId) {
const newVal =
(tasks?.length &&
tasks.filter((item) => {
return item.id !== inSideFlowNodeId;
})) ||
[];
// 删除节点时同时删除相关的线
newVal?.forEach((task) => {
task.edges = task.edges.filter(
(edge) => edge.target !== inSideFlowNodeId
);
});
setTasks && setTasks(newVal);
}
if (selectedEdge) {
const newVal = tasksDeleteLine(selectedEdge);
setTasks && setTasks(newVal);
}
}
},
[
inSideFlowNodeId,
ListenState,
tasks,
selectedEdge,
setTasks,
tasksDeleteLine,
]
);
/** 监听鼠标按下事件 */
useEffect(() => {
window.addEventListener("keyup", deleteSelectFlowNode);
return () => {
window.removeEventListener("keyup", deleteSelectFlowNode);
};
}, [deleteSelectFlowNode]);
/** 生成初始化node节点 */
const initialNodes = useMemo(() => {
const val: any = [];
tasks?.length &&
tasks.forEach((item) => {
val.push({
id: item.id,
type: "flowNode",
/** 每一项的数据 */
data: {
info: item,
...{
selectedStatus: inSideFlowNodeId === item.id,
flowNodeStyle: {
backgroundColor: "#fff",
borderRadius: "4px",
},
inStyle: {
backgroundColor: "rgba(19, 112, 255, 1)",
border: "none",
left: 12,
},
outStyle: {
backgroundColor: "rgba(19, 112, 255, 1)",
border: "none",
left: 12,
},
},
/** 样式 */
style: {
padding: "20px",
},
showVersion,
},
/** 坐标 */
position: {
x: Number(item.position?.x) || 0,
y: Number(item.position?.y) || 0,
},
/**
* extent: "parent" 跟随父节点移动
* draggable: false 节点不可移动
*/
draggable: flowNodeDraggable,
});
});
return val;
}, [tasks, inSideFlowNodeId, flowNodeDraggable, showVersion]);
/** 生成初始化的连线节点 */
const initialEdges = useMemo(() => {
const val: ILine[] = [];
tasks?.length &&
tasks.forEach((item) => {
item?.edges?.length &&
item?.edges.forEach((every) => {
const newLine = {
...every,
};
val.push(newLine);
}, []);
});
return val.map((item: ILine) => {
return {
...item,
/** 点击线选中 */
...(selectedEdge?.id === item.id
? {
style: { stroke: "#1370FF", strokeWidth: 2 },
animated: true,
}
: {}),
labelStyle: { fill: "#8A9099" },
labelBgStyle: { fill: "#F7F8FA " },
label: item.label ? `(${item.label})` : "",
};
});
}, [selectedEdge?.id, tasks]);
/** flowNode点击事件 */
const onNodeClick = (e: any, node: Node) => {
tasks?.forEach((item) => {
if (item.id === node.id) {
setInSideFlowNodeId(node.id);
}
});
if (onFlowNodeClick) {
onFlowNodeClick(node.id);
}
/** 点击node统一清除选中的edge */
setSelectedEdge(undefined);
};
// 点击面板、画布
const handlePaneClick = () => {
setInSideFlowNodeId("");
setSelectedEdge(undefined);
};
/** node节点 */
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
/** 连线数组 */
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
useEffect(() => {
setEdges(initialEdges);
}, [initialEdges, setEdges]);
useEffect(() => {
setNodes(initialNodes);
}, [initialNodes, setNodes]);
/** 节点拖动停止 */
const onNodeDragStop = useCallback(
(event: React.MouseEvent, node: Node) => {
const newVal =
(tasks?.length &&
tasks.map((item) => {
if (item.id === node.id) {
return {
...item,
position: node.position,
};
} else {
return item;
}
})) ||
[];
setTasks && setTasks(newVal);
},
[setTasks, tasks]
);
const connectModifyParameters = useCallback(
(parameters: IParameter[], edgeItem: Connection) => {
return parameters.map((item) => {
if (item.name === edgeItem.targetHandle) {
const { error, helperText } = getCustomTemplateParameterCheckResult({
...item,
linked: true,
hidden: true,
});
return { ...item, linked: true, hidden: true, helperText, error };
} else {
return item;
}
});
},
[]
);
/** 获取连接线的端点类型 */
const getClassType = useCallback(
(connection: Connection) => {
let inputClassType = "",
outClassType: string | undefined = undefined;
tasks?.length &&
tasks.forEach((item) => {
if ([connection.source, connection.target].includes(item.id)) {
item.parameters.forEach((every) => {
if (every.name === connection.targetHandle) {
inputClassType = every.classType;
}
if (every.name === connection.sourceHandle) {
outClassType = every.classType;
}
});
}
});
return { inputClassType, outClassType };
},
[tasks]
);
/** 连接校验并修改值 */
const connectCheck = useCallback(
(connection: Connection) => {
const newVal =
(tasks?.length &&
tasks?.map((item) => {
if (item.id === connection.source) {
return {
...item,
edges: [
...item.edges,
{
...connection,
id: uuid(),
},
],
};
} else if (item.id === connection.target) {
return {
...item,
parameters: connectModifyParameters(
item.parameters,
connection
),
};
} else {
return item;
}
})) ||
[];
return newVal;
},
[connectModifyParameters, tasks]
);
/** 已经连接线啦 */
const onConnect = useCallback(
(connection: Connection) => {
const { inputClassType, outClassType } = getClassType(connection);
let result: ITask[] = [];
if (inputClassType === outClassType) {
result = connectCheck(connection) as ITask[];
} else {
Message.error("端口数据类型不一致,无法连接!");
result = tasksDeleteLine(connection);
}
setTasks && setTasks(result);
},
[Message, connectCheck, getClassType, setTasks, tasksDeleteLine]
);
/** 点击连线 */
const onEdgeClick = useCallback((e: any, val: Edge) => {
setSelectedEdge(val);
/** 点击连线清除选中的node ID */
setInSideFlowNodeId("");
}, []);
const reactFlowParams =
flowType === "edit"
? {
onNodesChange,
onEdgesChange,
onNodeDragStop,
onConnect,
onEdgeClick,
}
: {};
return (
<ReactFlow
className={styles.reactFlowBox}
nodes={nodes}
edges={edges}
fitView={flowType === "default" ? true : false}
{...reactFlowParams}
nodeTypes={nodeTypes}
onPaneClick={handlePaneClick}
onNodeClick={onNodeClick}
{...other}
>
{showControls && <Controls />}
<Background color="#aaa" gap={16} />
</ReactFlow>
);
};
export default BatchOperatorFlow;
...@@ -23,3 +23,23 @@ ...@@ -23,3 +23,23 @@
border-radius: 8px; border-radius: 8px;
margin-left: 8px; margin-left: 8px;
} }
.errorDot{
display: inline-block;
line-height: 22px;
vertical-align: middle;
width: 8px;
height: 8px;
background-color: #FF4E4E;
border-radius: 8px;
margin-left: 8px;
}
.handleBox::before{
content: "";
position: absolute;
left: -4px;
top: -4px;
width: 14px;
height: 14px;
}
\ No newline at end of file
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-07-12 11:29:46 * @Date: 2022-07-12 11:29:46
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-12 21:06:48 * @LastEditTime: 2022-08-09 19:06:43
* @FilePath: /bkunyun/src/views/Project/components/Flow/components/FlowNode/index.tsx * @FilePath: /bkunyun/src/views/Project/components/Flow/components/FlowNode/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 classNames from "classnames"; import classNames from "classnames";
import { Handle, Position } from "react-flow-renderer"; import { Handle, Position } from "react-flow-renderer";
import { useMemo } from "react";
// import { IParameter } from "@/views/Project/ProjectSubmitWork/interface";
import { uuid } from "@/utils/util";
import { IExecutionStatus } from "@/views/Project/ProjectSubmitWork/interface"; import { IExecutionStatus } from "@/views/Project/ProjectSubmitWork/interface";
import jobFail from "@/assets/project/jobFail.svg"; import jobFail from "@/assets/project/jobFail.svg";
import jobRun from "@/assets/project/jobRun.svg"; import jobRun from "@/assets/project/jobRun.svg";
import jobSue from "@/assets/project/jobSue.svg"; import jobSue from "@/assets/project/jobSue.svg";
import MyTooltip from "@/components/mui/MyTooltip";
import styles from "./index.module.css"; import styles from "./index.module.css";
/** 自定义flow节点 */ /** 自定义flow节点 */
...@@ -35,25 +39,80 @@ const FlowNode = (props: any) => { ...@@ -35,25 +39,80 @@ const FlowNode = (props: any) => {
const { const {
dotStatus, dotStatus,
selectedStatus, selectedStatus,
info: { title, isCheck, executionStatus }, flowNodeStyle = { display: "flex", alignItems: "center" }, // 样式
inStyle = { background: "#C2C6CC ", left: 12 }, // 样式
outStyle = { background: "#C2C6CC ", left: 12 }, // 样式
flowType,
info: { title, isCheck, executionStatus, parameters, version },
showVersion,
} = data; } = data;
/** 获取输入参数数组 */
const inParamsArr = useMemo(() => {
return (
(parameters?.length &&
parameters?.filter((item: any) => {
return item.parameterGroup === "in";
})) ||
[]
);
}, [parameters]);
/** 获取输出参数数组 */
const outParamsArr = useMemo(() => {
return (
(parameters?.length &&
parameters?.filter((item: any) => {
return item.parameterGroup === "out";
})) ||
[]
);
}, [parameters]);
return ( return (
<div <div
style={flowNodeStyle}
className={classNames({ className={classNames({
[styles.flowNode]: true, [styles.flowNode]: true,
[styles.selectedFlowBox]: selectedStatus, [styles.selectedFlowBox]: selectedStatus,
})} })}
> >
{dotStatus?.isInput ? ( {/* {dotStatus?.isInput ? (
<Handle <Handle
style={{ background: "#C2C6CC ", left: 12 }} className={styles.handleBox}
style={inStyle}
type="target" type="target"
position={Position.Top} position={Position.Top}
/> />
) : null} */}
{inParamsArr?.length
? inParamsArr.map((item: any, index: number) => {
return (
<MyTooltip title={item.name} key={uuid()}>
<Handle
className={styles.handleBox}
id={item.name}
style={{
background: "#fff ",
border: "1px solid #D1D6DE",
...inStyle,
left: index * 24 + 16,
}}
type="target"
position={Position.Top}
/>
</MyTooltip>
);
})
: null}
<div>
{title || ""} {showVersion && version}
{flowType !== "edit" && isCheck ? (
<span className={styles.successDot}></span>
) : null}
{flowType === "edit" && isCheck === false ? (
<span className={styles.errorDot}></span>
) : null} ) : null}
<div style={{ display: "flex", alignItems: "center" }}>
{title || ""}
{isCheck && <span className={styles.successDot}></span>}
{getImgUrl(executionStatus) && ( {getImgUrl(executionStatus) && (
<img <img
style={{ marginLeft: "6px" }} style={{ marginLeft: "6px" }}
...@@ -62,13 +121,34 @@ const FlowNode = (props: any) => { ...@@ -62,13 +121,34 @@ const FlowNode = (props: any) => {
/> />
)} )}
</div> </div>
{dotStatus?.isOutput ? ( {/* {dotStatus?.isOutput ? (
<Handle <Handle
style={{ background: "#C2C6CC ", left: 12 }} className={styles.handleBox}
style={outStyle}
type="source" type="source"
position={Position.Bottom} position={Position.Bottom}
/> />
) : null} ) : null} */}
{outParamsArr?.length
? outParamsArr.map((item: any, index: number) => {
return (
<MyTooltip title={item.name} key={uuid()}>
<Handle
className={styles.handleBox}
id={item.name}
style={{
background: "#fff ",
border: "1px solid #D1D6DE",
...outStyle,
left: index * 24 + 16,
}}
type="source"
position={Position.Bottom}
/>
</MyTooltip>
);
})
: null}
</div> </div>
); );
}; };
......
...@@ -18,7 +18,7 @@ import FlowNode from "./components/FlowNode"; ...@@ -18,7 +18,7 @@ import FlowNode from "./components/FlowNode";
import { getCustomTemplateParameterCheckResult } from "@/views/WorkFlowEdit/util"; import { getCustomTemplateParameterCheckResult } from "@/views/WorkFlowEdit/util";
import { useMessage } from "@/components/MySnackbar"; import { useMessage } from "@/components/MySnackbar";
import styles from './index.module.css' import styles from "./index.module.css";
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
...@@ -61,11 +61,11 @@ const Flow = (props: IProps) => { ...@@ -61,11 +61,11 @@ const Flow = (props: IProps) => {
showControls = true, showControls = true,
...other ...other
} = props; } = props;
console.log(tasks);
/** 自定义的节点类型 */ /** 自定义的节点类型 */
const nodeTypes = useMemo(() => { const nodeTypes = useMemo(() => {
return { batchNode: BatchNode, flowNode: FlowNode }; return { batchNode: BatchNode, flowNode: FlowNode };
}, []); }, []);
console.log(tasks, "11111");
/** 内部维护的选择的batch节点Id */ /** 内部维护的选择的batch节点Id */
const [inSideBatchNodeId, setInSideBatchNodeId] = useState<string>(""); const [inSideBatchNodeId, setInSideBatchNodeId] = useState<string>("");
/** 内部维护的选择的flow节点Id */ /** 内部维护的选择的flow节点Id */
...@@ -76,7 +76,7 @@ const Flow = (props: IProps) => { ...@@ -76,7 +76,7 @@ const Flow = (props: IProps) => {
/** 原始数据删除线 */ /** 原始数据删除线 */
const tasksDeleteLine = useCallback( const tasksDeleteLine = useCallback(
(connection: Connection | Edge) => { (connection: Connection | Edge | any) => {
const result = const result =
(tasks?.length && (tasks?.length &&
tasks.map((item) => { tasks.map((item) => {
...@@ -84,9 +84,7 @@ const Flow = (props: IProps) => { ...@@ -84,9 +84,7 @@ const Flow = (props: IProps) => {
if (item.id === connection.source && item.type === "BATCH") { if (item.id === connection.source && item.type === "BATCH") {
const newEdges = const newEdges =
(item.edges?.length && (item.edges?.length &&
item.edges?.filter( item.edges?.filter((every) => every.id !== connection.id)) ||
(every) => every.sourceHandle !== connection.sourceHandle
)) ||
[]; [];
return { return {
...@@ -145,6 +143,15 @@ const Flow = (props: IProps) => { ...@@ -145,6 +143,15 @@ const Flow = (props: IProps) => {
); );
})) || })) ||
[]; [];
// 删除节点时同时删除相关的线
newVal?.forEach((task) => {
task.edges =
(task?.edges?.length &&
task.edges.filter(
(edge) => edge.target !== inSideBatchNodeId
)) ||
[];
});
setTasks && setTasks(newVal); setTasks && setTasks(newVal);
} }
if (selectedEdge) { if (selectedEdge) {
...@@ -153,7 +160,14 @@ const Flow = (props: IProps) => { ...@@ -153,7 +160,14 @@ const Flow = (props: IProps) => {
} }
} }
}, },
[inSideBatchNodeId, selectedEdge, setTasks, tasks, tasksDeleteLine] [
inSideBatchNodeId,
selectedEdge,
setTasks,
tasks,
tasksDeleteLine,
ListenState,
]
); );
/** 监听鼠标按下事件 */ /** 监听鼠标按下事件 */
...@@ -165,25 +179,25 @@ const Flow = (props: IProps) => { ...@@ -165,25 +179,25 @@ const Flow = (props: IProps) => {
}, [deleteSelectBatchNode]); }, [deleteSelectBatchNode]);
/** 获取是否有输入节点或者是否有输出节点 */ /** 获取是否有输入节点或者是否有输出节点 */
const nodesInputAndOutputStatus = useCallback( // const nodesInputAndOutputStatus = useCallback(
(id: string) => { // (id: string) => {
/** 所有的连线 */ // /** 所有的连线 */
const lineArr: IEdge[] = []; // const lineArr: IEdge[] = [];
tasks?.length && // tasks?.length &&
tasks.forEach((item) => { // tasks.forEach((item) => {
item.edges?.length && lineArr.push(...item.edges); // item.edges?.length && lineArr.push(...item.edges);
}); // });
/** 所有的输入节点ID */ // /** 所有的输入节点ID */
const isInput = lineArr?.some((item) => item.target === id); // const isInput = lineArr?.some((item) => item.target === id);
/** 所有的输出节点ID */ // /** 所有的输出节点ID */
const isOutput = lineArr?.some((item) => item.source === id); // const isOutput = lineArr?.some((item) => item.source === id);
return { // return {
isInput, // isInput,
isOutput, // isOutput,
}; // };
}, // },
[tasks] // [tasks]
); // );
/** 获取是否有流节点 */ /** 获取是否有流节点 */
const isFlowNode = useCallback( const isFlowNode = useCallback(
...@@ -245,10 +259,10 @@ const Flow = (props: IProps) => { ...@@ -245,10 +259,10 @@ const Flow = (props: IProps) => {
/** 每一项的数据 */ /** 每一项的数据 */
data: { data: {
info: item, info: item,
...(item.type === "BATCH"
? {
/** flow组件类型 */ /** flow组件类型 */
flowType, flowType,
...(item.type === "BATCH"
? {
/** 是否有流节点 */ /** 是否有流节点 */
isFlowNode: isFlowNode(item.id), isFlowNode: isFlowNode(item.id),
/** 选中状态 */ /** 选中状态 */
...@@ -260,7 +274,7 @@ const Flow = (props: IProps) => { ...@@ -260,7 +274,7 @@ const Flow = (props: IProps) => {
} }
: { selectedStatus: inSideFlowNodeId === item.id }), : { selectedStatus: inSideFlowNodeId === item.id }),
/** 输入输出圆点状态 */ /** 输入输出圆点状态 */
dotStatus: nodesInputAndOutputStatus(item.id), // dotStatus: nodesInputAndOutputStatus(item.id),
/** 样式 */ /** 样式 */
style: { style: {
...@@ -292,7 +306,7 @@ const Flow = (props: IProps) => { ...@@ -292,7 +306,7 @@ const Flow = (props: IProps) => {
selectedBatchNodeId, selectedBatchNodeId,
inSideBatchNodeId, inSideBatchNodeId,
inSideFlowNodeId, inSideFlowNodeId,
nodesInputAndOutputStatus, // nodesInputAndOutputStatus,
getBatchStyle, getBatchStyle,
]); ]);
...@@ -376,6 +390,7 @@ const Flow = (props: IProps) => { ...@@ -376,6 +390,7 @@ const Flow = (props: IProps) => {
setInSideFlowNodeId(""); setInSideFlowNodeId("");
onBatchClick && onBatchClick(""); onBatchClick && onBatchClick("");
setSelectedEdge(undefined); setSelectedEdge(undefined);
onFlowNodeClick && onFlowNodeClick("");
}; };
/** node节点 */ /** node节点 */
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生 15770852798@163.com * @Author: 吴永生 15770852798@163.com
* @Date: 2022-08-02 11:43:28 * @Date: 2022-08-02 11:43:28
* @LastEditors: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-08-02 19:36:32 * @LastEditTime: 2022-08-11 18:50:31
* @FilePath: /bkunyun/src/views/Project/components/ProjectListPopper/index.tsx * @FilePath: /bkunyun/src/views/Project/components/ProjectListPopper/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
*/ */
...@@ -19,7 +19,8 @@ import { toJS } from "mobx"; ...@@ -19,7 +19,8 @@ import { toJS } from "mobx";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
const ProjectListPopper = observer((props: any) => { const ProjectListPopper = observer((props: any) => {
const { handleChangeCurrentProject, handleClickOpen } = props; const { handleChangeCurrentProject, handleClickOpen, setProjectListOpen } =
props;
const { currentProjectStore } = useStores(); const { currentProjectStore } = useStores();
const projectList = toJS(currentProjectStore.projectList); const projectList = toJS(currentProjectStore.projectList);
const currentProjectId = toJS(currentProjectStore.currentProjectInfo.id); const currentProjectId = toJS(currentProjectStore.currentProjectInfo.id);
...@@ -36,6 +37,7 @@ const ProjectListPopper = observer((props: any) => { ...@@ -36,6 +37,7 @@ const ProjectListPopper = observer((props: any) => {
}, [projectList, name]); }, [projectList, name]);
const handleProjectBox = (e: React.SyntheticEvent) => { const handleProjectBox = (e: React.SyntheticEvent) => {
setProjectListOpen(false);
e.nativeEvent.stopImmediatePropagation(); e.nativeEvent.stopImmediatePropagation();
}; };
......
...@@ -32,6 +32,15 @@ ...@@ -32,6 +32,15 @@
padding: 2px 8px; padding: 2px 8px;
} }
.versionBox {
display: inline-block;
color: #1E2633;
border-radius: 2px;
font-size: 12px;
padding: 2px 8px;
background-color: #F0F2F5;
}
.searchBox { .searchBox {
padding: 0 24px 16px 24px; padding: 0 24px 16px 24px;
} }
......
import { OutlinedInput } from "@mui/material"; import { OutlinedInput } from "@mui/material";
import SearchIcon from "@mui/icons-material/Search"; import SearchIcon from "@mui/icons-material/Search";
import classNames from "classnames"; import classNames from "classnames";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { toJS } from "mobx"; import { toJS } from "mobx";
import cloneDeep from "lodash/cloneDeep"; import cloneDeep from "lodash/cloneDeep";
...@@ -10,10 +10,9 @@ import { IOperatorItemProps, IOperatorListProps } from "./interface"; ...@@ -10,10 +10,9 @@ import { IOperatorItemProps, IOperatorListProps } from "./interface";
import { ITask } from "@/views/Project/ProjectSubmitWork/interface"; import { ITask } from "@/views/Project/ProjectSubmitWork/interface";
import useMyRequest from "@/hooks/useMyRequest"; import useMyRequest from "@/hooks/useMyRequest";
import { IResponse } from "@/api/http"; import { IResponse } from "@/api/http";
import { fetchOperatorList, fetchVersionOperator } from "@/api/workbench_api"; import { fetchOperatorList } from "@/api/workbench_api";
import { useStores } from "@/store"; import { useStores } from "@/store";
import noTemplate from "@/assets/project/noTemplate.svg"; import noTemplate from "@/assets/project/noTemplate.svg";
import MyMenu from "@/components/mui/MyMenu";
import styles from "./index.module.css"; import styles from "./index.module.css";
...@@ -30,15 +29,13 @@ let count = 1; ...@@ -30,15 +29,13 @@ let count = 1;
const OperatorItem = (props: IOperatorItemProps) => { const OperatorItem = (props: IOperatorItemProps) => {
const { const {
info: { title, description, tags, allVersions, version }, info: { title, description, tags, version },
setTemplateConfigInfo, setTemplateConfigInfo,
templateConfigInfo, templateConfigInfo,
setOperatorListData,
operatorListData, operatorListData,
info, info,
} = props; } = props;
const [isDragStyle, setIsDragStyle] = useState<boolean>(false); const [isDragStyle, setIsDragStyle] = useState<boolean>(false);
const [versionValue, setVersionValue] = useState<string>("");
/** 拖拽开始 */ /** 拖拽开始 */
const onDragStart = useCallback(() => { const onDragStart = useCallback(() => {
...@@ -46,27 +43,6 @@ const OperatorItem = (props: IOperatorItemProps) => { ...@@ -46,27 +43,6 @@ const OperatorItem = (props: IOperatorItemProps) => {
count++; count++;
}, []); }, []);
// 获取指定版本的算子
const { run: getVersionOperator } = useMyRequest(fetchVersionOperator, {
onSuccess: (res: IResponse<any>) => {
if (res.data) {
let index: number | undefined = undefined;
const newVal = operatorListData.filter((item, i) => {
const bol = item.id === info.id || item.parentNode === info.id;
if (bol && index === undefined) {
index = i;
}
return !bol;
});
if (index !== undefined) {
newVal.splice(index, 0, ...res.data);
}
setOperatorListData(newVal);
}
},
});
/** 通过id查找相对的批流数组 */ /** 通过id查找相对的批流数组 */
const getBatchFlowArr = useCallback( const getBatchFlowArr = useCallback(
(id: string) => { (id: string) => {
...@@ -99,7 +75,7 @@ const OperatorItem = (props: IOperatorItemProps) => { ...@@ -99,7 +75,7 @@ const OperatorItem = (props: IOperatorItemProps) => {
x: item.type === "BATCH" ? x : item.position?.x, x: item.type === "BATCH" ? x : item.position?.x,
y: item.type === "BATCH" ? y : item.position?.y, y: item.type === "BATCH" ? y : item.position?.y,
}, },
edges: newEdges.length ? newEdges : item?.edges, edges: newEdges?.length ? newEdges : item?.edges,
}; };
}); });
return newVal; return newVal;
...@@ -144,27 +120,6 @@ const OperatorItem = (props: IOperatorItemProps) => { ...@@ -144,27 +120,6 @@ const OperatorItem = (props: IOperatorItemProps) => {
] ]
); );
/** 所有版本信息格式化 */
const getAllVersion = useMemo(() => {
return (
allVersions?.length &&
allVersions?.map((item) => {
return { label: item, value: item };
})
);
}, [allVersions]);
/** 切换版本 */
const changeVersion = useCallback(
(val: string) => {
if (val !== versionValue) {
setVersionValue(val);
getVersionOperator({ title: info.title, version: val });
}
},
[getVersionOperator, info.title, versionValue]
);
return ( return (
<div <div
className={classNames({ className={classNames({
...@@ -192,17 +147,7 @@ const OperatorItem = (props: IOperatorItemProps) => { ...@@ -192,17 +147,7 @@ const OperatorItem = (props: IOperatorItemProps) => {
</span> </span>
); );
})} })}
<MyMenu <span className={styles.versionBox}>{version}</span>
options={getAllVersion || []}
value={versionValue || version || ""}
setValue={(val: string) => {
changeVersion(val);
}}
>
<div className={styles.versionBox}>{`版本:${
versionValue || version
}`}</div>
</MyMenu>
</div> </div>
</div> </div>
); );
...@@ -211,7 +156,8 @@ const OperatorItem = (props: IOperatorItemProps) => { ...@@ -211,7 +156,8 @@ const OperatorItem = (props: IOperatorItemProps) => {
const OperatorList = observer((props: IOperatorListProps) => { const OperatorList = observer((props: IOperatorListProps) => {
const { currentProjectStore } = useStores(); const { currentProjectStore } = useStores();
const productId = toJS(currentProjectStore.currentProductInfo.id); const productId = toJS(currentProjectStore.currentProductInfo.id);
const { templateConfigInfo, setTemplateConfigInfo } = props; const { templateConfigInfo, setTemplateConfigInfo, showCustomOperator } =
props;
const [operatorListData, setOperatorListData] = useState<ITask[]>([]); const [operatorListData, setOperatorListData] = useState<ITask[]>([]);
const [keyword, setKeyword] = useState<string>(""); const [keyword, setKeyword] = useState<string>("");
...@@ -223,22 +169,29 @@ const OperatorList = observer((props: IOperatorListProps) => { ...@@ -223,22 +169,29 @@ const OperatorList = observer((props: IOperatorListProps) => {
}); });
useEffect(() => { useEffect(() => {
if (!showCustomOperator) {
run({ run({
owner: "root", owner: "root",
productId: "cadd" || "", productId: productId as string,
}); });
}, [productId, run]); }
}, [productId, run, showCustomOperator]);
/** 处理回车键 */ /** 处理回车键 */
const handleEnterCode = (e: React.KeyboardEvent<HTMLInputElement>) => { const handleEnterCode = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.keyCode === 13) { if (e.keyCode === 13) {
getOperatorList();
}
};
const getOperatorList = useCallback(() => {
run({ run({
owner: "root", owner: "root",
productId: "cadd" || "", productId: productId as string,
keyword, keyword,
}); });
} }, [run, keyword, productId]);
};
return ( return (
<div className={styles.operatorListBox}> <div className={styles.operatorListBox}>
<div className={styles.searchBox}> <div className={styles.searchBox}>
......
...@@ -19,4 +19,5 @@ export interface IOperatorItemProps { ...@@ -19,4 +19,5 @@ export interface IOperatorItemProps {
export interface IOperatorListProps { export interface IOperatorListProps {
templateConfigInfo: ITask[] templateConfigInfo: ITask[]
setTemplateConfigInfo: (val: ITask[]) => void setTemplateConfigInfo: (val: ITask[]) => void
showCustomOperator: boolean
} }
\ No newline at end of file
...@@ -229,7 +229,7 @@ const SaveCustomTemplate = (props: IProps) => { ...@@ -229,7 +229,7 @@ const SaveCustomTemplate = (props: IProps) => {
required required
error={titleHelper.error} error={titleHelper.error}
helperText={titleHelper.helperText} helperText={titleHelper.helperText}
style={{ margin: "20px 0" }} style={{ margin: "8px 0 20px" }}
disabled={id ? true : false} disabled={id ? true : false}
></MyInput> ></MyInput>
<MyInput <MyInput
...@@ -240,14 +240,27 @@ const SaveCustomTemplate = (props: IProps) => { ...@@ -240,14 +240,27 @@ const SaveCustomTemplate = (props: IProps) => {
helperText={versionHelper.helperText} helperText={versionHelper.helperText}
style={{ marginBottom: "20px" }} style={{ marginBottom: "20px" }}
></MyInput> ></MyInput>
<div style={{ position: "relative" }}>
<MyInput <MyInput
value={description} value={description}
id="desc"
label="模板描述" label="模板描述"
placeholder="模板描述" placeholder="模板描述"
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
multiline multiline
rows={4} rows={4}
></MyInput> />
<span
style={{
position: "absolute",
bottom: "7px",
right: "12px",
color: description.length >= 300 ? "#d32f2f" : "#C2C6CC"
}}
>
{description.length}/300
</span>
</div>
</div> </div>
</MyDialog> </MyDialog>
); );
......
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-21 20:03:56 * @Date: 2022-06-21 20:03:56
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-07-20 15:01:18 * @LastEditTime: 2022-08-09 15:49:59
* @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/index.tsx * @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/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
*/ */
...@@ -101,6 +101,20 @@ const WorkFlowEdit = observer((props: IProps) => { ...@@ -101,6 +101,20 @@ const WorkFlowEdit = observer((props: IProps) => {
} }
}, [id, fetchTemplateConfigInfoRun]); }, [id, fetchTemplateConfigInfoRun]);
/** 页面刷新提醒 */
const pageRefreshTips = (e: any) => {
const event: any = window.event || e;
event.returnValue = "确定回退页面吗?";
};
/** 监听页面刷新事件 */
useEffect(() => {
window.addEventListener("beforeunload", pageRefreshTips, false);
return () => {
window.removeEventListener("beforeunload", pageRefreshTips, false);
};
});
// 确认弹窗相对位置 // 确认弹窗相对位置
const [anchorEl, setAnchorEl] = useState<any>(null); const [anchorEl, setAnchorEl] = useState<any>(null);
// 隐藏确认弹窗, 确认弹窗点击取消 // 隐藏确认弹窗, 确认弹窗点击取消
...@@ -204,6 +218,7 @@ const WorkFlowEdit = observer((props: IProps) => { ...@@ -204,6 +218,7 @@ const WorkFlowEdit = observer((props: IProps) => {
</div> </div>
{leftContentType === "list" && ( {leftContentType === "list" && (
<OperatorList <OperatorList
showCustomOperator={showCustomOperator}
templateConfigInfo={templateConfigInfo} templateConfigInfo={templateConfigInfo}
setTemplateConfigInfo={setTemplateConfigInfo} setTemplateConfigInfo={setTemplateConfigInfo}
/> />
...@@ -228,7 +243,7 @@ const WorkFlowEdit = observer((props: IProps) => { ...@@ -228,7 +243,7 @@ const WorkFlowEdit = observer((props: IProps) => {
setTasks={setTemplateConfigInfo} setTasks={setTemplateConfigInfo}
type="edit" type="edit"
onFlowNodeClick={handleNodeClick} onFlowNodeClick={handleNodeClick}
ListenState={!saveFormDialog} ListenState={!saveFormDialog && !showCustomOperator}
/> />
</div> </div>
</div> </div>
...@@ -237,6 +252,7 @@ const WorkFlowEdit = observer((props: IProps) => { ...@@ -237,6 +252,7 @@ const WorkFlowEdit = observer((props: IProps) => {
anchorEl={anchorEl} anchorEl={anchorEl}
onCancel={handleCancel} onCancel={handleCancel}
onConfirm={handleConfirm} onConfirm={handleConfirm}
placement="bottom-end"
/> />
{saveFormDialog && ( {saveFormDialog && (
<SaveCustomTemplate <SaveCustomTemplate
...@@ -258,6 +274,9 @@ const WorkFlowEdit = observer((props: IProps) => { ...@@ -258,6 +274,9 @@ const WorkFlowEdit = observer((props: IProps) => {
{showCustomOperator && ( {showCustomOperator && (
<CustomOperator <CustomOperator
setShowCustomOperator={() => setShowCustomOperator(false)} setShowCustomOperator={() => setShowCustomOperator(false)}
initOperatorList={JSON.parse(
sessionStorage.getItem("operatorList") || "[]"
)}
/> />
)} )}
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment