Commit ba4c0f70 authored by chenshouchao's avatar chenshouchao

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

cn-Feat 20221012 environment

See merge request !150
parents 73c3aef2 82e2d8ba
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-13 09:56:57 * @Date: 2022-06-13 09:56:57
* @LastEditors: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 21:14:11 * @LastEditTime: 2022-10-24 13:41:26
* @FilePath: /bkunyun/src/api/api_manager.ts * @FilePath: /bkunyun/src/api/api_manager.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -55,6 +55,11 @@ const RESTAPI = { ...@@ -55,6 +55,11 @@ const RESTAPI = {
API_ACTORENV_LIST:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/list`, // 查询用户的应用环境(算子环境) API_ACTORENV_LIST:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/list`, // 查询用户的应用环境(算子环境)
API_ACTORENV_DELETE:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/delete`, // 删除用户算子环境 API_ACTORENV_DELETE:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/delete`, // 删除用户算子环境
API_ACTORENV_DETAIL:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/detail`, // 查询应用环境的详情信息 API_ACTORENV_DETAIL:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/detail`, // 查询应用环境的详情信息
API_ACTOR_ENV_OPTIONS:`${BACKEND_API_URI_PREFIX}/cpp/actorenv/usableenv`, // 查询用户应用环境下拉
API_SAVE_OPERATOR:`${BACKEND_API_URI_PREFIX}/cpp/workflow/custom/actor`, // 保存自定义批流算子
API_WORKFLOWSPEC_LIST:`${BACKEND_API_URI_PREFIX}/cpp/workbench/product/workflowspec/list`, // 模板列表 所有的
API_WORKFLOWSPEC_DELETE:`${BACKEND_API_URI_PREFIX}/cpp/workbench/workflowspec/delete`, // 删除模板
API_WORKFLOWSPEC_DETAIL:`${BACKEND_API_URI_PREFIX}/cpp/workbench/workflowspec`, // 删除模板
}; };
export default RESTAPI; export default RESTAPI;
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生 15770852798@163.com * @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-19 17:09:23 * @Date: 2022-10-19 17:09:23
* @LastEditors: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 21:14:35 * @LastEditTime: 2022-10-24 13:41:53
* @FilePath: /bkunyun/src/api/resourceCenter.ts * @FilePath: /bkunyun/src/api/resourceCenter.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
*/ */
...@@ -55,7 +55,7 @@ const addActorenvBuildenv = (params: addActorenvBuildenvParams) => { ...@@ -55,7 +55,7 @@ const addActorenvBuildenv = (params: addActorenvBuildenvParams) => {
// 获取公共环境 // 获取公共环境
const getActorenvList = (params: { const getActorenvList = (params: {
type: 'BATCH' | 'FLOW' | '', type?: 'BATCH' | 'FLOW' | '',
page: number, page: number,
size: number, size: number,
title?: string, title?: string,
...@@ -76,11 +76,11 @@ const deleteActorenv = (params: {id: string}) => { ...@@ -76,11 +76,11 @@ const deleteActorenv = (params: {id: string}) => {
}; };
// 获取算子列表 // 获取算子列表
const getOperatorList = (data: IOperatorListParams) => { const getOperatorList = (params: IOperatorListParams) => {
return request({ return request({
url: Api.API_OPERATOR_LIST, url: Api.API_OPERATOR_LIST,
method: "get", method: "get",
data params
}); });
} }
...@@ -93,6 +93,55 @@ const getActorenvDetail = (params: {id: string}) => { ...@@ -93,6 +93,55 @@ const getActorenvDetail = (params: {id: string}) => {
}); });
}; };
// 查询应用环境的详情信息
const getActorEnvOptions = (params: {type: string}) => {
return request({
url: `${Api.API_ACTOR_ENV_OPTIONS}`,
method: "get",
params
});
};
// 获取工作流模板
const getWorkflowspecList = (params: {productId?: string, title?: string}) => {
return request({
// url:`${Api.API_WORKFLOWSPEC_LIST}/${params.productId}`,
url:`${Api.API_WORKFLOWSPEC_LIST}`,
method: "get",
params
});
};
// API_WORKFLOWSPEC_LIST
// 新增应用环境
const saveOperator = (params: any) => {
return request({
url: Api.API_SAVE_OPERATOR,
method: "post",
data: params,
});
};
// 删除工作流模板
const deleteWorkflowspec = (params: {
id: string
}) => {
return request({
url:`${Api.API_WORKFLOWSPEC_DELETE}`,
method: "delete",
params
});
};
// 获取工作流模板详情
const getWorkflowspecDetail = (id: string) => {
return request({
url: Api.API_WORKFLOWSPEC_DETAIL + '/' + id,
method: "get",
});
};
export { export {
getPublicEnv, getPublicEnv,
getPublicProject, getPublicProject,
...@@ -101,4 +150,9 @@ export { ...@@ -101,4 +150,9 @@ export {
deleteActorenv, deleteActorenv,
getOperatorList, getOperatorList,
getActorenvDetail, getActorenvDetail,
getActorEnvOptions,
getWorkflowspecList,
saveOperator,
deleteWorkflowspec,
getWorkflowspecDetail
}; };
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 69备份 2</title>
<defs>
<path d="M14.2058659,3.5763545 L8.20586587,0.376450501 C7.76469814,0.141168108 7.23530186,0.141168108 6.79413413,0.376450501 L0.794134134,3.5763545 C0.305329737,3.83704236 1.54202278e-16,4.34591807 0,4.89989271 L0,11.0999285 C-8.68266642e-16,11.6538919 0.305317533,12.1627593 0.794106656,12.423452 L6.79410666,15.623516 C7.23528896,15.858818 7.76471104,15.858818 8.20589334,15.623516 L14.2058933,12.423452 C14.6946825,12.1627593 15,11.6538919 15,11.0999285 L15,4.89989271 C15,4.34591807 14.6946703,3.83704236 14.2058659,3.5763545 Z" id="path-1"></path>
</defs>
<g id="新" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-69备份-2">
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
<g id="编组-66" transform="translate(0.500000, 0.000000)">
<g id="矩形">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<path stroke="#8A9099" stroke-width="1.5" d="M7.14706707,1.0382196 C7.36765093,0.920578407 7.63234907,0.920578407 7.85293293,1.0382196 L7.85293293,1.0382196 L13.8529329,4.2381236 L14.25,11.0999285 L7.85294667,14.9617542 C7.63235552,15.0794052 7.36764448,15.0794052 7.14705333,14.9617542 L7.14705333,14.9617542 L1.14705333,11.7616902 L0.75,4.89989271 Z"></path>
<path d="M7.5,11.25 C9.78208033,11.25 11.543715,11.9030473 12.75,13.2731396 L12.75,13.2731396 L12.75,18.6744339 L2.25,18.6744339 L2.25,13.2731396 C3.45628505,11.9030473 5.21791967,11.25 7.5,11.25 Z" stroke="#8A9099" stroke-width="1.5" mask="url(#mask-2)"></path>
</g>
<circle id="椭圆形" stroke="#8A9099" stroke-width="1.5" cx="7.5" cy="6.8" r="2.25"></circle>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 69备份</title>
<defs>
<path d="M14.2058659,3.5763545 L8.20586587,0.376450501 C7.76469814,0.141168108 7.23530186,0.141168108 6.79413413,0.376450501 L0.794134134,3.5763545 C0.305329737,3.83704236 1.54202278e-16,4.34591807 0,4.89989271 L0,11.0999285 C-8.68266642e-16,11.6538919 0.305317533,12.1627593 0.794106656,12.423452 L6.79410666,15.623516 C7.23528896,15.858818 7.76471104,15.858818 8.20589334,15.623516 L14.2058933,12.423452 C14.6946825,12.1627593 15,11.6538919 15,11.0999285 L15,4.89989271 C15,4.34591807 14.6946703,3.83704236 14.2058659,3.5763545 Z" id="path-1"></path>
</defs>
<g id="新" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-69备份">
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
<g id="编组-66" transform="translate(0.500000, 0.000000)">
<g id="矩形">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<path stroke="#1370FF" stroke-width="1.5" d="M7.14706707,1.0382196 C7.36765093,0.920578407 7.63234907,0.920578407 7.85293293,1.0382196 L7.85293293,1.0382196 L13.8529329,4.2381236 L14.25,11.0999285 L7.85294667,14.9617542 C7.63235552,15.0794052 7.36764448,15.0794052 7.14705333,14.9617542 L7.14705333,14.9617542 L1.14705333,11.7616902 L0.75,4.89989271 Z"></path>
<path d="M7.5,11.25 C9.78208033,11.25 11.543715,11.9030473 12.75,13.2731396 L12.75,13.2731396 L12.75,18.6744339 L2.25,18.6744339 L2.25,13.2731396 C3.45628505,11.9030473 5.21791967,11.25 7.5,11.25 Z" stroke="#1370FF" stroke-width="1.5" mask="url(#mask-2)"></path>
</g>
<circle id="椭圆形" stroke="#1370FF" stroke-width="1.5" cx="7.5" cy="6.8" r="2.25"></circle>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="240px" height="249px" viewBox="0 0 240 249" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>矩形备份 26</title>
<defs>
<rect id="path-1" x="0" y="0" width="240" height="249" rx="8"></rect>
<filter x="-14.5%" y="-17.6%" width="129.1%" height="135.2%" filterUnits="objectBoundingBox" id="filter-3">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="3" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.803607664 0 0 0 0 0.598314046 0 0 0 0 0.12907149 0 0 0 1 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="path-4" x="0" y="0" width="110" height="91" rx="5.5"></rect>
</defs>
<g id="新" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="矩形备份-26">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="蒙版" fill="#FFFFFF" xlink:href="#path-1"></use>
<rect id="矩形备份-6" fill="#FFB919" mask="url(#mask-2)" x="0" y="0" width="240" height="165"></rect>
<g id="编组-8备份-3" mask="url(#mask-2)">
<g transform="translate(65.000000, 37.000000)" id="编组-19">
<g id="矩形" filter="url(#filter-3)">
<mask id="mask-5" fill="white">
<use xlink:href="#path-4"></use>
</mask>
<use id="蒙版" fill="#FFFFFF" xlink:href="#path-4"></use>
<rect fill="#136EFA" mask="url(#mask-5)" x="0" y="0" width="2.5" height="91.4251263"></rect>
<rect fill="#C2C6CC" mask="url(#mask-5)" x="15" y="8.76679293" width="25" height="5.00959596"></rect>
</g>
<g id="编组-7" transform="translate(15.250000, 26.070581)">
<path d="M11.25,5.51515152 L7.125,5.51515152 C5.60621694,5.51515152 4.375,6.74636845 4.375,8.26515152 L4.375,46.8863636 C4.375,48.4051467 5.60621694,49.6363636 7.125,49.6363636 L11.25,49.6363636 L11.25,49.6363636" id="路径-4" stroke="#979797" stroke-width="1.375"></path>
<rect id="矩形备份-10" fill="#F0F2F5" x="0" y="0" width="45" height="11.030303" rx="2.75"></rect>
<rect id="矩形备份-11" fill="#F0F2F5" x="0" y="22.0606061" width="24.75" height="11.030303" rx="2.75"></rect>
<rect id="矩形备份-15" fill="#F0F2F5" x="39.875" y="33.332197" width="24.75" height="11.030303" rx="2.75"></rect>
<rect id="矩形备份-12" fill="#F0F2F5" x="0" y="44.1212121" width="24.75" height="11.030303" rx="2.75"></rect>
<rect id="矩形备份-4" fill="#C2C6CC" x="2.25" y="3.20453283" width="32.5" height="3.75719697"></rect>
<rect id="矩形备份-5" fill="#C2C6CC" x="2.25" y="25.7477146" width="13.75" height="3.75719697"></rect>
<rect id="矩形备份-8" fill="#C2C6CC" x="2.25" y="48.2908965" width="13.75" height="3.75719697"></rect>
<rect id="矩形备份-9" fill="#C2C6CC" x="42.25" y="37.0193056" width="13.75" height="3.75719697"></rect>
<ellipse id="椭圆形备份-3" fill="#0DD09B" cx="39" cy="5.00959596" rx="1.25" ry="1.25239899"></ellipse>
<ellipse id="椭圆形备份-4" fill="#0DD09B" cx="20.25" cy="27.5527778" rx="1.25" ry="1.25239899"></ellipse>
<ellipse id="椭圆形备份-5" fill="#0DD09B" cx="20.25" cy="50.0959596" rx="1.25" ry="1.25239899"></ellipse>
<ellipse id="椭圆形备份-6" fill="#0DD09B" cx="60.25" cy="38.8243687" rx="1.25" ry="1.25239899"></ellipse>
<line x1="4.125" y1="38.7726641" x2="39.875" y2="38.8473485" id="路径-6" stroke="#979797" stroke-width="1.375"></line>
</g>
</g>
</g>
<text id="构建批式算子" mask="url(#mask-2)" font-family="PingFangSC-Medium, PingFang SC" font-size="16" font-weight="400" line-spacing="24" fill="#1E2633">
<tspan x="72" y="198.000567">构建批式算子</tspan>
</text>
<text id="仅支持shell脚本" mask="url(#mask-2)" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="20" fill="#8A9099">
<tspan x="77.484" y="222">仅支持shell脚本</tspan>
</text>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="240px" height="249px" viewBox="0 0 240 249" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 40备份 8</title>
<defs>
<rect id="path-1" x="0" y="0" width="240" height="249" rx="8"></rect>
<rect id="path-3" x="0" y="0" width="96.2033898" height="23.9201798" rx="4.80638562"></rect>
<filter x="-8.8%" y="-27.2%" width="117.7%" height="171.1%" filterUnits="objectBoundingBox" id="filter-5">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.114151225 0 0 0 0 0.59037626 0 0 0 0 0.477660275 0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
<rect id="path-6" x="0" y="0" width="96.2033898" height="23.9201798" rx="4.80638562"></rect>
<filter x="-8.8%" y="-27.2%" width="117.7%" height="171.1%" filterUnits="objectBoundingBox" id="filter-8">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.114151225 0 0 0 0 0.59037626 0 0 0 0 0.477660275 0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
<rect id="path-9" x="0" y="0" width="96.2033898" height="23.9201798" rx="4.80638562"></rect>
<filter x="-8.8%" y="-27.2%" width="117.7%" height="171.1%" filterUnits="objectBoundingBox" id="filter-11">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.114151225 0 0 0 0 0.59037626 0 0 0 0 0.477660275 0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="新" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-40备份-8">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="蒙版" fill="#FFFFFF" xlink:href="#path-1"></use>
<rect id="矩形" fill="#02AB83" mask="url(#mask-2)" x="0" y="0" width="240" height="165"></rect>
<g id="编组-2" mask="url(#mask-2)">
<g transform="translate(56.000000, 40.000000)">
<g id="矩形">
<mask id="mask-4" fill="white">
<use xlink:href="#path-3"></use>
</mask>
<g id="蒙版">
<use fill="black" fill-opacity="1" filter="url(#filter-5)" xlink:href="#path-3"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-3"></use>
</g>
<rect fill="#136EFA" mask="url(#mask-4)" x="0" y="0" width="2.18644068" height="24.0319281"></rect>
<rect fill="#C2C6CC" mask="url(#mask-4)" x="13.1186441" y="7.64652257" width="50.2881356" height="8.73888294"></rect>
<ellipse id="椭圆形备份-3" fill="#0DD09B" mask="url(#mask-4)" cx="77.6186441" cy="12.015964" rx="3.27966102" ry="3.2770811"></ellipse>
</g>
<g id="矩形" transform="translate(32.796610, 30.586090)">
<mask id="mask-7" fill="white">
<use xlink:href="#path-6"></use>
</mask>
<g id="蒙版">
<use fill="black" fill-opacity="1" filter="url(#filter-8)" xlink:href="#path-6"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-6"></use>
</g>
<rect fill="#136EFA" mask="url(#mask-7)" x="0" y="0" width="2.18644068" height="24.0319281"></rect>
<rect fill="#C2C6CC" mask="url(#mask-7)" x="13.1186441" y="7.64652257" width="50.2881356" height="8.73888294"></rect>
<ellipse id="椭圆形备份-3" fill="#0DD09B" mask="url(#mask-7)" cx="77.6186441" cy="12.015964" rx="3.27966102" ry="3.2770811"></ellipse>
</g>
<g id="矩形" transform="translate(4.372881, 60.079820)">
<mask id="mask-10" fill="white">
<use xlink:href="#path-9"></use>
</mask>
<g id="蒙版">
<use fill="black" fill-opacity="1" filter="url(#filter-11)" xlink:href="#path-9"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-9"></use>
</g>
<rect fill="#136EFA" mask="url(#mask-10)" x="0" y="0" width="2.18644068" height="24.0319281"></rect>
<rect fill="#C2C6CC" mask="url(#mask-10)" x="13.1186441" y="7.64652257" width="50.2881356" height="8.73888294"></rect>
<ellipse id="椭圆形备份-3" fill="#0DD09B" mask="url(#mask-10)" cx="77.6186441" cy="12.015964" rx="3.27966102" ry="3.2770811"></ellipse>
</g>
</g>
</g>
<text id="构建流式算子" mask="url(#mask-2)" font-family="PingFangSC-Medium, PingFang SC" font-size="16" font-weight="400" line-spacing="24" fill="#1E2633">
<tspan x="72" y="198">构建流式算子</tspan>
</text>
<text id="仅支持Python脚本" mask="url(#mask-2)" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="20" fill="#8A9099">
<tspan x="70.836" y="222">仅支持Python脚本</tspan>
</text>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 84</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="个人资源-工作流模版" transform="translate(-661.000000, -574.000000)">
<g id="编组-4" transform="translate(641.000000, 526.000000)">
<g id="编组-84" transform="translate(20.000000, 48.000000)">
<rect id="矩形" x="0" y="0" width="24" height="24"></rect>
<g id="编组-83" transform="translate(3.500000, 2.000000)">
<polyline id="路径" stroke="#1370FF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" points="17 8.19811748 17 4.99985 8.5 0 0 4.99985 0 14.9999 2.37372196 16.396235"></polyline>
<circle id="椭圆形" fill="#1370FF" cx="3" cy="17" r="2"></circle>
<circle id="椭圆形备份-13" fill="#1370FF" cx="17" cy="12" r="2"></circle>
<polyline id="路径-72" stroke="#1370FF" stroke-width="1.5" points="0.283430346 5.19688053 8.5 9.92482816 8.5 19.6575672"></polyline>
<line x1="8.5" y1="9.92482816" x2="17" y2="4.99985" id="路径-73" stroke="#1370FF" stroke-width="1.5"></line>
<polyline id="路径" stroke="#1370FF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" points="6.25871064 18.7359651 8.5 20 17 14.9999 17 12.5168189"></polyline>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
import MySelect from "@/components/mui/MySelect";
import MyDialog from "@/components/mui/MyDialog";
import { toJS } from "mobx";
import { useStores } from "@/store";
import { useEffect } from "react";
interface IProductSelectProps {
open: boolean;
setOpen: any;
productId: string;
setProductId: any;
okText?: string;
onConfirm?: any;
}
const ProductSelect = (props: IProductSelectProps) => {
const {
open,
productId,
setProductId,
okText = "下一步",
setOpen,
onConfirm,
} = props;
const { productListStore } = useStores();
useEffect(() => {
if (!productId) {
setProductId(toJS(productListStore.productList)[0].value);
}
}, [productId, productListStore.productList, setProductId]);
const handleChange = (e: any) => {
console.log(e);
setProductId(e);
};
const handleConfirm = () => {
setOpen(false);
onConfirm && onConfirm();
};
return (
<MyDialog
open={open}
title="选择产品"
okText={okText}
onClose={() => setOpen(false)}
onConfirm={() => handleConfirm()}
>
<MySelect
value={productId}
onChange={(e) => handleChange(e)}
options={[...productListStore?.productList] || []}
fullWidth
></MySelect>
</MyDialog>
);
};
export default ProductSelect;
.tableBox {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
}
.itemBox {
/* flex: 1; */
}
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import style from "./index.module.css";
interface ICardTableProps {
data: Array<any>; // 列表数据
renderItem: any; // 单个卡片的渲染函数
itemMinWidth?: number; // 单个卡片的最小宽度,有这个参数时numberOfColumns参数失效,效果为根据屏幕大小和单个卡片的最小宽度来适配每行渲染个数
tableKey?: string; // 表格数据的key
numberOfColumns?: number; // 列数 每行渲染几个
horizontalSpacing?: number; // 水平方向的间隔
verticalSpacing?: number; // 垂直方向的间隔
}
const CardTable = (props: ICardTableProps) => {
const {
data,
renderItem,
tableKey = "id",
numberOfColumns: propsNumberOfColumns = 3,
horizontalSpacing = 20,
verticalSpacing = 20,
itemMinWidth,
} = props;
const [numberOfColumns, setNumberOfColumns] = useState(3);
const getNumberOfColumns = useCallback(() => {
if (itemMinWidth) {
const boxWidth = tableBoxRef?.current?.offsetWidth;
if (boxWidth) {
setNumberOfColumns(Math.floor(boxWidth / itemMinWidth));
} else {
setNumberOfColumns(propsNumberOfColumns);
}
} else {
setNumberOfColumns(propsNumberOfColumns);
}
}, [itemMinWidth, propsNumberOfColumns]);
useEffect(() => {
getNumberOfColumns();
}, [getNumberOfColumns]);
const tableBoxRef: any = useRef(null);
const boxWidth = useMemo(() => {
return `${100 / numberOfColumns}%`;
}, [numberOfColumns]);
window.onresize = () => {
getNumberOfColumns();
};
return (
<div
className={style.tableBox}
style={{
marginLeft: `-${horizontalSpacing / 2}px`,
marginRight: `-${horizontalSpacing / 2}px`,
}}
ref={tableBoxRef}
>
{data.map((item, index) => {
return (
<div
className={style.itemBox}
key={item[tableKey] ? item[tableKey] : index}
style={{
width: boxWidth,
paddingLeft: `${horizontalSpacing / 2}px`,
paddingRight: `${horizontalSpacing / 2}px`,
paddingBottom: `${verticalSpacing}px`,
boxSizing: "border-box",
}}
>
{renderItem(item, index)}
</div>
);
})}
</div>
);
};
export default CardTable;
import CodeMirror from "@uiw/react-codemirror"; /*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-20 19:45:32
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-21 10:19:46
* @FilePath: /bkunyun/src/components/CommonComponents/Code/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import CodeMirror, { ReactCodeMirrorProps } from "@uiw/react-codemirror";
// import { javascript } from "@codemirror/lang-javascript";
type ICodeType = { interface ICodeType extends ReactCodeMirrorProps {
value: string; value: string;
onChange: any; onChange: any;
height?: string; height?: string;
width?: string;
maxWidth?: string;
theme?: "light" | "dark"; theme?: "light" | "dark";
}; }
const Code = (props: ICodeType) => { const Code = (props: ICodeType) => {
const { value, onChange, height, theme = "dark" } = props; const { value, onChange, height, theme = "dark", width, maxWidth } = props;
return ( return (
<CodeMirror <CodeMirror
{...props}
height={height || "100%"} height={height || "100%"}
width={width || "100%"}
maxWidth={maxWidth || "100%"}
value={value} value={value}
onChange={(e) => onChange(e)} onChange={(e) => onChange(e)}
theme={theme} theme={theme}
......
...@@ -44,10 +44,11 @@ const useMyRouter = () => { ...@@ -44,10 +44,11 @@ const useMyRouter = () => {
}); });
} }
for (let item of menuInfo.res.data) { for (let item of menuInfo.res.data) {
let routeHead = `/${item.type}` || '/'
let childrenRoutes: any = [] let childrenRoutes: any = []
for (let route of item.routes) { for (let route of item.routes) {
route.element = elements[route.element] || NotFound; route.element = elements[route.element] || NotFound;
route.path = `/product/${item.id}${route.path}`; route.path = `${routeHead}/${item.id}${route.path}`
if (Array.isArray(route.children)) { if (Array.isArray(route.children)) {
route.children.forEach((childrenItem: any, index: number) => { route.children.forEach((childrenItem: any, index: number) => {
if (childrenItem.path) { if (childrenItem.path) {
...@@ -62,7 +63,6 @@ const useMyRouter = () => { ...@@ -62,7 +63,6 @@ const useMyRouter = () => {
route.children = route.children.filter((childrenItem: any) => !childrenItem.path) route.children = route.children.filter((childrenItem: any) => !childrenItem.path)
} }
} }
// permissionStore.setAddRoutes(item.routes);
permissionStore.setAddRoutes([...childrenRoutes, ...item.routes]); permissionStore.setAddRoutes([...childrenRoutes, ...item.routes]);
} }
menuStore.initMenu(menuInfo.res.data); menuStore.initMenu(menuInfo.res.data);
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
.dynamicFormitem_helpText { .dynamicFormitem_helpText {
position: absolute; position: absolute;
bottom: -36px; bottom: -36px;
font-size: 13px; font-size: 12px;
line-height: 1.15; line-height: 1.15;
color: #9894a5; color: #9894a5;
} }
...@@ -41,6 +41,6 @@ ...@@ -41,6 +41,6 @@
.dynamicFormitem_errorTips { .dynamicFormitem_errorTips {
position: absolute; position: absolute;
bottom: -20px; bottom: -20px;
font-size: 13px; font-size: 12px;
color: #ff4e4e; color: #ff4e4e;
} }
...@@ -77,14 +77,19 @@ const MyMenu = (props: IMyMenuProps) => { ...@@ -77,14 +77,19 @@ const MyMenu = (props: IMyMenuProps) => {
const open = Boolean(anchorEl); const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLDivElement>) => { const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
setAnchorEl(event.currentTarget); setAnchorEl(event.currentTarget);
event.stopPropagation();
}; };
const handleClose = (value: string) => { const handleClose = (value: string) => {
setAnchorEl(null); setAnchorEl(null);
}; };
const handleMenuClick = (value: string) => { const handleMenuClick = (
value: string,
event: React.MouseEvent<HTMLLIElement, MouseEvent>
) => {
setValue && setValue(value); setValue && setValue(value);
setAnchorEl(null); setAnchorEl(null);
event.stopPropagation();
}; };
return ( return (
...@@ -124,7 +129,7 @@ const MyMenu = (props: IMyMenuProps) => { ...@@ -124,7 +129,7 @@ const MyMenu = (props: IMyMenuProps) => {
{options.map((option, index) => { {options.map((option, index) => {
return ( return (
<MenuItem <MenuItem
onClick={() => handleMenuClick(option.value)} onClick={(e) => handleMenuClick(option.value, e)}
selected={value === option.value} selected={value === option.value}
key={index} key={index}
> >
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2021-12-04 15:46:25 * @Date: 2021-12-04 15:46:25
* @LastEditors: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 18:41:04 * @LastEditTime: 2022-10-24 17:39:24
* @FilePath: /lionet-slb-pc/src/components/SearchView/components/Collapse.tsx * @FilePath: /lionet-slb-pc/src/components/SearchView/components/Collapse.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -49,6 +49,8 @@ interface IProps ...@@ -49,6 +49,8 @@ interface IProps
isTitle?: boolean; isTitle?: boolean;
/** 是否显示提示文案 */ /** 是否显示提示文案 */
error?: boolean; error?: boolean;
/** 默认值 */
defaultValue?: string;
/** 提示文案 */ /** 提示文案 */
helpertext?: string; helpertext?: string;
} }
...@@ -64,6 +66,7 @@ export default function MySelect(props: IProps) { ...@@ -64,6 +66,7 @@ export default function MySelect(props: IProps) {
fullWidth, fullWidth,
error = false, error = false,
helpertext, helpertext,
defaultValue,
...other ...other
} = props; } = props;
...@@ -193,7 +196,7 @@ export default function MySelect(props: IProps) { ...@@ -193,7 +196,7 @@ export default function MySelect(props: IProps) {
}, },
}); });
const [insideValue, setInsideValue] = useState<string>(""); const [insideValue, setInsideValue] = useState<string>(defaultValue || "");
const handleChange = (e: SelectChangeEvent<unknown>) => { const handleChange = (e: SelectChangeEvent<unknown>) => {
setInsideValue(e.target.value as string); setInsideValue(e.target.value as string);
......
...@@ -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-10-18 10:54:54 * @LastEditTime: 2022-10-24 20:23:02
* @FilePath: /bkunyun/src/router/index.ts * @FilePath: /bkunyun/src/router/index.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
import { AnyMap } from "immer/dist/internal"; import { AnyMap } from "immer/dist/internal";
...@@ -14,6 +14,7 @@ import * as React from "react"; ...@@ -14,6 +14,7 @@ import * as React from "react";
import NotFound from "@/views/404"; import NotFound from "@/views/404";
import Demo from "@/views/demo"; import Demo from "@/views/demo";
import SeeEnv from "@/views/ResourceCenter/UserResources/UserResourcesEnvironment/SeeEnv"; import SeeEnv from "@/views/ResourceCenter/UserResources/UserResourcesEnvironment/SeeEnv";
import OperatorDetails from "@/views/ResourceCenter/UserResources/WorkflowOperator/components/OperatorDetails";
import ProjectSetting from "@/views/Project/ProjectSetting"; import ProjectSetting from "@/views/Project/ProjectSetting";
import ProjectData from "@/views/Project/ProjectData"; import ProjectData from "@/views/Project/ProjectData";
import ProjectWorkbench from "@/views/Project/ProjectWorkbench"; import ProjectWorkbench from "@/views/Project/ProjectWorkbench";
...@@ -53,9 +54,9 @@ export const elements: { ...@@ -53,9 +54,9 @@ export const elements: {
}) => JSX.Element | any; }) => JSX.Element | any;
} = { } = {
Demo: Demo, Demo: Demo,
SeeTemplate: Demo,
UserResources: UserResources, UserResources: UserResources,
SeeEnv: SeeEnv, SeeEnv: SeeEnv,
OperatorDetails: OperatorDetails,
ProjectSetting: ProjectSetting, ProjectSetting: ProjectSetting,
ProjectData: ProjectData, ProjectData: ProjectData,
ProjectWorkbench: ProjectWorkbench, ProjectWorkbench: ProjectWorkbench,
...@@ -70,6 +71,11 @@ export const routes: Array<route | navigate> = [ ...@@ -70,6 +71,11 @@ export const routes: Array<route | navigate> = [
from: "/", from: "/",
to: "/home", to: "/home",
}, },
{
type: "navigate",
from: "/utility",
to: "/home",
},
{ {
type: "navigate", type: "navigate",
from: "/product", from: "/product",
...@@ -87,6 +93,13 @@ export const routes: Array<route | navigate> = [ ...@@ -87,6 +93,13 @@ export const routes: Array<route | navigate> = [
path: "/home", path: "/home",
element: Home, element: Home,
}, },
{
type: "page",
name: "Utility",
path: "/utility",
element: MenuLayout,
children: [],
},
{ {
type: "page", type: "page",
name: "Product", name: "Product",
......
...@@ -38,7 +38,11 @@ class Permission { ...@@ -38,7 +38,11 @@ class Permission {
if (route.type === "page" && route.children) { if (route.type === "page" && route.children) {
if (route.name === "Product") { if (route.name === "Product") {
for (let item of this.addRoutes) { for (let item of this.addRoutes) {
route.children.push(...item); route.children.push(...item.filter((a) => a.path.indexOf('product/') !== -1));
}
} else if (route.name === "Utility") {
for (let item of this.addRoutes) {
route.children.push(...item.filter((a) => a.path.indexOf('utility/') !== -1));
} }
} else { } else {
this.AddInsertRoutes(route.children); this.AddInsertRoutes(route.children);
......
...@@ -7,22 +7,18 @@ import { useState, useCallback, useEffect, useMemo } from "react"; ...@@ -7,22 +7,18 @@ import { useState, useCallback, useEffect, useMemo } from "react";
import { getOperatorList } from "@/api/project_api"; import { getOperatorList } from "@/api/project_api";
import CircularProgress from "@mui/material/CircularProgress"; import CircularProgress from "@mui/material/CircularProgress";
import useMyRequest from "@/hooks/useMyRequest"; import useMyRequest from "@/hooks/useMyRequest";
import { useStores } from "@/store";
import { toJS } from "mobx";
import { ITask } from "@/views/Project/ProjectSubmitWork/interface"; import { ITask } from "@/views/Project/ProjectSubmitWork/interface";
import { observer } from "mobx-react-lite";
type IProps = { type IProps = {
operatorList: ITask[]; operatorList: ITask[];
setOperatorList: any; setOperatorList: any;
setInputActive: any; setInputActive: any;
productId: string;
}; };
const OperatorList = observer((props: IProps) => { const OperatorList = (props: IProps) => {
const { operatorList, setOperatorList, setInputActive } = props; // 流程图中流算子列表 const { operatorList, setOperatorList, setInputActive, productId } = props; // 流程图中流算子列表
const { currentProjectStore } = useStores();
const [list, setList] = useState<ITask[]>([]); // 算子列表 const [list, setList] = useState<ITask[]>([]); // 算子列表
const productId = toJS(currentProjectStore.currentProductInfo.id); // 产品ID
const [keyword, setKeyword] = useState(""); // 搜索算子列表时的关键词 const [keyword, setKeyword] = useState(""); // 搜索算子列表时的关键词
const [dragItem, setDragItem] = useState<any>({}); // 拖拽的算子 const [dragItem, setDragItem] = useState<any>({}); // 拖拽的算子
const [page, setPage] = useState(0); // 算子列表页码 const [page, setPage] = useState(0); // 算子列表页码
...@@ -250,6 +246,6 @@ const OperatorList = observer((props: IProps) => { ...@@ -250,6 +246,6 @@ const OperatorList = observer((props: IProps) => {
</div> </div>
</div> </div>
); );
}); };
export default OperatorList; export default OperatorList;
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-24 17:32:00
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-24 19:42:23
* @FilePath: /bkunyun/src/views/CustomOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { observer } from "mobx-react-lite"; 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 BatchOperatorFlow from "../Project/components/Flow/components/BatchOperatorFlow";
import SaveOperator from "./components/SaveOperator"; import SaveOperator from "./components/SaveOperator";
import { ITask } from "../Project/ProjectSubmitWork/interface"; import { ITask } from "../Project/ProjectSubmitWork/interface";
import _ from "lodash"; import _ from "lodash";
import styles from "./index.module.css"; import styles from "./index.module.css";
import useCheckOperator from "./useCheckOperator";
type IProps = { type IProps = {
setShowCustomOperator: any; setShowCustomOperator: any;
initOperatorList: ITask[]; initOperatorList: ITask[];
productId: string;
}; };
const CustomOperator = observer((props: IProps) => { const CustomOperator = observer((props: IProps) => {
const { setShowCustomOperator, initOperatorList } = props; const { setShowCustomOperator, initOperatorList, productId } = props;
const Message = useMessage();
const [operatorList, setOperatorList] = useState<ITask[]>(initOperatorList); const [operatorList, setOperatorList] = useState<ITask[]>(initOperatorList);
const [saveFormDialog, setSaveFormDialog] = useState(false); const [saveFormDialog, setSaveFormDialog] = useState(false);
const [inputActive, setInputActive] = useState(false); const [inputActive, setInputActive] = useState(false);
// const [showCustomOperator, setShowCustomOperator] = useState(false); // const [showCustomOperator, setShowCustomOperator] = useState(false);
const { handleCheck } = useCheckOperator(operatorList, () =>
setSaveFormDialog(true)
);
/** 设置选中唯一标识符 */ /** 设置选中唯一标识符 */
const handleNodeClick = useCallback((val: string) => { const handleNodeClick = useCallback((val: string) => {
// setSelectTaskId(val); // setSelectTaskId(val);
}, []); }, []);
// 判断 每个流算子必须至少有一条连接线。
const checkHasOneLine = (sourceArr: string[], targetArr: string[]) => {
const all = _.uniq([...sourceArr, ...targetArr]);
if (all.length === operatorList.length) {
return true;
} else {
return false;
}
// _.uniq([2, 1, 2]);
};
// 判断 每个起始算子(可以有多个起始点)的输入必须为文件的路径输入或数据集的路径输入。
const checkIn = (targetArr: string[]) => {
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[]) => {
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" ||
(outItem.domType || "").toLowerCase() === "input"
);
})
) {
check = false;
}
} else {
// 起点没有输入
check = false;
}
}
});
return check;
};
useEffect(() => { useEffect(() => {
sessionStorage.setItem("operatorList", JSON.stringify(operatorList)); sessionStorage.setItem("operatorList", JSON.stringify(operatorList));
}, [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);
});
});
if (!checkHasOneLine([...sourceArr], [...targetArr])) {
Message.error("部分算子没有流程线,请检查流程!");
return;
}
if (!checkIn([...targetArr])) {
Message.error("每个流程第一步需读取文件/数据集,请检查流程!");
return;
}
if (!checkOut([...sourceArr])) {
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}>
...@@ -168,6 +68,7 @@ const CustomOperator = observer((props: IProps) => { ...@@ -168,6 +68,7 @@ const CustomOperator = observer((props: IProps) => {
operatorList={operatorList} operatorList={operatorList}
setOperatorList={setOperatorList} setOperatorList={setOperatorList}
setInputActive={setInputActive} setInputActive={setInputActive}
productId={productId}
/> />
<BatchOperatorFlow <BatchOperatorFlow
tasks={operatorList} tasks={operatorList}
......
import { useMessage } from "@/components/MySnackbar";
import _ from "lodash";
import { useState } from "react";
import { ITask } from "../Project/ProjectSubmitWork/interface";
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-24 18:08:47
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-24 18:31:12
* @FilePath: /bkunyun/src/views/CustomOperator/useCheckOperator.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
const useCheckOperator = (
operatorList: ITask[],
successCallBack?: any,
nullText?: string
) => {
const Message = useMessage();
const [checkStatus, setCheckStatus] = useState<boolean>(false);
// 判断 每个起始算子(可以有多个起始点)的输入必须为文件的路径输入或数据集的路径输入。
const checkIn = (targetArr: string[]) => {
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[]) => {
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" ||
(outItem.domType || "").toLowerCase() === "input"
);
})
) {
check = false;
}
} else {
// 起点没有输入
check = false;
}
}
});
return check;
};
// 判断 每个流算子必须至少有一条连接线。
const checkHasOneLine = (sourceArr: string[], targetArr: string[]) => {
const all = _.uniq([...sourceArr, ...targetArr]);
if (all.length === operatorList.length) {
return true;
} else {
return false;
}
// _.uniq([2, 1, 2]);
};
const handleCheck = () => {
if (operatorList.length === 0) {
Message.error(nullText || "内容不能为空!");
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);
});
});
if (!checkHasOneLine([...sourceArr], [...targetArr])) {
Message.error("部分算子没有流程线,请检查流程!");
return;
}
if (!checkIn([...targetArr])) {
Message.error("每个流程第一步需读取文件/数据集,请检查流程!");
return;
}
if (!checkOut([...sourceArr])) {
Message.error(
"每个流程最后一步必须将数据写入为文件/数据集,请检查流程!"
);
return;
}
setCheckStatus(true);
successCallBack();
};
return { handleCheck, checkStatus };
};
export default useCheckOperator;
...@@ -100,7 +100,7 @@ const MenuLayout = observer(() => { ...@@ -100,7 +100,7 @@ const MenuLayout = observer(() => {
src={ src={
routerIcon( routerIcon(
item.id || "", item.id || "",
`/v3${item.path}` === pathname pathname.indexOf(`/v3${item.path}`) !== -1
) || undefined ) || undefined
} }
alt="" alt=""
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
} }
.form { .form {
width: 368px; width: 368px;
min-width: 368px;
box-sizing: border-box; box-sizing: border-box;
padding: 16px 24px; padding: 16px 24px;
border: 1px solid #ebedf0; border: 1px solid #ebedf0;
......
...@@ -33,23 +33,23 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -33,23 +33,23 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
const { setAddopen } = props; const { setAddopen } = props;
const Message = useMessage(); const Message = useMessage();
let tokenInfo = getTokenInfo(); let tokenInfo = getTokenInfo();
const [hpczoneList, setHpczoneList] = useState<Array<any>>([]); const [hpczoneList, setHpczoneList] = useState<Array<any>>([]); // 计算区列表 通过计算区列表和计算区id拿fileServerEndPoint
const [publicProjectId, setPublicProjectId] = useState(""); const [publicProjectId, setPublicProjectId] = useState(""); // 公共项目id 环境压缩包临时存放在公共项目下
const [publicZoneId, setPublicZoneId] = useState(""); const [publicZoneId, setPublicZoneId] = useState(""); // 公共项目计算区id
const [computeType, setComputeType] = useState(""); const [computeType, setComputeType] = useState(""); // 基础环境是cpu 还是 gpu
const [fileToken, setFileToken] = useState(""); const [fileToken, setFileToken] = useState(""); // 访问公共项目的fileToken
const [taskType, setTaskType] = useState<"BATCH" | "FLOW">("BATCH"); const [taskType, setTaskType] = useState<"BATCH" | "FLOW">("BATCH"); // 环境类型 批式环境还是流式环境
const [name, setName] = useState(""); const [name, setName] = useState(""); // 环境名称
const [desc, setDesc] = useState(""); const [desc, setDesc] = useState(""); // 环境描述
const [baseEnvId, setBaseEnvId] = useState(""); const [baseEnvId, setBaseEnvId] = useState(""); // 基础环境id
const [filePaths, setFilePaths] = useState<Array<string>>([]); const [filePaths, setFilePaths] = useState<Array<string>>([]); // 环境压缩包上传后的路径集合(目前只有一个)
const [isUploading, setIsUploading] = useState(false); const [isUploading, setIsUploading] = useState(false); // 保存接口loading
const [fileName, setFileName] = useState(""); const [fileName, setFileName] = useState(""); // 环境压缩包文件名
// const [bashScript, setBashScript] = useState(''); const [envList, setEnvList] = useState<Array<any>>([]); // 基础环境列表 和taskType环境类型有关
const [envList, setEnvList] = useState<Array<any>>([]); const [progress, setProgress] = useState("0%"); // 上传压缩包进度
const [progress, setProgress] = useState("0%"); const [code, setCode] = useState(""); // 脚本内容
const [code, setCode] = useState(""); const [codeWidth, setCodeWidth] = useState(0);
const [upload, setUpload] = useState<any>(() => {}); const [upload, setUpload] = useState<any>(() => {}); // 上传压缩包实例 用于暂停(取消上传)upload.abort(true);
const [nameHelper, setNameHelper] = useState({ const [nameHelper, setNameHelper] = useState({
error: false, error: false,
text: "30字符以内,仅限字母、数字、中文", text: "30字符以内,仅限字母、数字、中文",
...@@ -298,6 +298,16 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -298,6 +298,16 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
setFilePaths([]); setFilePaths([]);
}; };
const getCodeWidth = () => {
const addEnvironmentCodeElement =
document.getElementById("addEnvironmentCode");
setCodeWidth(addEnvironmentCodeElement?.offsetWidth || 0);
};
useEffect(() => {
getCodeWidth();
}, []);
return ( return (
<div className={style.addEnvironment}> <div className={style.addEnvironment}>
<div className={style.left}> <div className={style.left}>
...@@ -370,7 +380,16 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -370,7 +380,16 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
<div className={style.label}> <div className={style.label}>
上传环境压缩包<span className={style.zipText}>(.zip)</span> 上传环境压缩包<span className={style.zipText}>(.zip)</span>
<span className={style.required}>*</span> <span className={style.required}>*</span>
<span className={style.download}>下载模板</span> <span
className={style.download}
onClick={() =>
window.open(
"https://projectsif.oss-cn-beijing.aliyuncs.com/build_env_template.zip"
)
}
>
下载模板
</span>
</div> </div>
<div className={style.formItem}> <div className={style.formItem}>
{filePaths.length === 0 && !isUploading && ( {filePaths.length === 0 && !isUploading && (
...@@ -445,11 +464,12 @@ const AddEnvironment = (props: IAddEnvironmentProps) => { ...@@ -445,11 +464,12 @@ const AddEnvironment = (props: IAddEnvironmentProps) => {
<div className={style.codeTitle}> <div className={style.codeTitle}>
{taskType === "BATCH" ? "Shell脚本" : "Python脚本"} {taskType === "BATCH" ? "Shell脚本" : "Python脚本"}
</div> </div>
<div className={style.code}> <div className={style.code} id="addEnvironmentCode">
<Code <Code
value={code} value={code}
onChange={(e: string) => setCode(e)} onChange={(e: string) => setCode(e)}
height="535px" height="535px"
width={`${codeWidth}px`}
/> />
</div> </div>
</div> </div>
......
.seeEnv {
position: relative;
}
.titleBox { .titleBox {
padding: 14px 24px; padding: 14px 24px;
display: flex; display: flex;
...@@ -8,10 +11,6 @@ ...@@ -8,10 +11,6 @@
.goBackIcon { .goBackIcon {
width: 22px; width: 22px;
height: 22px; height: 22px;
/* background-color: #fff; */
/* border-radius: 4px; */
/* margin-right: 12px; */
/* box-shadow: 0px 2px 5px 0px rgba(3, 47, 105, 0.09); */
cursor: pointer; cursor: pointer;
} }
.title { .title {
......
import style from "./index.module.css"; import style from "./index.module.css";
import React, { useState, useEffect, useMemo } from "react"; import React, { useState, useEffect } from "react";
import LogView from "@/views/ResourceCenter/components/LogView"; import LogView from "@/views/ResourceCenter/components/LogView";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import BasicInfo, { import BasicInfo, {
...@@ -9,6 +9,7 @@ import { getActorenvDetail } from "@/api/resourceCenter"; ...@@ -9,6 +9,7 @@ import { getActorenvDetail } from "@/api/resourceCenter";
import useMyRequest from "@/hooks/useMyRequest"; import useMyRequest from "@/hooks/useMyRequest";
import goback from "@/assets/project/goback.svg"; import goback from "@/assets/project/goback.svg";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import MyCircularProgress from "@/components/mui/MyCircularProgress";
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";
...@@ -18,8 +19,34 @@ const SeeEnv = () => { ...@@ -18,8 +19,34 @@ const SeeEnv = () => {
const location = useLocation(); const location = useLocation();
const [id, setId] = useState(""); const [id, setId] = useState("");
const [info, setInfo] = useState<any>({}); const [info, setInfo] = useState<any>({});
const [infoListTop, setInfoListTop] = useState<Array<IInfoItem>>([]); const [infoListTop, setInfoListTop] = useState<Array<IInfoItem>>([
const [infoListBot, setInfoListBot] = useState<Array<IInfoItem>>([]); {
label: "构建状态",
value: "",
},
{
label: "创建时间",
value: "",
},
{
label: "构建时长",
value: "",
},
{
label: "构建成本",
value: "",
},
{
label: "基础环境",
value: "",
},
]);
const [infoListBot, setInfoListBot] = useState<Array<IInfoItem>>([
{
label: "描述",
value: "",
},
]);
const [logs, setLogs] = useState<Array<any>>([]); const [logs, setLogs] = useState<Array<any>>([]);
const getStatus = (item: any) => { const getStatus = (item: any) => {
...@@ -58,7 +85,6 @@ const SeeEnv = () => { ...@@ -58,7 +85,6 @@ const SeeEnv = () => {
const { run, loading } = useMyRequest(getActorenvDetail, { const { run, loading } = useMyRequest(getActorenvDetail, {
onSuccess: (res) => { onSuccess: (res) => {
console.log(res);
setInfo(res.data); setInfo(res.data);
let data = res.data; let data = res.data;
setInfoListTop([ setInfoListTop([
...@@ -112,12 +138,13 @@ const SeeEnv = () => { ...@@ -112,12 +138,13 @@ const SeeEnv = () => {
return ( return (
<div className={style.seeEnv}> <div className={style.seeEnv}>
<MyCircularProgress loading={loading} />
<div className={style.titleBox}> <div className={style.titleBox}>
<img <img
className={style.goBackIcon} className={style.goBackIcon}
src={goback} src={goback}
alt="" alt=""
onClick={() => navigate("/product/resourceCenter/userResources")} onClick={() => navigate("/utility/resourceCenter/userResources")}
/> />
<div className={style.title}>{info.title}</div> <div className={style.title}>{info.title}</div>
<div className={style.type}> <div className={style.type}>
...@@ -134,7 +161,7 @@ const SeeEnv = () => { ...@@ -134,7 +161,7 @@ const SeeEnv = () => {
<div className={style.logsBox}> <div className={style.logsBox}>
<div className={style.logsTitle}>日志</div> <div className={style.logsTitle}>日志</div>
<div className={style.LogViewBox}> <div className={style.LogViewBox}>
<LogView logs={logs}></LogView> {!loading && <LogView logs={logs}></LogView>}
</div> </div>
</div> </div>
</div> </div>
......
...@@ -20,7 +20,7 @@ const UserResourcesEnvironment = () => { ...@@ -20,7 +20,7 @@ const UserResourcesEnvironment = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [addOpen, setAddopen] = useState(false); const [addOpen, setAddopen] = useState(false);
const [title, setTitle] = useState(""); const [title, setTitle] = useState("");
const [type, setType] = useState<"BATCH" | "FLOW" | "">(""); const [type, setType] = useState<"BATCH" | "FLOW" | "ALL">("ALL");
const [list, setList] = useState([]); const [list, setList] = useState([]);
const [page, setPage] = useState(0); const [page, setPage] = useState(0);
const [count, setCount] = useState(0); const [count, setCount] = useState(0);
...@@ -54,7 +54,7 @@ const UserResourcesEnvironment = () => { ...@@ -54,7 +54,7 @@ const UserResourcesEnvironment = () => {
width: 140, width: 140,
}, },
]; ];
const { run: getList } = useMyRequest(getActorenvList, { const { run: getList, loading } = useMyRequest(getActorenvList, {
onSuccess: (res) => { onSuccess: (res) => {
setList(res.data.content); setList(res.data.content);
setCount(res.data.totalPages - 1); setCount(res.data.totalPages - 1);
...@@ -72,7 +72,7 @@ const UserResourcesEnvironment = () => { ...@@ -72,7 +72,7 @@ const UserResourcesEnvironment = () => {
page, page,
size, size,
title, title,
type, type: type === "ALL" ? "" : type,
}); });
} }
}, [getList, page, size, title, type, addOpen, deleteOpen]); }, [getList, page, size, title, type, addOpen, deleteOpen]);
...@@ -131,9 +131,7 @@ const UserResourcesEnvironment = () => { ...@@ -131,9 +131,7 @@ const UserResourcesEnvironment = () => {
}; };
const hanleToSeeEnv = (item: any) => { const hanleToSeeEnv = (item: any) => {
console.log("hanleToSeeEnv"); navigate("/utility/resourceCenter/userResources/seeEnv", {
console.log(item.id);
navigate("/product/resourceCenter/userResources/seeEnv", {
state: { id: item.id }, state: { id: item.id },
}); });
}; };
...@@ -207,6 +205,10 @@ const UserResourcesEnvironment = () => { ...@@ -207,6 +205,10 @@ const UserResourcesEnvironment = () => {
title="环境类型" title="环境类型"
isTitle={true} isTitle={true}
options={[ options={[
{
label: "全部",
value: "ALL",
},
{ {
label: "批式", label: "批式",
value: "BATCH", value: "BATCH",
...@@ -253,6 +255,7 @@ const UserResourcesEnvironment = () => { ...@@ -253,6 +255,7 @@ const UserResourcesEnvironment = () => {
nodataText="暂无应用环境" nodataText="暂无应用环境"
paginationType="complex" paginationType="complex"
totalElements={totalElements} totalElements={totalElements}
loading={loading}
></MyTable> ></MyTable>
</div> </div>
{addOpen && <AddEnvironment setAddopen={setAddopen}></AddEnvironment>} {addOpen && <AddEnvironment setAddopen={setAddopen}></AddEnvironment>}
......
.swBox {
position: fixed;
z-index: 1000;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: RGBA(247, 248, 250, 1);
overflow-y: overlay;
}
.swHeader {
z-index: 1001;
position: sticky;
top: 0;
height: 56px;
background-color: #fff;
box-shadow: 0px 3px 10px 0px rgba(0, 24, 57, 0.06);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
}
.swHeaderLeft {
display: flex;
justify-content: flex-start;
align-items: center;
}
.goBackIcon {
cursor: pointer;
}
.goBackIconBox {
width: 32px;
height: 32px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
.goBackIconBox:hover {
background-color: rgb(240, 242, 245);
}
.swTemplateTitle {
margin: 0 19px 0 3px;
line-height: 20px;
font-size: 14px;
color: rgba(30, 38, 51, 1);
font-weight: 700;
}
.swContent {
display: flex;
height: calc(100vh - 56px);
}
.swFormBox {
background-color: #fff;
border-right: 1px solid #ebedf0;
width: 360px;
overflow-y: overlay;
box-sizing: border-box;
padding: 24px;
}
.swFlowBox {
flex: 1;
height: calc(100vh - 56px);
}
.title {
color: rgba(30, 38, 51, 1);
font-size: 16px;
line-height: 24px;
font-weight: 600;
margin-bottom: 16px;
}
.taskInfoLi {
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.taskInfoParams {
color: rgba(138, 144, 153, 1);
font-size: 14px;
line-height: 22px;
width: 80px;
margin-right: 44px;
word-wrap: break-word;
}
.taskInfoValue {
color: rgba(30, 38, 51, 1);
font-size: 14px;
line-height: 22px;
display: flex;
position: relative;
align-items: center;
text-align: left;
word-break: break-all;
flex: 1;
justify-content: flex-end;
word-wrap: break-word;
}
.taskInfoValueShowAll {
white-space: normal;
}
.taskInfoValueIcon {
margin-right: 9px;
}
.tabs {
display: flex;
justify-content: flex-start;
border-bottom: 1px solid rgba(240, 242, 245, 1);
}
.tabLi {
cursor: pointer;
font-size: 14px;
line-height: 22px;
padding-bottom: 8px;
color: rgba(138, 144, 153, 1);
margin-right: 32px;
position: relative;
}
.tabLiAcitve {
color: rgba(19, 112, 255, 1);
border-bottom: 2px solid rgba(19, 112, 255, 1);
}
.overview {
padding-top: 19px;
}
.params {
padding-top: 19px;
}
.options {
position: absolute;
top: 33px;
max-height: 230px;
overflow-y: overlay;
padding: 8px 0px;
background: #ffffff;
box-shadow: 0px 3px 10px 0px rgba(0, 24, 57, 0.14);
border-radius: 4px;
z-index: 1002;
}
import { useEffect, useState, useMemo } from "react";
import { observer } from "mobx-react-lite";
import classNames from "classnames";
import useMyRequest from "@/hooks/useMyRequest";
import { getWorkflowspecDetail } from "@/api/resourceCenter";
import { IResponse } from "@/api/http";
import goback from "@/assets/project/goback.svg";
import { ITaskInfo } from "@/views/Project/ProjectSubmitWork/interface";
import Flow from "@/views/Project/components/Flow";
import moment from "moment";
import MyTooltip from "@/components/mui/MyTooltip";
import { toJS } from "mobx";
import { useStores } from "@/store";
import styles from "./index.module.css";
interface ITemplateDetailProps {
id: string;
setShowDetail: any;
}
const TemplateDetail = observer((props: ITemplateDetailProps) => {
const { id, setShowDetail } = props;
const [templateInfo, setTemplateInfo] = useState<any>({});
const [overviewActive, setOverviewActive] = useState(true);
/** 选中的node Id */
const [activeFlowIndex, setActiveFlowIndex] = useState<string>("");
const { productListStore } = useStores();
const getProductName = (productId: string) => {
let res = "-";
toJS(productListStore.productList).forEach((item) => {
if (item.value === productId) {
res = item.label;
}
});
return res;
};
const taskInfo = useMemo(() => {
if (activeFlowIndex) {
return templateInfo.tasks.filter(
(task: any) => task.id === activeFlowIndex
)[0];
} else {
return {};
}
}, [templateInfo, activeFlowIndex]);
const randerParameters = useMemo(() => {
return taskInfo.parameters;
}, [taskInfo]);
/** 获取模版数据 */
const { run } = useMyRequest(getWorkflowspecDetail, {
onSuccess: (res: IResponse<ITaskInfo>) => {
console.log(res);
setTemplateInfo(res.data);
},
});
useEffect(() => {
run(id);
}, [id, run]);
/** 返回事件 */
const onBack = () => {
setShowDetail(false);
};
const setExternalSelectedNodeId = (id: string) => {
setActiveFlowIndex(id);
};
const handleParams = () => {
setOverviewActive(false);
};
return (
<div className={styles.swBox}>
<div className={styles.swHeader}>
<div className={styles.swHeaderLeft}>
<div className={styles.goBackIconBox}>
<img
onClick={onBack}
className={styles.goBackIcon}
src={goback}
alt=""
/>
</div>
<div className={styles.swTemplateTitle}>{templateInfo.title}</div>
</div>
</div>
<div className={styles.swContent}>
<div className={styles.swFormBox}>
{!activeFlowIndex && (
<div>
<div className={styles.title}>基础信息</div>
<div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>模板名称</div>
<div className={styles.taskInfoValue}>{templateInfo.title}</div>
</div>
<div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>描述</div>
<div className={styles.taskInfoValue}>
{templateInfo.description}
</div>
</div>
<div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>创建时间</div>
<div className={styles.taskInfoValue}>
{moment(new Date(templateInfo.createdAt)).format(
"yyyy-MM-DD hh:mm"
)}
</div>
</div>
<div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>创建人</div>
<div className={styles.taskInfoValue}>
{templateInfo.creator}
</div>
</div>
<div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>版本</div>
<div className={styles.taskInfoValue}>
{templateInfo.version}
</div>
</div>
<div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>所属产品</div>
<div className={styles.taskInfoValue}>
{getProductName(templateInfo.productId)}
</div>
</div>
</div>
)}
{activeFlowIndex && (
<div className={styles.suanziInfo}>
<MyTooltip>
<div className={styles.title}>{taskInfo?.title}</div>
</MyTooltip>
<div className={styles.tabs}>
<div
className={classNames({
[styles.tabLi]: true,
[styles.tabLiAcitve]: overviewActive,
})}
onClick={() => setOverviewActive(true)}
>
概览
</div>
<div
className={classNames({
[styles.tabLi]: true,
[styles.tabLiAcitve]: !overviewActive,
})}
onClick={() => handleParams()}
>
参数
</div>
</div>
{overviewActive && (
<div className={styles.overview}>
<div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>描述</div>
<div
className={classNames({
[styles.taskInfoValue]: true,
[styles.taskInfoValueShowAll]: true,
})}
>
{taskInfo?.description}
</div>
</div>
<div className={styles.taskInfoLi}>
<div className={styles.taskInfoParams}>算子版本</div>
<div className={styles.taskInfoValue}>
{taskInfo?.version || "-"}
</div>
</div>
</div>
)}
{!overviewActive && (
<div className={styles.params}>
{randerParameters?.map((parameter: any) => {
return (
<div className={styles.taskInfoLi} key={parameter.name}>
<div className={styles.taskInfoParams}>
{parameter.name}
</div>
<div className={styles.taskInfoValue}>
{parameter.defaultValue || "-"}
</div>
</div>
);
})}
</div>
)}
</div>
)}
</div>
<div className={styles.swFlowBox}>
<Flow
externalSelectedNodeId={activeFlowIndex}
tasks={templateInfo?.tasks}
setExternalSelectedNodeId={setExternalSelectedNodeId}
/>
</div>
</div>
</div>
);
});
export default TemplateDetail;
.template {
padding: 19px 24px 0;
}
.top {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.tableBox {
height: calc(100vh - 177px);
}
.templateBox {
border: 2px solid #fff;
box-shadow: 0px 3px 12px 0px rgba(3, 47, 105, 0.09);
border-radius: 6px;
background: linear-gradient(180deg, #f5f7fa 0%, #ffffff 100%);
height: 208px;
box-sizing: border-box;
padding: 16px 20px;
}
.templateTop {
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 12px;
}
.templateTopItem {
font-size: 12px;
line-height: 20px;
color: rgba(138, 144, 153, 1);
}
.templateTopLine {
width: 1px;
height: 16px;
background-color: rgba(221, 225, 230, 1);
margin: 0 11px;
}
.templateTitleBox {
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 12px;
}
.templateTitleBox {
height: 24px;
}
.templateTitle {
font-size: 14px;
line-height: 22px;
color: rgba(30, 38, 51, 1);
margin-left: 8px;
font-weight: 550;
}
.templateDesc {
height: 60px;
font-size: 12px;
line-height: 20px;
color: rgba(86, 92, 102, 1);
margin-bottom: 16px;
overflow: hidden;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
display: -webkit-box;
}
.templateButtons {
display: flex;
align-items: center;
justify-content: space-between;
}
.button {
cursor: pointer;
line-height: 22px;
font-size: 14px;
color: rgba(19, 112, 255, 1);
}
import { useEffect, useState } from "react";
import { observer } from "mobx-react";
import SearchInput from "@/components/BusinessComponents/SearchInput";
import MyButton from "@/components/mui/MyButton";
import MySelect from "@/components/mui/MySelect";
import Add from "@mui/icons-material/Add";
import CardTable from "@/components/CommonComponents/CardTable";
import useMyRequest from "@/hooks/useMyRequest";
import { getWorkflowspecList, deleteWorkflowspec } from "@/api/resourceCenter";
import MyDialog from "@/components/mui/MyDialog";
import templateIcon from "@/assets/resourceCenter/templateIcon.svg";
import { useStores } from "@/store";
import { toJS } from "mobx";
import ProductSelect from "@/components/BusinessComponents/ProductSelect";
import WorkFlowEdit from "@/views/WorkFlowEdit";
import { useMessage } from "@/components/MySnackbar";
import TemplateDetail from "./TemplateDetail";
import style from "./index.module.css";
const UserResourcesTemplate = observer(() => {
const [title, setTitle] = useState("");
const Message = useMessage();
const [showAddTemplate, setShowAddTemplate] = useState(false); // 新增、编辑模板弹窗
const [showProductSelect, setShowProductSelect] = useState(false);
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [showDetail, setShowDetail] = useState(false); // 详情
const [templateId, setTemplateId] = useState("");
const [product, setProduct] = useState(""); // 搜索列表用
const [productId, setProductId] = useState(""); // 新增模板用
const [list, setList] = useState([]);
const { productListStore } = useStores();
const getProductName = (productId: string) => {
let res = "-";
toJS(productListStore.productList).forEach((item) => {
if (item.value === productId) {
res = item.label;
}
});
return res;
};
const renderItem = (item: any) => {
return (
<div className={style.templateBox}>
<div className={style.templateTop}>
<div className={style.templateTopItem}>
{getProductName(item.productId)}
</div>
<div className={style.templateTopLine}></div>
<div className={style.templateTopItem}>{item.version}</div>
<div className={style.templateTopLine}></div>
<div className={style.templateTopItem}>{item.updatedTime}</div>
</div>
<div className={style.templateTitleBox}>
<img src={templateIcon} alt="" />
<div className={style.templateTitle}>{item.title}</div>
</div>
<div className={style.templateDesc}>{item.description}</div>
<div className={style.templateButtons}>
<div className={style.templateBL}>
<span
className={style.button}
onClick={() => {
setTemplateId(item.id);
setProductId(item.productId);
setShowAddTemplate(true);
}}
>
升级
</span>
</div>
<div className={style.templateBR}>
<MyButton
text="删除"
variant="outlined"
style={{
border: "1px solid rgba(221, 225, 230, 1)",
color: "rgba(138, 144, 153, 1)",
marginRight: "12px",
}}
onClick={() => {
setShowDeleteDialog(true);
setTemplateId(item.id);
}}
></MyButton>
<MyButton
text="查看"
variant="outlined"
onClick={() => {
setTemplateId(item.id);
setShowDetail(true);
}}
></MyButton>
</div>
</div>
</div>
);
};
// 获取模板列表
const { run } = useMyRequest(getWorkflowspecList, {
onSuccess: (result: any) => {
setList(result.data);
},
});
useEffect(() => {
if (!showAddTemplate) {
run({
productId: product === "all" ? "" : product,
title,
});
}
}, [run, title, product, showAddTemplate]);
const { run: deleteWorkflowspecFn } = useMyRequest(deleteWorkflowspec, {
onSuccess: () => {
Message.success("删除成功");
setShowDeleteDialog(false);
run({
productId: product === "all" ? "" : product,
title,
});
},
});
const deleteConfirm = () => {
deleteWorkflowspecFn({ id: templateId });
};
return (
<div className={style.template}>
<div className={style.top}>
<div className={style.topLeft}>
<SearchInput
sx={{ width: 340, marginRight: "16px" }}
onKeyUp={(e: any) => {
if (e.keyCode === 13) {
setTitle(e.target.value);
}
}}
></SearchInput>
<MySelect
title="产品类型"
isTitle={true}
options={
[
{ label: "全部", value: "all" },
...productListStore?.productList,
] || []
}
value={product}
onChange={(e: any) => setProduct(e)}
sx={{ width: "150px", height: "32px" }}
></MySelect>
</div>
<div className={style.topRight}>
<MyButton
text="新建自定义模板"
onClick={() => setShowProductSelect(true)}
startIcon={<Add />}
></MyButton>
</div>
</div>
<div className={style.tableBox}>
<CardTable
data={list}
renderItem={renderItem}
minWidth={377}
></CardTable>
</div>
{showAddTemplate && (
<WorkFlowEdit
id={templateId}
propsProductId={productId}
onBack={() => {
setShowAddTemplate(false);
}}
></WorkFlowEdit>
)}
{showProductSelect && (
<ProductSelect
open={showProductSelect}
setOpen={setShowProductSelect}
productId={productId}
setProductId={setProductId}
onConfirm={() => {
setTemplateId("");
setShowAddTemplate(true);
}}
></ProductSelect>
)}
{showDeleteDialog && (
<MyDialog
onClose={() => setShowDeleteDialog(false)}
onConfirm={() => deleteConfirm()}
open={showDeleteDialog}
isText={true}
title="提示"
>
确定要删除这个模板吗?
</MyDialog>
)}
{showDetail && (
<TemplateDetail
id={templateId}
setShowDetail={setShowDetail}
></TemplateDetail>
)}
</div>
);
});
export default UserResourcesTemplate;
.addOperatorBox { .addOperatorBox {
z-index: 2;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
...@@ -38,6 +39,7 @@ ...@@ -38,6 +39,7 @@
} }
.form { .form {
width: 368px; width: 368px;
min-width: 368px;
box-sizing: border-box; box-sizing: border-box;
padding: 16px 24px; padding: 16px 24px;
border: 1px solid #ebedf0; border: 1px solid #ebedf0;
...@@ -47,6 +49,7 @@ ...@@ -47,6 +49,7 @@
} }
.newForm { .newForm {
width: 368px; width: 368px;
min-width: 368px;
box-sizing: border-box; box-sizing: border-box;
padding: 16px 32px 0px 0; padding: 16px 32px 0px 0;
border-right: 1px solid #ebedf0; border-right: 1px solid #ebedf0;
...@@ -61,6 +64,9 @@ ...@@ -61,6 +64,9 @@
} }
.codeTitle { .codeTitle {
background-color: rgba(230, 233, 237, 1); background-color: rgba(230, 233, 237, 1);
display: flex;
justify-content: space-between;
align-items: center;
padding: 11px 20px; padding: 11px 20px;
color: rgba(30, 38, 51, 1); color: rgba(30, 38, 51, 1);
font-size: 14px; font-size: 14px;
......
...@@ -2,20 +2,21 @@ ...@@ -2,20 +2,21 @@
* @Author: 吴永生 15770852798@163.com * @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-18 16:12:55 * @Date: 2022-10-18 16:12:55
* @LastEditors: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 21:27:03 * @LastEditTime: 2022-10-24 20:39:26
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/components/AddOperator/index.tsx * @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/components/AddOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
import { useEffect, useState, useCallback, useMemo } from "react"; import { useEffect, useState, useCallback, useMemo } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Base64 } from "js-base64";
import _ from "lodash";
import { toJS } from "mobx";
import MyInput from "@/components/mui/MyInput"; import MyInput from "@/components/mui/MyInput";
import MySelect from "@/components/mui/MySelect"; import MySelect from "@/components/mui/MySelect";
import MyButton from "@/components/mui/MyButton"; import MyButton from "@/components/mui/MyButton";
import style from "./index.module.css";
import SwitchBatchFolw from "@/views/ResourceCenter/components/SwitchBatchFolw"; import SwitchBatchFolw from "@/views/ResourceCenter/components/SwitchBatchFolw";
import Code from "@/components/CommonComponents/Code"; import Code from "@/components/CommonComponents/Code";
import RadioGroupOfButtonStyle from "@/components/CommonComponents/RadioGroupOfButtonStyle"; import RadioGroupOfButtonStyle from "@/components/CommonComponents/RadioGroupOfButtonStyle";
import { ITask } from "@/views/Project/ProjectSubmitWork/interface"; import { ITask } from "@/views/Project/ProjectSubmitWork/interface";
...@@ -23,15 +24,28 @@ import BatchOperatorFlow from "@/views/Project/components/Flow/components/BatchO ...@@ -23,15 +24,28 @@ import BatchOperatorFlow from "@/views/Project/components/Flow/components/BatchO
import OperatorList from "@/views/CustomOperator/components/OperatorList"; import OperatorList from "@/views/CustomOperator/components/OperatorList";
import FormItemBox from "@/components/mui/FormItemBox"; import FormItemBox from "@/components/mui/FormItemBox";
import { useStores } from "@/store"; import { useStores } from "@/store";
import useMyRequest from "@/hooks/useMyRequest";
import { getActorEnvOptions, saveOperator } from "@/api/resourceCenter";
import { IOperatorAddFormData } from "../../interface";
import { checkFormData, checkParamsConfig, initCode, text } from "./utils";
import batchOperator from "@/assets/resourceCenter/batchOperator.svg";
import flowOperator from "@/assets/resourceCenter/flowOperator.svg";
import { useMessage } from "@/components/MySnackbar";
import style from "./index.module.css";
import MyPopover from "@/components/mui/MyPopover";
import useCheckOperator from "@/views/CustomOperator/useCheckOperator";
interface IAddOperator { interface IAddOperator {
setAddOpen: (val: boolean) => void; pageType: string;
setPageType: (val: string) => void;
detailsId: string;
} }
type IBuildType = "ENVIRONMENT" | "OPERATOR"; type IBuildType = "ENVIRONMENT" | "OPERATOR";
const AddOperator = observer((props: IAddOperator) => { const AddOperator = observer((props: IAddOperator) => {
const { setAddOpen } = props; const { pageType, setPageType, detailsId } = props;
const Message = useMessage();
/** 创建类型 BATCH - 批算子; FLOW - 流算子*/ /** 创建类型 BATCH - 批算子; FLOW - 流算子*/
const [taskType, setTaskType] = useState<"BATCH" | "FLOW">("BATCH"); const [taskType, setTaskType] = useState<"BATCH" | "FLOW">("BATCH");
...@@ -44,9 +58,29 @@ const AddOperator = observer((props: IAddOperator) => { ...@@ -44,9 +58,29 @@ const AddOperator = observer((props: IAddOperator) => {
const [inputActive, setInputActive] = useState(false); const [inputActive, setInputActive] = useState(false);
/** 产品store */ /** 产品store */
const { productListStore } = useStores(); const { productListStore } = useStores();
/** 产品第一项 默认所属产品值 */
const defaultProduct = toJS(productListStore?.productList)?.length
? toJS(productListStore?.productList[0])?.value
: undefined;
/** 应用环境下拉 */
const [actorEnvOptions, setActorEnvOptions] = useState([]);
/** 参数配置 */
const [code, setCode] = useState(JSON.stringify(initCode, null, "\t"));
/** 运行脚本 */
const [command, setCommand] = useState<string>("");
const [code, setCode] = useState(""); /** 表单数据 */
// const [formData, setFormData] = useState<any>(); const [formData, setFormData] = useState<IOperatorAddFormData>({});
/** 表单数据修改 */
const [formErrors, setFormErrors] = useState<IOperatorAddFormData>({});
/** 是否打开帮助手册 */
const [tipsOpen, setTipsOpen] = useState<boolean>(false);
const { handleCheck, checkStatus } = useCheckOperator(
operatorList,
() => {},
"流程编排不能为空"
);
const buildTypeList = useMemo(() => { const buildTypeList = useMemo(() => {
return [ return [
...@@ -55,53 +89,197 @@ const AddOperator = observer((props: IAddOperator) => { ...@@ -55,53 +89,197 @@ const AddOperator = observer((props: IAddOperator) => {
]; ];
}, []); }, []);
/** 表单数据 */
const changeFormData = useCallback(
(val: IOperatorAddFormData) => {
setFormData({ ...formData, ...val });
},
[formData]
);
const paramsConfigBlur = useCallback(() => {
console.log(2);
if (code === "") return;
let result;
try {
result = JSON.stringify(JSON.parse(code), null, "\t");
if (typeof result === "string") {
setCode(result);
const checkErrorArr = checkParamsConfig(result);
if (checkErrorArr.length) {
// Message.error("参数配置不正确!");
// setFormErrors({
// ...formErrors,
// ...{ parameters: "参数配置不正确!" },
// });
const codeDom = document.getElementById("paramsConfig");
const all = codeDom?.getElementsByClassName("cm-line");
const allArr = Array.prototype.slice.call(all);
for (let x = 0; x < allArr?.length; x++) {
const a = `${allArr[x]?.innerHTML}`
.replace(/"/g, "")
.replace(/,/g, "")
.replace(/\t/g, "");
if (checkErrorArr.includes(a)) {
setTimeout(() => {
allArr[x].style =
"text-decoration: wavy underline; text-decoration-color: #ff4e4e;";
}, 200);
}
}
} else {
const newFormErrors = _.cloneDeep(formErrors);
delete newFormErrors.parameters;
setFormErrors(newFormErrors);
}
}
} catch (error) {
Message.error("JSON格式不正确!");
console.log("JSON格式不正确!");
}
}, [Message, code, formErrors]);
/** 获取应用环境下拉 */
const { run: fetchActorEnvOptions } = useMyRequest(getActorEnvOptions, {
onSuccess: (res: any) => {
if (res.message === "success") {
const newActorEnvOptions =
res?.data?.map((item: any) => {
return { label: item.title, value: item.id };
}) || [];
setActorEnvOptions(newActorEnvOptions);
}
},
});
useEffect(() => {
fetchActorEnvOptions({ type: taskType });
}, [fetchActorEnvOptions, taskType]);
/** 切换类型 */ /** 切换类型 */
const handleRadio = (val: IBuildType) => { const handleRadio = (val: IBuildType) => {
setBatchBuildType(val); setBatchBuildType(val);
}; };
const handleSubmit = () => { const handleSubmit = useCallback(() => {
console.log(33); const resultErrors = checkFormData(formData, batchBuildType);
}; // paramsConfigBlur();
formErrors?.parameters
? setFormErrors({ ...resultErrors, parameters: formErrors.parameters })
: setFormErrors({ ...resultErrors });
if (taskType === "BATCH" && batchBuildType === "OPERATOR") {
handleCheck();
}
if (Object.getOwnPropertyNames(resultErrors)?.length || !checkStatus)
return;
let newParameters = [];
try {
newParameters = JSON.parse(code);
} catch (err) {
console.log(err);
}
console.log(operatorList, "operatorList");
const params = {
...formData,
...(batchBuildType === "ENVIRONMENT" && taskType === "BATCH"
? { command: Base64.encode(command) }
: {}),
parameters:
batchBuildType === "OPERATOR" && taskType === "BATCH"
? operatorList
: newParameters,
type: taskType,
};
/** 基于流算子没有应用环境字段 */
if (batchBuildType === "OPERATOR") {
delete params.envId;
}
saveOperator(params).then((res: any) => {
if (res?.message === "success") {
Message.success("构建成功");
} else {
Message.success(res?.message || "构建失败");
}
});
}, [
Message,
batchBuildType,
checkStatus,
code,
command,
formData,
formErrors.parameters,
handleCheck,
operatorList,
taskType,
]);
return ( return (
<div className={style.addOperatorBox}> <div className={style.addOperatorBox}>
<div className={style.left}> <div className={style.left}>
<SwitchBatchFolw <SwitchBatchFolw
bottomImg={flowOperator}
topImg={batchOperator}
active={taskType} active={taskType}
setActive={setTaskType} setActive={(e: "BATCH" | "FLOW") => {
goBack={() => setAddOpen(false)} setBatchBuildType("ENVIRONMENT");
></SwitchBatchFolw> setTaskType(e);
}}
goBack={() => setPageType("")}
/>
</div> </div>
<div className={style.right}> <div className={style.right}>
<div className={style.title}> <div className={style.title}>
{taskType === "BATCH" ? "批式算子信息" : "流式算子信息"} {taskType === "BATCH" ? "批式算子信息" : "流式算子信息"}
</div> </div>
<div style={{ paddingBottom: "20px" }}> {taskType === "BATCH" ? (
<RadioGroupOfButtonStyle <div
RadiosBoxStyle={{ width: 236 }} style={{
value={batchBuildType} paddingBottom: batchBuildType === "ENVIRONMENT" ? "20px" : "2px",
radioOptions={buildTypeList} }}
handleRadio={handleRadio} >
/> <RadioGroupOfButtonStyle
</div> RadiosBoxStyle={{ width: 236 }}
value={batchBuildType}
radioOptions={buildTypeList}
handleRadio={handleRadio}
/>
</div>
) : null}
<div className={style.content}> <div className={style.content}>
<div <div
className={classNames({ className={classNames({
[style.form]: batchBuildType === "ENVIRONMENT", [style.form]: batchBuildType === "ENVIRONMENT",
[style.newForm]: batchBuildType != "ENVIRONMENT", [style.newForm]: batchBuildType !== "ENVIRONMENT",
})} })}
> >
<FormItemBox <FormItemBox
label="算子名称" label="算子名称"
labelClassName={style.labelClassName} labelClassName={style.labelClassName}
className={style.operatorFormItem} className={classNames({
[style.operatorFormItem]: formErrors?.title,
})}
itemFlex="column" itemFlex="column"
errorMessage={formErrors?.title || ""}
require require
> >
<MyInput <MyInput
helperText="30字符以内,仅限字母、数字、中文" helperText={
formErrors?.title ? "" : "15字符以内,仅限字母、数字、中文"
}
placeholder="请输入算子名称" placeholder="请输入算子名称"
value={formData?.title}
onChange={(e) => {
if (e.target.value?.length > 15) return;
changeFormData({ title: e.target.value });
}}
/> />
</FormItemBox> </FormItemBox>
<FormItemBox <FormItemBox
...@@ -109,20 +287,35 @@ const AddOperator = observer((props: IAddOperator) => { ...@@ -109,20 +287,35 @@ const AddOperator = observer((props: IAddOperator) => {
labelClassName={style.labelClassName} labelClassName={style.labelClassName}
className={style.operatorFormItem} className={style.operatorFormItem}
itemFlex="column" itemFlex="column"
errorMessage={formErrors?.version || ""}
require require
> >
<MyInput /> <MyInput
value={formData?.version}
onChange={(e) => {
changeFormData({ version: e.target.value });
}}
/>
</FormItemBox> </FormItemBox>
<FormItemBox <FormItemBox
label="所属产品" label="所属产品"
labelClassName={style.labelClassName} labelClassName={style.labelClassName}
className={style.operatorFormItem} errorMessage={formErrors?.productId || ""}
className={classNames({
[style.operatorFormItem]: batchBuildType === "ENVIRONMENT",
})}
itemFlex="column" itemFlex="column"
require require
> >
<MySelect <MySelect
fullWidth fullWidth
options={productListStore?.productList || []} options={productListStore?.productList || []}
defaultValue={defaultProduct}
value={formData?.productId}
onChange={(e) => {
changeFormData({ productId: e });
}}
/> />
</FormItemBox> </FormItemBox>
{batchBuildType === "ENVIRONMENT" ? ( {batchBuildType === "ENVIRONMENT" ? (
...@@ -130,12 +323,17 @@ const AddOperator = observer((props: IAddOperator) => { ...@@ -130,12 +323,17 @@ const AddOperator = observer((props: IAddOperator) => {
label="应用环境" label="应用环境"
labelClassName={style.labelClassName} labelClassName={style.labelClassName}
className={style.operatorFormItem} className={style.operatorFormItem}
errorMessage={formErrors?.envId || ""}
itemFlex="column" itemFlex="column"
require require
> >
<MySelect <MySelect
fullWidth fullWidth
options={[{ label: "cadd", value: "CADD" }]} options={actorEnvOptions || []}
value={formData?.envId}
onChange={(e) => {
changeFormData({ envId: e });
}}
/> />
</FormItemBox> </FormItemBox>
) : null} ) : null}
...@@ -143,22 +341,50 @@ const AddOperator = observer((props: IAddOperator) => { ...@@ -143,22 +341,50 @@ const AddOperator = observer((props: IAddOperator) => {
<FormItemBox <FormItemBox
label="描述" label="描述"
labelClassName={style.labelClassName} labelClassName={style.labelClassName}
className={style.operatorFormItem}
itemFlex="column" itemFlex="column"
errorMessage={formErrors?.description || ""}
> >
<MyInput multiline rows={4} placeholder="请输入算子描述" /> <MyInput
multiline
rows={4}
placeholder="请输入算子描述"
value={formData?.description}
onChange={(e) => {
changeFormData({ description: e.target.value });
}}
/>
</FormItemBox> </FormItemBox>
) : null} ) : null}
</div> </div>
{batchBuildType === "ENVIRONMENT" ? ( {batchBuildType === "ENVIRONMENT" ? (
<div className={style.codeBox}> <div className={style.codeBox}>
<div className={style.codeTitle}>参数配置</div> <div className={style.codeTitle}>
<span>参数配置</span>
<MyPopover
open={tipsOpen}
changeOpen={(val) => setTipsOpen(val)}
content={<pre>{text}</pre>}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
>
<span style={{ color: "#1370FF" }}>帮助手册</span>
</MyPopover>
</div>
<div className={style.code}> <div className={style.code}>
<Code <Code
id="paramsConfig"
value={code} value={code}
onChange={(e: string) => setCode(e)} // placeholder="dd"
onChange={(e: string, viewUpdate: any) => {
setCode(e);
}}
onBlur={paramsConfigBlur}
height="535px" height="535px"
width="600"
style={{ flex: 1 }}
/> />
</div> </div>
</div> </div>
...@@ -169,54 +395,63 @@ const AddOperator = observer((props: IAddOperator) => { ...@@ -169,54 +395,63 @@ const AddOperator = observer((props: IAddOperator) => {
labelClassName={style.labelClassName} labelClassName={style.labelClassName}
className={style.operatorFormItem} className={style.operatorFormItem}
itemFlex="column" itemFlex="column"
errorMessage={formErrors?.description || ""}
> >
<MyInput <MyInput
style={{ width: "420px" }} style={{ width: "420px" }}
multiline multiline
rows={10} rows={10}
placeholder="请输入算子描述" placeholder="请输入算子描述"
value={formData?.description}
onChange={(e) => {
changeFormData({ description: e.target.value });
}}
/> />
</FormItemBox> </FormItemBox>
</div> </div>
)} )}
</div> </div>
<div className={style.parameterConfigBox}> {taskType === "FLOW" ? null : (
<div className={style.codeTitle}> <div className={style.parameterConfigBox}>
{batchBuildType === "ENVIRONMENT" ? "运行脚本" : "流程编排"} <div className={style.codeTitle}>
</div> {batchBuildType === "ENVIRONMENT" ? "运行脚本" : "流程编排"}
{batchBuildType === "ENVIRONMENT" ? (
<div className={style.code}>
<Code
value={code}
onChange={(e: string) => setCode(e)}
height="350px"
/>
</div>
) : (
<div
id="customOperatorFlow"
style={{ position: "relative", height: 400 }}
>
<OperatorList
operatorList={operatorList}
setOperatorList={setOperatorList}
setInputActive={setInputActive}
/>
<BatchOperatorFlow
tasks={operatorList}
setTasks={setOperatorList}
type="edit"
// onFlowNodeClick={handleNodeClick}
flowNodeDraggable={true}
ListenState={!inputActive}
showVersion={true}
showControls={false}
/>
</div> </div>
)} {batchBuildType === "ENVIRONMENT" ? (
</div> <div className={style.code}>
<Code
value={command}
onChange={(e: string) => setCommand(e)}
height="350px"
/>
</div>
) : (
<div
id="customOperatorFlow"
style={{ position: "relative", height: 400 }}
>
<OperatorList
operatorList={operatorList}
setOperatorList={setOperatorList}
setInputActive={setInputActive}
productId={formData.productId || defaultProduct || ""}
/>
<BatchOperatorFlow
tasks={operatorList}
setTasks={setOperatorList}
type="edit"
// onFlowNodeClick={handleNodeClick}
flowNodeDraggable={true}
ListenState={!inputActive}
showVersion={true}
showControls={false}
/>
</div>
)}
</div>
)}
<div className={style.buttonBox}> <div className={style.buttonBox}>
<MyButton text="开始构建"></MyButton> <MyButton text="开始构建" onClick={handleSubmit} />
</div> </div>
</div> </div>
</div> </div>
......
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-20 17:36:14
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-24 15:52:56
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/components/AddOperator/utils.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { IOperatorAddFormData } from "../../interface";
type IBuildType = "ENVIRONMENT" | "OPERATOR";
export const checkFormData = (formData: IOperatorAddFormData, taskType: IBuildType)=>{
const reg = new RegExp("^[A-Za-z0-9\u4e00-\u9fa5]{1,15}$");
const result:IOperatorAddFormData = {}
if(!formData?.title){
result.title = '请输入算子名称'
}
if(!reg.test(formData?.title || '')){
result.title = '格式不正确,仅限大小写字母、数字、中文'
}
if(!/^[1-9]\d?(\.(0|[1-9]\d?)){2}$/.test(formData?.version || '')){
result.version = '格式不正确,必须为X.Y.Z格式,且XYZ必须为0~99的正整数'
}
if(!formData?.version){
result.version = '请输入算子版本'
}
if(!formData?.productId){
result.productId = '请选择所属产品'
}
if(taskType ==='ENVIRONMENT' && !formData?.envId){
result.envId = '请选择应用环境'
}
return result
}
/** 参数配置校验 */
export const checkParamsConfig = (val: string) => {
let result: any = []
try{
const value = JSON.parse(val)
value?.length && value.forEach((item: any)=>{
const nameReg = new RegExp("^[A-Za-z][A-Za-z0-9_]{1,14}$");
const titleReg = new RegExp("^[A-Za-z0-9\u4e00-\u9fa5]{1,15}$");
if( !nameReg.test(item?.name) ){
result.push(`name: ${item?.name}`)
}
if( !titleReg.test(item?.title) ){
result.push(`title: ${item?.title}`)
}
if( item?.description > 300 ){
result.push(`description: ${item?.description}`)
}
if( !['STRING','FILE','DATASET','INT','FLOAT','DOUBLE','BOOLEAN','ARRAY_STRING','ARRAY_FILE','ARRAY_DATASET','ARRAY_INT','ARRAY_FLOAT','ARRAY_DOUBLE','ARRAY_BOOLEAN'].includes(item?.classType )){
result.push(`classType: ${item?.classType}`)
}
if(!['true','false'].includes(String(item?.required)) ) {
result.push(`required: ${item?.required}`)
}
if( !['PATH','DATASET','FILE','INPUT','SELECT','MULTIPLESELECT','RADIO','CHECKBOX'].includes(item?.domType )){
result.push(`domType: ${item?.domType}`)
}
})
} catch(error){
console.log(error)
}
return result
}
export const initCode = [{
"name" : "timeout",
"classType" : "INT",
"required" : false,
"defaultValue" : 10000,
"description" : "",
"hidden" : true,
"title" : "",
"order" : 0,
"domType" : "INPUT",
"choices" : [
{
"label" : "是",
"value" : "true"
},
{
"label": "否",
"value": "false"
}
],
"validators" : [
{
"regex" : "^.*\\.(pdb|PDB|pdbqt|PDBQT)$",
"message" : "请输入PDB或PDBQT文件"
}
]
}]
export const text = `{
// 参数名, 必填,在15字符以内,仅限大小写字母、数字、"_",且只能以大小写字母开头
"name" : "timeout",
/** 参数类型。可选值有 STRING:字符串、FILE:文件、DATASET:数据集、INT:整型、FLOAT:单精度浮点型、
* DOUBLE:多精度浮点型、BOOLEAN:布尔值、ARRAY_STRING:字符串数组、ARRAY_FILE:文件数组、ARRAY_DATASET:数据集数组、
* ARRAY_INT:整型数组、ARRAY_FLOAT:单精度浮点型数组、ARRAY_DOUBLE:多精度浮点型数组、ARRAY_BOOLEAN:布尔值数组
*/
"classType" : "INT",
// 是否必填。在使用该算子时是否必须输入改参数的值
"required" : false,
// 默认值
"defaultValue" : 10000,
// 参数描述。在300字符以内
"description" : "",
// 是否隐藏,隐藏就在页面不显示该参数 必填
"hidden" : true,
// 页面展示的参数的名称
"title" : "",
// 参数展示的顺序优先级
"order" : 0,
/**
* 前端填值的方式 PATH:路径选择器、DATASET:数据集选择器、FILE:文件选择器、INPUT:输入框、
* SELECT:下拉框、MULTIPLESELECT:多选下拉框、RADIO:单选按钮、CHECKBOX:多选按钮
*/
"domType" : "INPUT",
// 选项。当domType为SELECT、MULTIPLESELECT、RADIO、CHECKBOX时生效。以对象数组的形式保存
"choices" : [
{
// 在前端展示的值
"label" : "是",
//选中时传递服务使用的值
"value" : "true"
},
],
//用于校验输入值。以对象数组的形式保存
"validators" : [
{
//正则表达式
"regex" : "^.*\\.(pdb|PDB|pdbqt|PDBQT)$",
//不符合正则时的报错信息
"message" : "请输入PDB或PDBQT文件"
}
]
}`
\ No newline at end of file
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
height: 108px; height: 108px;
padding: 20px; padding: 20px;
margin: 0 20px 20px 0; margin: 0 20px 20px 0;
cursor: pointer;
background: linear-gradient(180deg, #f5f7fa 0%, #ffffff 100%); background: linear-gradient(180deg, #f5f7fa 0%, #ffffff 100%);
box-shadow: 0px 3px 12px 0px rgba(3, 47, 105, 0.09); box-shadow: 0px 3px 12px 0px rgba(3, 47, 105, 0.09);
border-radius: 6px; border-radius: 6px;
...@@ -15,10 +16,20 @@ ...@@ -15,10 +16,20 @@
.itemHeaderBox { .itemHeaderBox {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center;
margin-bottom: 20px; margin-bottom: 20px;
} }
.startTitleBox {
width: 36px;
height: 36px;
border-radius: 4px;
background: #a657f4;
color: #fff;
font-size: 18px;
font-weight: 600;
text-align: center;
line-height: 36px;
}
.itemContentBox { .itemContentBox {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
...@@ -30,17 +41,26 @@ ...@@ -30,17 +41,26 @@
color: #1e2633; color: #1e2633;
} }
.titleBox {
display: block;
font-size: 16px;
color: #1e2633;
}
.operatorTypeBox { .operatorTypeBox {
display: block; display: block;
color: #8a9099; color: #8a9099;
font-size: 12px; font-size: 12px;
} }
.operationBox {
display: inline-block;
width: 24px;
height: 24px;
text-align: center;
line-height: 24px;
cursor: pointer;
}
.operationBox:hover {
background-color: #ebedf0;
border-radius: 2px;
}
.infoBox { .infoBox {
width: 50%; width: 50%;
font-weight: 400; font-weight: 400;
......
...@@ -2,45 +2,90 @@ ...@@ -2,45 +2,90 @@
* @Author: 吴永生 15770852798@163.com * @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11 * @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 21:08:31 * @LastEditTime: 2022-10-24 20:38:16
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx * @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
import styles from "./index.module.css"; import styles from "./index.module.css";
import { IOperatorInfo } from "../../interface"; import { IOperatorInfo, operatorType } from "../../interface";
import { observer } from "mobx-react";
import { useStores } from "@/store";
import { useMemo } from "react";
import MyMenu from "@/components/mui/MyMenu";
import { useNavigate } from "react-router-dom";
interface IProps { interface IProps {
operatorInfo: IOperatorInfo; operatorInfo: IOperatorInfo;
setPageType: (val: string) => void;
setDetailsId: (val: string) => void;
} }
const OperatorCard = (props: IProps) => { const OperatorCard = observer((props: IProps) => {
const navigate = useNavigate();
const { const {
operatorInfo: { title = "", type, version }, operatorInfo: { title = "", type, version, productId, createdTime, id },
setPageType,
setDetailsId,
} = props; } = props;
/** 产品store */
const { productListStore } = useStores();
const productText = useMemo(() => {
const result = productListStore.productList?.filter((item) => {
return item.value === productId;
});
return result?.length ? result[0].label : "";
}, [productListStore.productList, productId]);
const onClickOperation = (val: string) => {
if (val === "upgrade") {
setPageType("edit");
setDetailsId(id || "");
}
};
const handleToSeeOperator = (item: any) => {
navigate("/utility/resourceCenter/userResources/seeFloe", {
state: { id: item.id },
});
};
return ( return (
<div className={styles.itemBox}> <div className={styles.itemBox} onClick={handleToSeeOperator}>
<div className={styles.itemHeaderBox}> <div className={styles.itemHeaderBox}>
<img alt="" style={{ width: 36, height: 36 }} /> <div className={styles.startTitleBox}>{title?.slice(0, 1)}</div>
<div> <div style={{ flex: 1, marginLeft: 12 }}>
<b className={styles.titleBox}>{title}</b> <b className={styles.titleBox}>{title}</b>
<span className={styles.operatorTypeBox}>批算子</span> <span className={styles.operatorTypeBox}>
{operatorType[type] || ""}
</span>
</div> </div>
<MyMenu
value=""
options={[{ label: "升级", value: "upgrade" }]}
hasTriangle={false}
setValue={onClickOperation}
sx={{
zIndex: 1601,
}}
>
<span className={styles.operationBox}>大大</span>
</MyMenu>
</div> </div>
<div className={styles.itemContentBox}> <div className={styles.itemContentBox}>
<p className={styles.infoBox}> <p className={styles.infoBox}>
所属产品:<span>{type}</span> 所属产品:<span>{productText}</span>
</p> </p>
<p className={styles.infoBox}> <p className={styles.infoBox}>
算子版本:<span>{`V${version}`}</span> 算子版本:<span>{`V${version}`}</span>
</p> </p>
<p className={styles.infoBox}> <p className={styles.infoBox}>
创建时间:<span>2022-10-11</span> 创建时间:<span>{createdTime}</span>
</p> </p>
</div> </div>
</div> </div>
); );
}; });
export default OperatorCard; export default OperatorCard;
...@@ -2,13 +2,15 @@ ...@@ -2,13 +2,15 @@
* @Author: 吴永生 15770852798@163.com * @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-17 14:35:11 * @Date: 2022-10-17 14:35:11
* @LastEditors: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 21:11:50 * @LastEditTime: 2022-10-24 20:40:16
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx * @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
// 应用环境 // 应用环境
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import _ from "lodash";
import Add from "@mui/icons-material/Add";
import SearchInput from "@/components/BusinessComponents/SearchInput"; import SearchInput from "@/components/BusinessComponents/SearchInput";
import MySelect from "@/components/mui/MySelect"; import MySelect from "@/components/mui/MySelect";
...@@ -17,34 +19,55 @@ import OperatorCard from "./components/OperatorCard"; ...@@ -17,34 +19,55 @@ import OperatorCard from "./components/OperatorCard";
import AddOperator from "./components/AddOperator"; import AddOperator from "./components/AddOperator";
import { useStores } from "@/store"; import { useStores } from "@/store";
import { getOperatorList, IOperatorListParams } from "@/api/resourceCenter"; import { getOperatorList, IOperatorListParams } from "@/api/resourceCenter";
import styles from "./index.module.css";
import useMyRequest from "@/hooks/useMyRequest"; import useMyRequest from "@/hooks/useMyRequest";
import { IOperatorInfo } from "./interface"; import { IOperatorInfo } from "./interface";
import styles from "./index.module.css";
const WorkflowOperator = observer(() => { const WorkflowOperator = observer(() => {
const [addOpen, setAddOpen] = useState<boolean>(false); const [pageType, setPageType] = useState<string>("");
/** 产品store */ /** 产品store */
const { productListStore } = useStores(); const { productListStore } = useStores();
/** 算子列表参数 */ /** 算子列表参数 */
const [searchParams, setSearchParams] = useState<IOperatorListParams>({ const [searchParams, setSearchParams] = useState<IOperatorListParams>({
keyword: "", keyword: "",
productId: "", productId: "all",
type: "", type: "all",
}); });
const [detailsId, setDetailsId] = useState<string>("");
const [list, setList] = useState<IOperatorInfo[]>(); const [list, setList] = useState<IOperatorInfo[]>();
const { run: getList } = useMyRequest(getOperatorList, { const { run: getList } = useMyRequest(getOperatorList, {
// refreshDeps: [],
onSuccess: (res) => { onSuccess: (res) => {
console.log(res); console.log(res);
setList(res?.data); setList(res?.data);
}, },
}); });
const newGetList = useCallback(() => {
const params = _.cloneDeep(searchParams);
if (params.productId === "all") {
params.productId = "";
}
if (params.type === "all") {
params.type = "";
}
getList(params);
}, [getList, searchParams]);
// 按回车搜索
const handleKeyWordChangeKeyUp = (e: any) => {
if (e.keyCode === 13) {
newGetList();
}
};
useEffect(() => { useEffect(() => {
getList(); newGetList();
}, []); }, [searchParams.productId, searchParams.type]);
return ( return (
<> <>
...@@ -55,6 +78,7 @@ const WorkflowOperator = observer(() => { ...@@ -55,6 +78,7 @@ const WorkflowOperator = observer(() => {
sx={{ width: 340, marginRight: "16px" }} sx={{ width: 340, marginRight: "16px" }}
placeholder="输入关键词搜索" placeholder="输入关键词搜索"
value={searchParams.keyword} value={searchParams.keyword}
onKeyUp={handleKeyWordChangeKeyUp}
onChange={(e) => { onChange={(e) => {
setSearchParams({ ...searchParams, keyword: e.target.value }); setSearchParams({ ...searchParams, keyword: e.target.value });
}} }}
...@@ -62,8 +86,13 @@ const WorkflowOperator = observer(() => { ...@@ -62,8 +86,13 @@ const WorkflowOperator = observer(() => {
<MySelect <MySelect
title="所属产品" title="所属产品"
isTitle={true} isTitle={true}
options={productListStore?.productList || []} options={
value={searchParams.keyword} [
{ label: "全部", value: "all" },
...productListStore?.productList,
] || []
}
value={searchParams.productId}
onChange={(e) => { onChange={(e) => {
setSearchParams({ ...searchParams, productId: e }); setSearchParams({ ...searchParams, productId: e });
}} }}
...@@ -73,11 +102,12 @@ const WorkflowOperator = observer(() => { ...@@ -73,11 +102,12 @@ const WorkflowOperator = observer(() => {
<MySelect <MySelect
title="环境类型" title="环境类型"
isTitle={true} isTitle={true}
value={searchParams.keyword} value={searchParams.type}
onChange={(e) => { onChange={(e) => {
setSearchParams({ ...searchParams, type: e }); setSearchParams({ ...searchParams, type: e });
}} }}
options={[ options={[
{ label: "全部", value: "all" },
{ {
label: "批式", label: "批式",
value: "BATCH", value: "BATCH",
...@@ -92,22 +122,32 @@ const WorkflowOperator = observer(() => { ...@@ -92,22 +122,32 @@ const WorkflowOperator = observer(() => {
</div> </div>
<MyButton <MyButton
text="构建算子" text="构建算子"
img={ startIcon={<Add />}
<span onClick={() => {
style={{ fontSize: "14px", marginRight: "8px" }} setPageType("add");
className="iconfont icon-dianzan" setDetailsId("");
></span> }}
}
onClick={() => setAddOpen(true)}
></MyButton> ></MyButton>
</div> </div>
<div className={styles.contentBox}> <div className={styles.contentBox}>
{list?.map((item) => { {list?.map((item) => {
return <OperatorCard operatorInfo={item} />; return (
<OperatorCard
setDetailsId={setDetailsId}
operatorInfo={item}
setPageType={setPageType}
/>
);
})} })}
</div> </div>
</div> </div>
{addOpen && <AddOperator setAddOpen={setAddOpen} />} {pageType && (
<AddOperator
detailsId={detailsId}
setPageType={setPageType}
pageType={pageType}
/>
)}
</> </>
); );
}); });
......
...@@ -2,15 +2,32 @@ ...@@ -2,15 +2,32 @@
* @Author: 吴永生 15770852798@163.com * @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-19 20:50:18 * @Date: 2022-10-19 20:50:18
* @LastEditors: 吴永生 15770852798@163.com * @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-19 21:09:14 * @LastEditTime: 2022-10-24 20:38:09
* @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/interface.ts * @FilePath: /bkunyun/src/views/ResourceCenter/UserResources/WorkflowOperator/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
*/ */
export type IOperatorType = 'BATCH' | 'FLOW' export type IOperatorType = 'BATCH' | 'FLOW'
export interface IOperatorInfo{ export interface IOperatorInfo{
id: string;
title: string; title: string;
type: IOperatorType; type: IOperatorType;
version: string; version: string;
productId: string; productId: string;
createdTime: string;
}
export enum operatorType {
'FLOW' = '流算子',
'BATCH' = '批算子',
}
export interface IOperatorAddFormData {
title?: string;
type?: IOperatorType;
version?: string;
productId?: string;
description?: string;
envId?: string;
parameters?: any
} }
\ No newline at end of file
import style from "./index.module.css"; import style from "./index.module.css";
import usePass from "@/hooks/usePass"; import usePass from "@/hooks/usePass";
import UserResourcesTemplate from "./UserResourcesTemplate";
import UserResourcesEnvironment from "./UserResourcesEnvironment"; import UserResourcesEnvironment from "./UserResourcesEnvironment";
import { useMemo, useState } from "react"; import { useMemo } from "react";
import classNames from "classnames";
import Tabs from "@/components/mui/MyTabs"; import Tabs from "@/components/mui/MyTabs";
import WorkflowOperator from "./WorkflowOperator"; import WorkflowOperator from "./WorkflowOperator";
import OperatorDetails from "./WorkflowOperator/components/OperatorDetails"; import OperatorDetails from "./WorkflowOperator/components/OperatorDetails";
...@@ -13,7 +13,7 @@ const UserResources = () => { ...@@ -13,7 +13,7 @@ const UserResources = () => {
{ {
label: "工作流模版", label: "工作流模版",
value: "USERRESOURCES_TEMPLATE", value: "USERRESOURCES_TEMPLATE",
component: <div />, component: <UserResourcesTemplate />,
hide: !isPass("USERRESOURCES_TEMPLATE"), hide: !isPass("USERRESOURCES_TEMPLATE"),
}, },
{ {
...@@ -36,7 +36,7 @@ const UserResources = () => { ...@@ -36,7 +36,7 @@ const UserResources = () => {
<Tabs <Tabs
title="个人资源" title="个人资源"
tabList={tabList} tabList={tabList}
defaultValue={"USERRESOURCES_ENVIRONMENT"} defaultValue={"USERRESOURCES_TEMPLATE"}
tabPanelSx={{ padding: "0" }} tabPanelSx={{ padding: "0" }}
/> />
{/* <OperatorDetails /> */} {/* <OperatorDetails /> */}
......
.switchBatchFolw { .switchBatchFolw {
height: 100vh; height: 100vh;
width: 50vh; width: 40vh;
background-color: rgba(247, 248, 250, 1); background-color: rgba(247, 248, 250, 1);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-end;
} }
.goBackBox { .goBackBox {
box-sizing: border-box; box-sizing: border-box;
height: 110px; height: 120px;
padding: 64px 0 14px 25%; padding: 64px 0 24px;
margin-right: 44px;
line-height: 32px; line-height: 32px;
font-size: 18px; font-size: 18px;
color: #8a9099; color: #8a9099;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
width: calc(82% - 54px);
} }
.goBackIcon { .goBackIcon {
width: 32px; width: 32px;
...@@ -31,33 +34,35 @@ ...@@ -31,33 +34,35 @@
color: #8a9099; color: #8a9099;
} }
.switchBox { .switchBox {
width: 100%;
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-around; justify-content: flex-start;
align-items: flex-end; align-items: flex-end;
position: relative; position: relative;
} }
.switchItem { .switchItem {
width: 100%; width: calc(82% - 44px);
position: relative; position: relative;
padding-right: 44px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-end; align-items: flex-end;
margin-bottom: 40px;
padding-right: calc(2% + 34px);
} }
.activeSwitchItem { .activeSwitchItem {
padding-right: 34px; padding-right: 34px;
width: calc(86% - 44px);
} }
.itemImg { .itemImg {
width: 58%; width: 100%;
cursor: pointer; cursor: pointer;
} }
.itemImg:hover { .itemImg:hover {
box-shadow: 0px 8px 20px -6px rgba(3, 47, 105, 0.14); box-shadow: 0px 8px 20px -6px rgba(3, 47, 105, 0.14);
} }
.activeImg { .activeImg {
width: 64%;
box-shadow: 0px 12px 30px -8px rgba(3, 47, 105, 0.18); box-shadow: 0px 12px 30px -8px rgba(3, 47, 105, 0.18);
} }
.activeImg:hover { .activeImg:hover {
......
/*
* @Author: 吴永生 15770852798@163.com
* @Date: 2022-10-18 09:32:40
* @LastEditors: 吴永生 15770852798@163.com
* @LastEditTime: 2022-10-24 14:57:06
* @FilePath: /bkunyun/src/views/ResourceCenter/components/SwitchBatchFolw/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import batchImg from "@/assets/resourceCenter/batchImg.svg"; import batchImg from "@/assets/resourceCenter/batchImg.svg";
import flowImg from "@/assets/resourceCenter/flowImg.svg"; import flowImg from "@/assets/resourceCenter/flowImg.svg";
import classNames from "classnames"; import classNames from "classnames";
import goback from "@/assets/project/goback.svg"; import goback from "@/assets/project/goback.svg";
import style from "./index.module.css"; import style from "./index.module.css";
type ISwitchBatchFolwProps = { type ISwitchBatchFolwProps = {
topImg?: string;
bottomImg?: string;
active: "BATCH" | "FLOW"; active: "BATCH" | "FLOW";
setActive: any; setActive: any;
goBack: any; goBack: any;
}; };
const SwitchBatchFolw = (props: ISwitchBatchFolwProps) => { const SwitchBatchFolw = (props: ISwitchBatchFolwProps) => {
const { active, setActive, goBack } = props; const { active, setActive, goBack, topImg, bottomImg } = props;
return ( return (
<div className={style.switchBatchFolw}> <div className={style.switchBatchFolw}>
<div className={style.goBackBox}> <div className={style.goBackBox}>
...@@ -36,7 +47,7 @@ const SwitchBatchFolw = (props: ISwitchBatchFolwProps) => { ...@@ -36,7 +47,7 @@ const SwitchBatchFolw = (props: ISwitchBatchFolwProps) => {
[style.itemImg]: true, [style.itemImg]: true,
[style.activeImg]: active === "BATCH", [style.activeImg]: active === "BATCH",
})} })}
src={batchImg} src={topImg || batchImg}
alt="" alt=""
/> />
{active === "BATCH" && <div className={style.arrow}></div>} {active === "BATCH" && <div className={style.arrow}></div>}
...@@ -53,7 +64,7 @@ const SwitchBatchFolw = (props: ISwitchBatchFolwProps) => { ...@@ -53,7 +64,7 @@ const SwitchBatchFolw = (props: ISwitchBatchFolwProps) => {
[style.itemImg]: true, [style.itemImg]: true,
[style.activeImg]: active === "FLOW", [style.activeImg]: active === "FLOW",
})} })}
src={flowImg} src={bottomImg || flowImg}
alt="" alt=""
/> />
{active === "FLOW" && <div className={style.arrow}></div>} {active === "FLOW" && <div className={style.arrow}></div>}
......
import { OutlinedInput } from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import classNames from "classnames"; import classNames from "classnames";
import { useCallback, useEffect, 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 cloneDeep from "lodash/cloneDeep"; import cloneDeep from "lodash/cloneDeep";
import { IOperatorItemProps, IOperatorListProps } from "./interface"; import { IOperatorItemProps, IOperatorListProps } from "./interface";
...@@ -11,7 +8,6 @@ import { ITask } from "@/views/Project/ProjectSubmitWork/interface"; ...@@ -11,7 +8,6 @@ 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 } from "@/api/workbench_api"; import { fetchOperatorList } from "@/api/workbench_api";
import { useStores } from "@/store";
import noTemplate from "@/assets/project/noTemplate.svg"; import noTemplate from "@/assets/project/noTemplate.svg";
import SearchInput from "@/components/BusinessComponents/SearchInput"; import SearchInput from "@/components/BusinessComponents/SearchInput";
...@@ -158,10 +154,12 @@ const OperatorItem = (props: IOperatorItemProps) => { ...@@ -158,10 +154,12 @@ const OperatorItem = (props: IOperatorItemProps) => {
}; };
const OperatorList = observer((props: IOperatorListProps) => { const OperatorList = observer((props: IOperatorListProps) => {
const { currentProjectStore } = useStores(); const {
const productId = toJS(currentProjectStore.currentProductInfo.id); templateConfigInfo,
const { templateConfigInfo, setTemplateConfigInfo, showCustomOperator } = productId,
props; setTemplateConfigInfo,
showCustomOperator,
} = props;
const [operatorListData, setOperatorListData] = useState<ITask[]>([]); const [operatorListData, setOperatorListData] = useState<ITask[]>([]);
const [keyword, setKeyword] = useState<string>(""); const [keyword, setKeyword] = useState<string>("");
......
...@@ -20,4 +20,5 @@ export interface IOperatorListProps { ...@@ -20,4 +20,5 @@ export interface IOperatorListProps {
templateConfigInfo: ITask[] templateConfigInfo: ITask[]
setTemplateConfigInfo: (val: ITask[]) => void setTemplateConfigInfo: (val: ITask[]) => void
showCustomOperator: boolean showCustomOperator: boolean
productId: string
} }
\ No newline at end of file
...@@ -12,11 +12,9 @@ import MyInput from "@/components/mui/MyInput"; ...@@ -12,11 +12,9 @@ import MyInput from "@/components/mui/MyInput";
import { checkIsNumberLetterChinese } from "@/utils/util"; import { checkIsNumberLetterChinese } from "@/utils/util";
import { useState } from "react"; import { useState } from "react";
import useMyRequest from "@/hooks/useMyRequest"; import useMyRequest from "@/hooks/useMyRequest";
import { useStores } from "@/store";
import styles from "./index.module.css"; import styles from "./index.module.css";
import { useMessage } from "@/components/MySnackbar"; import { useMessage } from "@/components/MySnackbar";
import { toJS } from "mobx";
import { ITask } from "@/views/Project/ProjectSubmitWork/interface"; import { ITask } from "@/views/Project/ProjectSubmitWork/interface";
interface IProps { interface IProps {
saveFormDialog: boolean; saveFormDialog: boolean;
...@@ -32,6 +30,7 @@ interface IProps { ...@@ -32,6 +30,7 @@ interface IProps {
creator?: string; creator?: string;
templateConfigInfo: ITask[]; templateConfigInfo: ITask[];
id?: string; id?: string;
productId: string;
} }
const SaveCustomTemplate = (props: IProps) => { const SaveCustomTemplate = (props: IProps) => {
const { const {
...@@ -48,10 +47,9 @@ const SaveCustomTemplate = (props: IProps) => { ...@@ -48,10 +47,9 @@ const SaveCustomTemplate = (props: IProps) => {
templateConfigInfo, templateConfigInfo,
creator, creator,
id, id,
productId,
} = props; } = props;
const { currentProjectStore } = useStores();
const Message = useMessage(); const Message = useMessage();
const productId = toJS(currentProjectStore.currentProductInfo.id);
const [titleHelper, setTitleHelper] = useState({ const [titleHelper, setTitleHelper] = useState({
// 自定义模板名称错误提示 // 自定义模板名称错误提示
error: false, error: false,
...@@ -192,25 +190,25 @@ const SaveCustomTemplate = (props: IProps) => { ...@@ -192,25 +190,25 @@ const SaveCustomTemplate = (props: IProps) => {
// 表单弹窗确定,新建/编辑自定义模板保存 // 表单弹窗确定,新建/编辑自定义模板保存
const handleOncofirm = () => { const handleOncofirm = () => {
if (checkTitle(title) && checkVersion(version)) { if (checkTitle(title) && checkVersion(version)) {
if (id) { // if (id) {
saveUserSpecRun({ // saveUserSpecRun({
title, // title,
version, // version,
description, // description,
tasks: templateConfigInfo, // tasks: templateConfigInfo,
productId, // productId,
id, // id,
creator, // creator,
}); // });
} else { // } else {
saveUserSpecRun({ saveUserSpecRun({
title, title,
version, version,
description, description,
tasks: templateConfigInfo, tasks: templateConfigInfo,
productId, productId,
}); });
} // }
} }
}; };
......
...@@ -45,14 +45,18 @@ const radioOptions = [ ...@@ -45,14 +45,18 @@ const radioOptions = [
interface IProps { interface IProps {
onBack?: () => void; onBack?: () => void;
id?: string; id?: string;
propsProductId?: string;
} }
const WorkFlowEdit = observer((props: IProps) => { const WorkFlowEdit = observer((props: IProps) => {
const { onBack, id } = props; const { onBack, id, propsProductId } = props;
const Message = useMessage(); const Message = useMessage();
const [templateConfigInfo, setTemplateConfigInfo] = useState<ITask[]>([]); // 算子大数组 const [templateConfigInfo, setTemplateConfigInfo] = useState<ITask[]>([]); // 算子大数组
const [showCustomOperator, setShowCustomOperator] = useState(false); // 是否显示自定义算子 const [showCustomOperator, setShowCustomOperator] = useState(false); // 是否显示自定义算子
const { currentProjectStore } = useStores(); const { currentProjectStore } = useStores();
const productId = propsProductId
? propsProductId
: toJS(currentProjectStore.currentProductInfo.id);
const zoneId = toJS(currentProjectStore.currentProjectInfo.zoneId); const zoneId = toJS(currentProjectStore.currentProjectInfo.zoneId);
const [saveFormDialog, setSaveFormDialog] = useState(false); // 保存弹窗显示与否控制 const [saveFormDialog, setSaveFormDialog] = useState(false); // 保存弹窗显示与否控制
const [title, setTitle] = useState(""); // 自定义模板名称 const [title, setTitle] = useState(""); // 自定义模板名称
...@@ -274,6 +278,7 @@ const WorkFlowEdit = observer((props: IProps) => { ...@@ -274,6 +278,7 @@ const WorkFlowEdit = observer((props: IProps) => {
showCustomOperator={showCustomOperator} showCustomOperator={showCustomOperator}
templateConfigInfo={templateConfigInfo} templateConfigInfo={templateConfigInfo}
setTemplateConfigInfo={setTemplateConfigInfo} setTemplateConfigInfo={setTemplateConfigInfo}
productId={productId as string}
/> />
)} )}
{leftContentType !== "list" && ( {leftContentType !== "list" && (
...@@ -333,6 +338,7 @@ const WorkFlowEdit = observer((props: IProps) => { ...@@ -333,6 +338,7 @@ const WorkFlowEdit = observer((props: IProps) => {
templateConfigInfo={templateConfigInfo} templateConfigInfo={templateConfigInfo}
id={id} id={id}
oldversion={oldversion} oldversion={oldversion}
productId={productId as string}
/> />
)} )}
{showCustomOperator && ( {showCustomOperator && (
...@@ -341,6 +347,7 @@ const WorkFlowEdit = observer((props: IProps) => { ...@@ -341,6 +347,7 @@ const WorkFlowEdit = observer((props: IProps) => {
initOperatorList={JSON.parse( initOperatorList={JSON.parse(
sessionStorage.getItem("operatorList") || "[]" sessionStorage.getItem("operatorList") || "[]"
)} )}
productId={productId as string}
/> />
)} )}
</div> </div>
......
import CardTable from "@/components/CommonComponents/CardTable";
const CardTableDemo = () => {
const list = [
{
id: 1,
},
{
id: 2,
},
{
id: 3,
},
{
id: 4,
},
{
id: 5,
},
{
id: 6,
},
{
id: 7,
},
{
id: 8,
},
{
id: 9,
},
{
id: 10,
},
{
id: 11,
},
{
id: 12,
},
{
id: 13,
},
];
const renderItem = (item: any) => {
return (
<div style={{ border: "1px solid red", height: "200px" }}>{item.id}</div>
);
};
return (
<div>
CardTableDemo
<CardTable
data={list}
renderItem={renderItem}
numberOfColumns={4}
></CardTable>
</div>
);
};
export default CardTableDemo;
import MyTableDemo from "./MyTableDemo"; import MyTableDemo from "./MyTableDemo";
import QueueSelectDemo from "./QueueSelectDemo"; import QueueSelectDemo from "./QueueSelectDemo";
import IconfontDemo from "./IconfontDemo"; import IconfontDemo from "./IconfontDemo";
import CardTableDemo from "./CardTableDemo";
import RadioGroupOfButtonStyle from "@/components/CommonComponents/RadioGroupOfButtonStyle"; import RadioGroupOfButtonStyle from "@/components/CommonComponents/RadioGroupOfButtonStyle";
import { useState } from "react"; import { useState } from "react";
import styles from "./index.module.css"; import styles from "./index.module.css";
const Demo = () => { const Demo = () => {
const radioOptionsArr = [ const radioOptionsArr = [
{
value: "cardTable",
label: "cardTable",
},
{ {
value: "iconfont", value: "iconfont",
label: "iconfont", label: "iconfont",
...@@ -27,7 +32,7 @@ const Demo = () => { ...@@ -27,7 +32,7 @@ const Demo = () => {
const handleRadio = (e: string) => { const handleRadio = (e: string) => {
setSelectDemo(e); setSelectDemo(e);
}; };
const [selectDemo, setSelectDemo] = useState("iconfont"); const [selectDemo, setSelectDemo] = useState("cardTable");
return ( return (
<div className={styles.demoBox}> <div className={styles.demoBox}>
...@@ -37,6 +42,7 @@ const Demo = () => { ...@@ -37,6 +42,7 @@ const Demo = () => {
handleRadio={handleRadio} handleRadio={handleRadio}
/> />
<div className={styles.demoContentBox}> <div className={styles.demoContentBox}>
{selectDemo === "cardTable" && <CardTableDemo></CardTableDemo>}
{selectDemo === "iconfont" && <IconfontDemo></IconfontDemo>} {selectDemo === "iconfont" && <IconfontDemo></IconfontDemo>}
{selectDemo === "队列选择器" && <QueueSelectDemo></QueueSelectDemo>} {selectDemo === "队列选择器" && <QueueSelectDemo></QueueSelectDemo>}
{selectDemo === "表格" && <MyTableDemo></MyTableDemo>} {selectDemo === "表格" && <MyTableDemo></MyTableDemo>}
......
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